/**
 * WithholdingTypeService - Gestion de Tipos de Retencion
 *
 * Administra los tipos de retenciones fiscales (ISR, IVA retenido, etc).
 *
 * @module Fiscal
 */

import { Repository } from 'typeorm';
import { WithholdingType } from '../entities/withholding-type.entity';

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

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

export interface CreateWithholdingTypeDto {
  code: string;
  name: string;
  description?: string;
  defaultRate?: number;
  taxCategoryId?: string;
  isActive?: boolean;
}

export interface UpdateWithholdingTypeDto extends Partial<CreateWithholdingTypeDto> {}

export interface WithholdingTypeFilters {
  taxCategoryId?: string;
  minRate?: number;
  maxRate?: number;
  isActive?: boolean;
  search?: string;
}

export class WithholdingTypeService {
  private repository: Repository<WithholdingType>;

  constructor(repository: Repository<WithholdingType>) {
    this.repository = repository;
  }

  /**
   * Crear un nuevo tipo de retencion
   */
  async create(_ctx: ServiceContext, data: CreateWithholdingTypeDto): Promise<WithholdingType> {
    const existing = await this.findByCode(data.code);
    if (existing) {
      throw new Error(`Withholding type with code ${data.code} already exists`);
    }

    const entity = this.repository.create({
      ...data,
      isActive: data.isActive ?? true,
    });
    return this.repository.save(entity);
  }

  /**
   * Buscar por ID
   */
  async findById(_ctx: ServiceContext, id: string): Promise<WithholdingType | null> {
    return this.repository.findOne({
      where: { id },
      relations: ['taxCategory'],
    });
  }

  /**
   * Buscar por codigo
   */
  async findByCode(code: string): Promise<WithholdingType | null> {
    return this.repository.findOne({
      where: { code },
      relations: ['taxCategory'],
    });
  }

  /**
   * Actualizar tipo de retencion
   */
  async update(_ctx: ServiceContext, id: string, data: UpdateWithholdingTypeDto): Promise<WithholdingType | null> {
    const entity = await this.findById(_ctx, id);
    if (!entity) return null;

    if (data.code && data.code !== entity.code) {
      const existing = await this.findByCode(data.code);
      if (existing) {
        throw new Error(`Withholding type with code ${data.code} already exists`);
      }
    }

    Object.assign(entity, data);
    return this.repository.save(entity);
  }

  /**
   * Desactivar tipo de retencion
   */
  async deactivate(_ctx: ServiceContext, id: string): Promise<boolean> {
    const entity = await this.findById(_ctx, id);
    if (!entity) return false;
    entity.isActive = false;
    await this.repository.save(entity);
    return true;
  }

  /**
   * Buscar tipos de retencion con filtros
   */
  async findWithFilters(
    _ctx: ServiceContext,
    filters: WithholdingTypeFilters,
    page = 1,
    limit = 20
  ): Promise<PaginatedResult<WithholdingType>> {
    const qb = this.repository
      .createQueryBuilder('wt')
      .leftJoinAndSelect('wt.taxCategory', 'tc');

    if (filters.taxCategoryId) {
      qb.andWhere('wt.tax_category_id = :taxCategoryId', {
        taxCategoryId: filters.taxCategoryId,
      });
    }

    if (filters.minRate !== undefined) {
      qb.andWhere('wt.default_rate >= :minRate', { minRate: filters.minRate });
    }

    if (filters.maxRate !== undefined) {
      qb.andWhere('wt.default_rate <= :maxRate', { maxRate: filters.maxRate });
    }

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

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

    const skip = (page - 1) * limit;
    qb.orderBy('wt.code', 'ASC').skip(skip).take(limit);

    const [data, total] = await qb.getManyAndCount();

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

  /**
   * Obtener todos los tipos activos
   */
  async findAllActive(_ctx: ServiceContext): Promise<WithholdingType[]> {
    return this.repository.find({
      where: { isActive: true },
      relations: ['taxCategory'],
      order: { code: 'ASC' },
    });
  }

  /**
   * Obtener tipos por categoria de impuesto
   */
  async findByTaxCategory(_ctx: ServiceContext, taxCategoryId: string): Promise<WithholdingType[]> {
    return this.repository.find({
      where: { taxCategoryId, isActive: true },
      relations: ['taxCategory'],
      order: { code: 'ASC' },
    });
  }

  /**
   * Calcular retencion
   */
  calculateWithholding(
    _ctx: ServiceContext,
    amount: number,
    rate: number
  ): { grossAmount: number; withholdingAmount: number; netAmount: number } {
    const withholdingAmount = amount * (rate / 100);
    return {
      grossAmount: amount,
      withholdingAmount: Math.round(withholdingAmount * 100) / 100,
      netAmount: Math.round((amount - withholdingAmount) * 100) / 100,
    };
  }

  /**
   * Calcular retencion usando tipo por codigo
   */
  async calculateWithholdingByCode(
    _ctx: ServiceContext,
    amount: number,
    withholdingTypeCode: string,
    customRate?: number
  ): Promise<{ grossAmount: number; withholdingAmount: number; netAmount: number; rate: number }> {
    const type = await this.findByCode(withholdingTypeCode);
    if (!type) {
      throw new Error(`Withholding type ${withholdingTypeCode} not found`);
    }

    const rate = customRate ?? Number(type.defaultRate);
    const result = this.calculateWithholding(_ctx, amount, rate);

    return {
      ...result,
      rate,
    };
  }

  /**
   * Obtener estadisticas
   */
  async getStats(_ctx: ServiceContext): Promise<WithholdingTypeStats> {
    const all = await this.repository.find({
      relations: ['taxCategory'],
    });

    let activeCount = 0;
    let withCategoryCount = 0;
    const byCategoryMap = new Map<string, number>();
    const rates: number[] = [];

    for (const type of all) {
      if (type.isActive) activeCount++;
      if (type.taxCategoryId) {
        withCategoryCount++;
        const catName = type.taxCategory?.name || type.taxCategoryId;
        byCategoryMap.set(catName, (byCategoryMap.get(catName) || 0) + 1);
      }
      rates.push(Number(type.defaultRate));
    }

    const avgRate = rates.length > 0 ? rates.reduce((a, b) => a + b, 0) / rates.length : 0;
    const minRate = rates.length > 0 ? Math.min(...rates) : 0;
    const maxRate = rates.length > 0 ? Math.max(...rates) : 0;

    return {
      total: all.length,
      active: activeCount,
      inactive: all.length - activeCount,
      withTaxCategory: withCategoryCount,
      byCategory: Array.from(byCategoryMap.entries()).map(([category, count]) => ({
        category,
        count,
      })),
      rateStats: {
        average: Math.round(avgRate * 100) / 100,
        min: minRate,
        max: maxRate,
      },
    };
  }
}

export interface WithholdingTypeStats {
  total: number;
  active: number;
  inactive: number;
  withTaxCategory: number;
  byCategory: { category: string; count: number }[];
  rateStats: {
    average: number;
    min: number;
    max: number;
  };
}
