import { Injectable, NotFoundException, ConflictException } from '@nestjs/common';
import { CompaniesRepository } from './companies.repository';
import { ExternalProfileRepository } from './external-profile.repository';
import { FFClientRepository } from './ff-client.repository';
import { CreateCompanyDto } from './dto/create-company.dto';
import { UpdateCompanyDto } from './dto/update-company.dto';
import { CreateExternalProfileDto } from './dto/create-external-profile.dto';
import { CreateFFClientDto } from './dto/create-ff-client.dto';
import { CreateCompanyWithProfileDto } from './dto/create-company-with-profile.dto';
import { UpdateProfileDto } from './dto/update-profile.dto';
import { ProfileResponseDto } from './dto/profile-response.dto';
import { Company } from './entities/company.entity';
import { ExternalProfile } from './entities/external-profile.entity';
import { FFClient } from './entities/ff-client.entity';

export interface UserIdentity {
  userId: string;
  firstName: string;
  lastName: string;
  email: string;
  phone: string;
}

@Injectable()
export class CompaniesService {
  constructor(
    private readonly companiesRepo: CompaniesRepository,
    private readonly profilesRepo: ExternalProfileRepository,
    private readonly ffClientsRepo: FFClientRepository,
  ) {}

  async createCompany(dto: CreateCompanyDto): Promise<Company> {
    const exists = await this.companiesRepo.existsByTin(dto.tin);
    if (exists) {
      throw new ConflictException(`Company with TIN ${dto.tin} already exists`);
    }
    return this.companiesRepo.create(dto);
  }

  async createCompanyWithProfile(identity: UserIdentity, dto: CreateCompanyWithProfileDto): Promise<{ company: Company; profile: ExternalProfile }> {
    if (dto.tin) {
      const exists = await this.companiesRepo.existsByTin(dto.tin);
      if (exists) {
        throw new ConflictException(`Company with TIN ${dto.tin} already exists`);
      }
    }

    const existingProfile = await this.profilesRepo.findByEmail(identity.email);
    if (existingProfile) {
      throw new ConflictException(`Profile with email ${identity.email} already exists`);
    }

    const company = await this.companiesRepo.create({
      name: dto.companyName,
      type: dto.companyType,
      tin: dto.tin ?? '',
      vatNumber: dto.vatNumber ?? null,
      businessLicense: dto.fanNumber ?? null,
      fanNumber: dto.fanNumber ?? null,
      country: dto.companyLocation ?? 'Ethiopia',
      address: dto.companyAddress ?? null,
      phone: dto.companyPhone ?? null,
      email: dto.companyEmail ?? null,
      attributes: dto.attributes ?? null,
    });

    const profile = await this.profilesRepo.create({
      userId: identity.userId,
      companyId: company.id,
      firstName: identity.firstName,
      lastName: identity.lastName,
      email: identity.email,
      phone: identity.phone,
      jobTitle: dto.jobTitle ?? null,
      isPrimaryContact: dto.isPrimaryContact ?? true,
    });

    return { company, profile };
  }

  async findAllCompanies(): Promise<Company[]> {
    return this.companiesRepo.findAll({ order: { name: 'ASC' as any } });
  }

  async findCompanyById(id: string): Promise<Company> {
    const company = await this.companiesRepo.findById(id);
    if (!company) throw new NotFoundException(`Company ${id} not found`);
    return company;
  }

  async getCompanyInfoByUserId(userId: string): Promise<{ profile: ExternalProfile; company: Company }> {
    const profile = await this.profilesRepo.findByUserId(userId);
    if (!profile) throw new NotFoundException(`Profile for user ${userId} not found`);

    const company = profile.company;
    if (!company) throw new NotFoundException(`Company for profile ${profile.id} not found`);

    return { profile, company };
  }

  async updateCompany(id: string, dto: UpdateCompanyDto): Promise<Company> {
    await this.findCompanyById(id);
    const updated = await this.companiesRepo.update(id, dto);
    if (!updated) throw new NotFoundException(`Company ${id} not found`);
    return updated;
  }

  async updateProfile(userId: string, dto: UpdateProfileDto): Promise<ProfileResponseDto> {
    const { profile, company } = await this.getCompanyInfoByUserId(userId);

    const companyUpdates: Record<string, any> = {};
    const attrUpdates: Record<string, any> = { ...(company.attributes ?? {}) };

    if (dto.companyName !== undefined) companyUpdates.name = dto.companyName;
    if (dto.companyEmail !== undefined) companyUpdates.email = dto.companyEmail;
    if (dto.companyPhone !== undefined) companyUpdates.phone = dto.companyPhone;
    if (dto.companyLocation !== undefined) companyUpdates.country = dto.companyLocation;
    if (dto.companyAddress !== undefined) companyUpdates.address = dto.companyAddress;
    if (dto.tin !== undefined) companyUpdates.tin = dto.tin;
    if (dto.vatNumber !== undefined) companyUpdates.vatNumber = dto.vatNumber;
    if (dto.fanNumber !== undefined) {
      companyUpdates.businessLicense = dto.fanNumber;
      companyUpdates.fanNumber = dto.fanNumber;
    }

    if (dto.contactPersonName !== undefined) attrUpdates.contactPersonName = dto.contactPersonName;
    if (dto.contactPersonPhone !== undefined) attrUpdates.contactPersonPhone = dto.contactPersonPhone;
    if (dto.generalManagerName !== undefined) attrUpdates.generalManagerName = dto.generalManagerName;
    if (dto.generalManagerEmail !== undefined) attrUpdates.generalManagerEmail = dto.generalManagerEmail;
    if (dto.generalManagerPhone !== undefined) attrUpdates.generalManagerPhone = dto.generalManagerPhone;
    if (dto.poaName !== undefined) attrUpdates.poaName = dto.poaName;
    if (dto.poaPhone !== undefined) attrUpdates.poaPhone = dto.poaPhone;
    if (dto.poaEmail !== undefined) attrUpdates.poaEmail = dto.poaEmail;
    if (dto.poaLocation !== undefined) attrUpdates.poaLocation = dto.poaLocation;
    if (dto.poaAddress !== undefined) attrUpdates.poaAddress = dto.poaAddress;

    companyUpdates.attributes = attrUpdates;

    const updated = await this.companiesRepo.update(company.id, companyUpdates);
    if (!updated) throw new NotFoundException(`Company ${company.id} not found`);
    return new ProfileResponseDto(profile, updated);
  }

  async deleteCompany(id: string): Promise<void> {
    await this.findCompanyById(id);
    await this.companiesRepo.softDelete(id);
  }

  async createProfile(dto: CreateExternalProfileDto): Promise<ExternalProfile> {
    await this.findCompanyById(dto.companyId);

    const existing = await this.profilesRepo.findByEmail(dto.email);
    if (existing) {
      throw new ConflictException(`Profile with email ${dto.email} already exists`);
    }

    return this.profilesRepo.create(dto);
  }

  async findProfileByUserId(userId: string): Promise<ExternalProfile> {
    const profile = await this.profilesRepo.findByUserId(userId);
    if (!profile) throw new NotFoundException(`Profile for user ${userId} not found`);
    return profile;
  }

  async findProfilesByCompany(companyId: string): Promise<ExternalProfile[]> {
    return this.profilesRepo.findByCompanyId(companyId);
  }

  async createFFClient(dto: CreateFFClientDto): Promise<FFClient> {
    await this.findCompanyById(dto.forwarderCompanyId);
    await this.findCompanyById(dto.clientCompanyId);

    const existing = await this.ffClientsRepo.findRelationship(
      dto.forwarderCompanyId,
      dto.clientCompanyId,
    );
    if (existing) {
      throw new ConflictException('This forwarder-client relationship already exists');
    }

    return this.ffClientsRepo.create(dto);
  }

  async findForwarderClients(forwarderCompanyId: string): Promise<FFClient[]> {
    return this.ffClientsRepo.findByForwarder(forwarderCompanyId);
  }

  async deleteFFClient(id: string): Promise<void> {
    const client = await this.ffClientsRepo.findById(id);
    if (!client) throw new NotFoundException(`FFClient ${id} not found`);
    await this.ffClientsRepo.softDelete(id);
  }
}
