import { Repository, IsNull } from 'typeorm';
import { Company } from '../entities';

export interface ServiceContext {
  tenantId: string;
  userId?: string;
}

export interface CreateCompanyDto {
  code: string;
  name: string;
  legalName?: string;
  taxId?: string;
  email?: string;
  phone?: string;
  address?: string;
  city?: string;
  state?: string;
  country?: string;
  postalCode?: string;
  logo?: string;
  isActive?: boolean;
}

export interface UpdateCompanyDto {
  code?: string;
  name?: string;
  legalName?: string;
  taxId?: string;
  email?: string;
  phone?: string;
  address?: string;
  city?: string;
  state?: string;
  country?: string;
  postalCode?: string;
  logo?: string;
  isActive?: boolean;
}

export interface CompanyFilters {
  code?: string;
  isActive?: boolean;
  search?: string;
}

export interface PaginatedResult<T> {
  data: T[];
  total: number;
  page: number;
  limit: number;
  totalPages: number;
}

export class CompanyService {
  constructor(private readonly companyRepository: Repository<Company>) {}

  async create(ctx: ServiceContext, data: CreateCompanyDto): Promise<Company> {
    const existing = await this.companyRepository.findOne({
      where: { tenantId: ctx.tenantId, code: data.code, deletedAt: IsNull() },
    });

    if (existing) {
      throw new Error(`Company with code '${data.code}' already exists`);
    }

    const company = this.companyRepository.create({
      tenantId: ctx.tenantId,
      code: data.code,
      name: data.name,
      legalName: data.legalName,
      taxId: data.taxId,
      email: data.email,
      phone: data.phone,
      address: data.address,
      city: data.city,
      state: data.state,
      country: data.country,
      postalCode: data.postalCode,
      logo: data.logo,
      isActive: data.isActive ?? true,
      createdBy: ctx.userId ?? null,
    });

    return this.companyRepository.save(company);
  }

  async findById(ctx: ServiceContext, id: string): Promise<Company | null> {
    return this.companyRepository.findOne({
      where: { id, tenantId: ctx.tenantId, deletedAt: IsNull() },
    });
  }

  async findByCode(ctx: ServiceContext, code: string): Promise<Company | null> {
    return this.companyRepository.findOne({
      where: { tenantId: ctx.tenantId, code, deletedAt: IsNull() },
    });
  }

  async findAll(ctx: ServiceContext): Promise<Company[]> {
    return this.companyRepository.find({
      where: { tenantId: ctx.tenantId, deletedAt: IsNull() },
      order: { name: 'ASC' },
    });
  }

  async findWithFilters(
    ctx: ServiceContext,
    filters: CompanyFilters,
    page = 1,
    limit = 50,
  ): Promise<PaginatedResult<Company>> {
    const qb = this.companyRepository
      .createQueryBuilder('company')
      .where('company.tenant_id = :tenantId', { tenantId: ctx.tenantId })
      .andWhere('company.deleted_at IS NULL');

    if (filters.code) {
      qb.andWhere('company.code = :code', { code: filters.code });
    }

    if (filters.isActive !== undefined) {
      qb.andWhere('company.is_active = :isActive', { isActive: filters.isActive });
    }

    if (filters.search) {
      qb.andWhere(
        '(company.name ILIKE :search OR company.code ILIKE :search OR company.legal_name ILIKE :search)',
        { search: `%${filters.search}%` },
      );
    }

    const [data, total] = await qb
      .orderBy('company.name', 'ASC')
      .skip((page - 1) * limit)
      .take(limit)
      .getManyAndCount();

    return {
      data,
      total,
      page,
      limit,
      totalPages: Math.ceil(total / limit),
    };
  }

  async update(ctx: ServiceContext, id: string, data: UpdateCompanyDto): Promise<Company | null> {
    const company = await this.findById(ctx, id);
    if (!company) return null;

    if (data.code && data.code !== company.code) {
      const existing = await this.companyRepository.findOne({
        where: { tenantId: ctx.tenantId, code: data.code, deletedAt: IsNull() },
      });
      if (existing) {
        throw new Error(`Company with code '${data.code}' already exists`);
      }
    }

    Object.assign(company, {
      ...data,
      updatedBy: ctx.userId ?? null,
    });

    return this.companyRepository.save(company);
  }

  async delete(ctx: ServiceContext, id: string): Promise<boolean> {
    const company = await this.findById(ctx, id);
    if (!company) return false;

    company.deletedAt = new Date();
    company.deletedBy = ctx.userId ?? null;
    await this.companyRepository.save(company);

    return true;
  }

  async getActiveCompanies(ctx: ServiceContext): Promise<Company[]> {
    return this.companyRepository.find({
      where: { tenantId: ctx.tenantId, isActive: true, deletedAt: IsNull() },
      order: { name: 'ASC' },
    });
  }
}
