/**
 * FiscalCalculationService - Servicio de Calculos Fiscales
 *
 * Proporciona funciones de calculo de impuestos, retenciones y totales fiscales.
 *
 * @module Fiscal
 */

import { Repository } from 'typeorm';
import { TaxCategory, TaxNature } from '../entities/tax-category.entity';
import { WithholdingType } from '../entities/withholding-type.entity';

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

export interface TaxLine {
  taxCategoryId: string;
  taxCategoryCode: string;
  taxCategoryName: string;
  rate: number;
  baseAmount: number;
  taxAmount: number;
  nature: TaxNature;
}

export interface WithholdingLine {
  withholdingTypeId: string;
  withholdingTypeCode: string;
  withholdingTypeName: string;
  rate: number;
  baseAmount: number;
  withholdingAmount: number;
}

export interface TaxCalculationResult {
  subtotal: number;
  taxes: TaxLine[];
  totalTaxes: number;
  withholdings: WithholdingLine[];
  totalWithholdings: number;
  total: number;
  breakdown: {
    transferredTaxes: number;
    retainedTaxes: number;
    netPayable: number;
  };
}

export interface TaxCalculationInput {
  subtotal: number;
  taxRates: Array<{
    taxCategoryId: string;
    rate: number;
    includeInTotal?: boolean;
  }>;
  withholdingRates?: Array<{
    withholdingTypeId: string;
    rate?: number; // If not provided, uses default rate
  }>;
}

export class FiscalCalculationService {
  private taxCategoryRepository: Repository<TaxCategory>;
  private withholdingTypeRepository: Repository<WithholdingType>;

  constructor(
    taxCategoryRepository: Repository<TaxCategory>,
    withholdingTypeRepository: Repository<WithholdingType>
  ) {
    this.taxCategoryRepository = taxCategoryRepository;
    this.withholdingTypeRepository = withholdingTypeRepository;
  }

  /**
   * Calcular impuestos y retenciones para un monto
   */
  async calculateTaxes(
    _ctx: ServiceContext,
    input: TaxCalculationInput
  ): Promise<TaxCalculationResult> {
    const { subtotal, taxRates, withholdingRates = [] } = input;

    // Calculate taxes
    const taxes: TaxLine[] = [];
    let totalTaxes = 0;
    let transferredTaxes = 0;
    let retainedTaxes = 0;

    for (const taxRate of taxRates) {
      const taxCategory = await this.taxCategoryRepository.findOne({
        where: { id: taxRate.taxCategoryId },
      });

      if (!taxCategory) {
        throw new Error(`Tax category ${taxRate.taxCategoryId} not found`);
      }

      const taxAmount = this.roundCurrency(subtotal * (taxRate.rate / 100));

      taxes.push({
        taxCategoryId: taxCategory.id,
        taxCategoryCode: taxCategory.code,
        taxCategoryName: taxCategory.name,
        rate: taxRate.rate,
        baseAmount: subtotal,
        taxAmount,
        nature: taxCategory.taxNature,
      });

      if (taxRate.includeInTotal !== false) {
        totalTaxes += taxAmount;
        if (taxCategory.taxNature === TaxNature.TAX || taxCategory.taxNature === TaxNature.BOTH) {
          transferredTaxes += taxAmount;
        }
      }
    }

    // Calculate withholdings
    const withholdings: WithholdingLine[] = [];
    let totalWithholdings = 0;

    for (const wRate of withholdingRates) {
      const withholdingType = await this.withholdingTypeRepository.findOne({
        where: { id: wRate.withholdingTypeId },
      });

      if (!withholdingType) {
        throw new Error(`Withholding type ${wRate.withholdingTypeId} not found`);
      }

      const rate = wRate.rate ?? Number(withholdingType.defaultRate);
      const withholdingAmount = this.roundCurrency(subtotal * (rate / 100));

      withholdings.push({
        withholdingTypeId: withholdingType.id,
        withholdingTypeCode: withholdingType.code,
        withholdingTypeName: withholdingType.name,
        rate,
        baseAmount: subtotal,
        withholdingAmount,
      });

      totalWithholdings += withholdingAmount;
      retainedTaxes += withholdingAmount;
    }

    const total = this.roundCurrency(subtotal + totalTaxes);
    const netPayable = this.roundCurrency(total - totalWithholdings);

    return {
      subtotal,
      taxes,
      totalTaxes: this.roundCurrency(totalTaxes),
      withholdings,
      totalWithholdings: this.roundCurrency(totalWithholdings),
      total,
      breakdown: {
        transferredTaxes: this.roundCurrency(transferredTaxes),
        retainedTaxes: this.roundCurrency(retainedTaxes),
        netPayable,
      },
    };
  }

  /**
   * Calcular IVA estandar (16%)
   */
  calculateStandardIVA(_ctx: ServiceContext, amount: number): { base: number; iva: number; total: number } {
    const iva = this.roundCurrency(amount * 0.16);
    return {
      base: amount,
      iva,
      total: this.roundCurrency(amount + iva),
    };
  }

  /**
   * Calcular IVA con tasa personalizada
   */
  calculateIVA(_ctx: ServiceContext, amount: number, rate: number): { base: number; iva: number; total: number } {
    const iva = this.roundCurrency(amount * (rate / 100));
    return {
      base: amount,
      iva,
      total: this.roundCurrency(amount + iva),
    };
  }

  /**
   * Calcular retencion ISR
   */
  calculateISRWithholding(
    _ctx: ServiceContext,
    amount: number,
    rate: number = 10
  ): { base: number; withholding: number; net: number } {
    const withholding = this.roundCurrency(amount * (rate / 100));
    return {
      base: amount,
      withholding,
      net: this.roundCurrency(amount - withholding),
    };
  }

  /**
   * Calcular retencion IVA
   */
  calculateIVAWithholding(
    _ctx: ServiceContext,
    amount: number,
    rate: number = 10.6667
  ): { base: number; withholding: number; net: number } {
    const withholding = this.roundCurrency(amount * (rate / 100));
    return {
      base: amount,
      withholding,
      net: this.roundCurrency(amount - withholding),
    };
  }

  /**
   * Calcular total para factura con IVA y retenciones
   */
  calculateInvoiceTotal(
    _ctx: ServiceContext,
    subtotal: number,
    ivaRate: number = 16,
    isrWithholdingRate: number = 0,
    ivaWithholdingRate: number = 0
  ): InvoiceTotalResult {
    const iva = this.roundCurrency(subtotal * (ivaRate / 100));
    const isrWithholding = this.roundCurrency(subtotal * (isrWithholdingRate / 100));
    const ivaWithholding = this.roundCurrency(subtotal * (ivaWithholdingRate / 100));

    const total = this.roundCurrency(subtotal + iva);
    const totalWithholdings = this.roundCurrency(isrWithholding + ivaWithholding);
    const netPayable = this.roundCurrency(total - totalWithholdings);

    return {
      subtotal,
      iva,
      ivaRate,
      total,
      isrWithholding,
      isrWithholdingRate,
      ivaWithholding,
      ivaWithholdingRate,
      totalWithholdings,
      netPayable,
    };
  }

  /**
   * Obtener base imponible desde total con IVA
   */
  getBaseFromTotalWithIVA(_ctx: ServiceContext, totalWithIVA: number, ivaRate: number = 16): number {
    return this.roundCurrency(totalWithIVA / (1 + ivaRate / 100));
  }

  /**
   * Redondear a 2 decimales (moneda)
   */
  private roundCurrency(amount: number): number {
    return Math.round(amount * 100) / 100;
  }

  /**
   * Validar que los calculos fiscales sean correctos
   */
  validateCalculation(
    _ctx: ServiceContext,
    subtotal: number,
    expectedTotal: number,
    ivaRate: number = 16,
    tolerance: number = 0.01
  ): { valid: boolean; expectedTotal: number; difference: number } {
    const calculated = this.calculateIVA(_ctx, subtotal, ivaRate);
    const difference = Math.abs(calculated.total - expectedTotal);

    return {
      valid: difference <= tolerance,
      expectedTotal: calculated.total,
      difference,
    };
  }
}

export interface InvoiceTotalResult {
  subtotal: number;
  iva: number;
  ivaRate: number;
  total: number;
  isrWithholding: number;
  isrWithholdingRate: number;
  ivaWithholding: number;
  ivaWithholdingRate: number;
  totalWithholdings: number;
  netPayable: number;
}
