import { query, queryOne } from '../../config/database.js';
import { NotFoundError, ConflictError } from '../../shared/errors/index.js';

export interface Tax {
  id: string;
  tenant_id: string;
  company_id: string;
  company_name?: string;
  name: string;
  code: string;
  tax_type: 'sales' | 'purchase' | 'all';
  amount: number;
  included_in_price: boolean;
  active: boolean;
  created_at: Date;
}

export interface CreateTaxDto {
  company_id: string;
  name: string;
  code: string;
  tax_type: 'sales' | 'purchase' | 'all';
  amount: number;
  included_in_price?: boolean;
}

export interface UpdateTaxDto {
  name?: string;
  code?: string;
  tax_type?: 'sales' | 'purchase' | 'all';
  amount?: number;
  included_in_price?: boolean;
  active?: boolean;
}

export interface TaxFilters {
  company_id?: string;
  tax_type?: string;
  active?: boolean;
  search?: string;
  page?: number;
  limit?: number;
}

class TaxesService {
  async findAll(tenantId: string, filters: TaxFilters = {}): Promise<{ data: Tax[]; total: number }> {
    const { company_id, tax_type, active, search, page = 1, limit = 20 } = filters;
    const offset = (page - 1) * limit;

    let whereClause = 'WHERE t.tenant_id = $1';
    const params: any[] = [tenantId];
    let paramIndex = 2;

    if (company_id) {
      whereClause += ` AND t.company_id = $${paramIndex++}`;
      params.push(company_id);
    }

    if (tax_type) {
      whereClause += ` AND t.tax_type = $${paramIndex++}`;
      params.push(tax_type);
    }

    if (active !== undefined) {
      whereClause += ` AND t.active = $${paramIndex++}`;
      params.push(active);
    }

    if (search) {
      whereClause += ` AND (t.name ILIKE $${paramIndex} OR t.code ILIKE $${paramIndex})`;
      params.push(`%${search}%`);
      paramIndex++;
    }

    const countResult = await queryOne<{ count: string }>(
      `SELECT COUNT(*) as count FROM financial.taxes t ${whereClause}`,
      params
    );

    params.push(limit, offset);
    const data = await query<Tax>(
      `SELECT t.*,
              c.name as company_name
       FROM financial.taxes t
       LEFT JOIN auth.companies c ON t.company_id = c.id
       ${whereClause}
       ORDER BY t.name
       LIMIT $${paramIndex} OFFSET $${paramIndex + 1}`,
      params
    );

    return {
      data,
      total: parseInt(countResult?.count || '0', 10),
    };
  }

  async findById(id: string, tenantId: string): Promise<Tax> {
    const tax = await queryOne<Tax>(
      `SELECT t.*,
              c.name as company_name
       FROM financial.taxes t
       LEFT JOIN auth.companies c ON t.company_id = c.id
       WHERE t.id = $1 AND t.tenant_id = $2`,
      [id, tenantId]
    );

    if (!tax) {
      throw new NotFoundError('Impuesto no encontrado');
    }

    return tax;
  }

  async create(dto: CreateTaxDto, tenantId: string, userId: string): Promise<Tax> {
    // Check unique code
    const existing = await queryOne(
      `SELECT id FROM financial.taxes WHERE tenant_id = $1 AND code = $2`,
      [tenantId, dto.code]
    );

    if (existing) {
      throw new ConflictError('Ya existe un impuesto con ese código');
    }

    const tax = await queryOne<Tax>(
      `INSERT INTO financial.taxes (
        tenant_id, company_id, name, code, tax_type, amount, included_in_price, created_by
      )
       VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
       RETURNING *`,
      [
        tenantId, dto.company_id, dto.name, dto.code, dto.tax_type,
        dto.amount, dto.included_in_price ?? false, userId
      ]
    );

    return tax!;
  }

  async update(id: string, dto: UpdateTaxDto, tenantId: string, userId: string): Promise<Tax> {
    const existing = await this.findById(id, tenantId);

    const updateFields: string[] = [];
    const values: any[] = [];
    let paramIndex = 1;

    if (dto.name !== undefined) {
      updateFields.push(`name = $${paramIndex++}`);
      values.push(dto.name);
    }
    if (dto.code !== undefined) {
      // Check unique code
      const existingCode = await queryOne(
        `SELECT id FROM financial.taxes WHERE tenant_id = $1 AND code = $2 AND id != $3`,
        [tenantId, dto.code, id]
      );
      if (existingCode) {
        throw new ConflictError('Ya existe un impuesto con ese código');
      }
      updateFields.push(`code = $${paramIndex++}`);
      values.push(dto.code);
    }
    if (dto.tax_type !== undefined) {
      updateFields.push(`tax_type = $${paramIndex++}`);
      values.push(dto.tax_type);
    }
    if (dto.amount !== undefined) {
      updateFields.push(`amount = $${paramIndex++}`);
      values.push(dto.amount);
    }
    if (dto.included_in_price !== undefined) {
      updateFields.push(`included_in_price = $${paramIndex++}`);
      values.push(dto.included_in_price);
    }
    if (dto.active !== undefined) {
      updateFields.push(`active = $${paramIndex++}`);
      values.push(dto.active);
    }

    if (updateFields.length === 0) {
      return existing;
    }

    updateFields.push(`updated_by = $${paramIndex++}`);
    values.push(userId);
    updateFields.push(`updated_at = CURRENT_TIMESTAMP`);

    values.push(id, tenantId);

    await query(
      `UPDATE financial.taxes SET ${updateFields.join(', ')}
       WHERE id = $${paramIndex++} AND tenant_id = $${paramIndex}`,
      values
    );

    return this.findById(id, tenantId);
  }

  async delete(id: string, tenantId: string): Promise<void> {
    await this.findById(id, tenantId);

    // Check if tax is used in any invoice lines
    const usageCheck = await queryOne<{ count: string }>(
      `SELECT COUNT(*) as count FROM financial.invoice_lines
       WHERE $1 = ANY(tax_ids)`,
      [id]
    );

    if (parseInt(usageCheck?.count || '0') > 0) {
      throw new ConflictError('No se puede eliminar: el impuesto está siendo usado en facturas');
    }

    await query(
      `DELETE FROM financial.taxes WHERE id = $1 AND tenant_id = $2`,
      [id, tenantId]
    );
  }

  /**
   * Calcula impuestos para una linea de documento
   * Sigue la logica de Odoo para calculos de IVA
   */
  async calculateTaxes(
    lineData: TaxCalculationInput,
    tenantId: string,
    transactionType: 'sales' | 'purchase' = 'sales'
  ): Promise<TaxCalculationResult> {
    // Validar inputs
    if (lineData.quantity <= 0 || lineData.priceUnit < 0) {
      return {
        amountUntaxed: 0,
        amountTax: 0,
        amountTotal: 0,
        taxBreakdown: [],
      };
    }

    // Calcular subtotal antes de impuestos
    const subtotal = lineData.quantity * lineData.priceUnit;
    const discountAmount = subtotal * (lineData.discount || 0) / 100;
    const amountUntaxed = subtotal - discountAmount;

    // Si no hay impuestos, retornar solo el monto sin impuestos
    if (!lineData.taxIds || lineData.taxIds.length === 0) {
      return {
        amountUntaxed,
        amountTax: 0,
        amountTotal: amountUntaxed,
        taxBreakdown: [],
      };
    }

    // Obtener impuestos de la BD
    const taxResults = await query<Tax>(
      `SELECT * FROM financial.taxes
       WHERE id = ANY($1) AND tenant_id = $2 AND active = true
       AND (tax_type = $3 OR tax_type = 'all')`,
      [lineData.taxIds, tenantId, transactionType]
    );

    if (taxResults.length === 0) {
      return {
        amountUntaxed,
        amountTax: 0,
        amountTotal: amountUntaxed,
        taxBreakdown: [],
      };
    }

    // Calcular impuestos
    const taxBreakdown: TaxBreakdownItem[] = [];
    let totalTax = 0;

    for (const tax of taxResults) {
      let taxBase = amountUntaxed;
      let taxAmount: number;

      if (tax.included_in_price) {
        // Precio incluye impuesto (IVA incluido)
        // Base = Precio / (1 + tasa)
        // Impuesto = Precio - Base
        taxBase = amountUntaxed / (1 + tax.amount / 100);
        taxAmount = amountUntaxed - taxBase;
      } else {
        // Precio sin impuesto (IVA añadido)
        // Impuesto = Base * tasa
        taxAmount = amountUntaxed * tax.amount / 100;
      }

      taxBreakdown.push({
        taxId: tax.id,
        taxName: tax.name,
        taxCode: tax.code,
        taxRate: tax.amount,
        includedInPrice: tax.included_in_price,
        base: Math.round(taxBase * 100) / 100,
        taxAmount: Math.round(taxAmount * 100) / 100,
      });

      totalTax += taxAmount;
    }

    // Redondear a 2 decimales
    const finalAmountTax = Math.round(totalTax * 100) / 100;
    const finalAmountUntaxed = Math.round(amountUntaxed * 100) / 100;
    const finalAmountTotal = Math.round((amountUntaxed + finalAmountTax) * 100) / 100;

    return {
      amountUntaxed: finalAmountUntaxed,
      amountTax: finalAmountTax,
      amountTotal: finalAmountTotal,
      taxBreakdown,
    };
  }

  /**
   * Calcula impuestos para multiples lineas (ej: para totales de documento)
   */
  async calculateDocumentTaxes(
    lines: TaxCalculationInput[],
    tenantId: string,
    transactionType: 'sales' | 'purchase' = 'sales'
  ): Promise<TaxCalculationResult> {
    let totalUntaxed = 0;
    let totalTax = 0;
    const allBreakdown: TaxBreakdownItem[] = [];

    for (const line of lines) {
      const result = await this.calculateTaxes(line, tenantId, transactionType);
      totalUntaxed += result.amountUntaxed;
      totalTax += result.amountTax;
      allBreakdown.push(...result.taxBreakdown);
    }

    // Consolidar breakdown por impuesto
    const consolidatedBreakdown = new Map<string, TaxBreakdownItem>();
    for (const item of allBreakdown) {
      const existing = consolidatedBreakdown.get(item.taxId);
      if (existing) {
        existing.base += item.base;
        existing.taxAmount += item.taxAmount;
      } else {
        consolidatedBreakdown.set(item.taxId, { ...item });
      }
    }

    return {
      amountUntaxed: Math.round(totalUntaxed * 100) / 100,
      amountTax: Math.round(totalTax * 100) / 100,
      amountTotal: Math.round((totalUntaxed + totalTax) * 100) / 100,
      taxBreakdown: Array.from(consolidatedBreakdown.values()),
    };
  }
}

// Interfaces para calculo de impuestos
export interface TaxCalculationInput {
  quantity: number;
  priceUnit: number;
  discount: number;
  taxIds: string[];
}

export interface TaxBreakdownItem {
  taxId: string;
  taxName: string;
  taxCode: string;
  taxRate: number;
  includedInPrice: boolean;
  base: number;
  taxAmount: number;
}

export interface TaxCalculationResult {
  amountUntaxed: number;
  amountTax: number;
  amountTotal: number;
  taxBreakdown: TaxBreakdownItem[];
}

export const taxesService = new TaxesService();
