/**
 * Validación Tanda A: para cada contribuyente con datos en metricas_mensuales,
 * toma 5 filas al azar y compara contra el cálculo on-the-fly usando los
 * servicios canónicos (dashboard, impuestos). Reporta PASS/FAIL por celda.
 *
 * Uso:
 *   pnpm --filter @horux/api exec tsx scripts/validate-metricas.ts
 */
import { prisma, tenantDb } from '../src/config/database.js';
import {
  calcularIngresosPorRegimen,
  calcularEgresosPorRegimen,
} from '../src/services/dashboard.service.js';
import { getResumenIva } from '../src/services/impuestos.service.js';

const TOL = 0.01; // tolerancia de $0.01 para redondeo decimal

interface StoredRow {
  contribuyente_id: string;
  anio: number;
  mes: number;
  regimen_fiscal: string | null;
  ingresos_cobrados: string;
  egresos_pagados: string;
  iva_trasladado_total: string;
  iva_acreditable: string;
  iva_retenido_cobrado: string;
  iva_resultado: string;
  cfdis_emitidos_count: number;
  cfdis_recibidos_count: number;
  cfdis_cancelados_count: number;
}

function cmp(a: number, b: number): boolean {
  return Math.abs(a - b) <= TOL;
}

function fmt(n: number): string {
  return n.toLocaleString('es-MX', { minimumFractionDigits: 2, maximumFractionDigits: 2 });
}

async function validateRow(
  tenantId: string,
  row: StoredRow,
): Promise<{ pass: boolean; diffs: string[] }> {
  const tenant = await prisma.tenant.findUnique({
    where: { id: tenantId },
    select: { databaseName: true },
  });
  if (!tenant) return { pass: false, diffs: ['tenant no encontrado'] };

  const pool = await tenantDb.getPool(tenantId, tenant.databaseName);
  const fi = `${row.anio}-${String(row.mes).padStart(2, '0')}-01`;
  const lastDay = new Date(row.anio, row.mes, 0).getDate();
  const ff = `${row.anio}-${String(row.mes).padStart(2, '0')}-${String(lastDay).padStart(2, '0')}`;

  // Ejecutamos secuencial para evitar interferencia entre queries bajo el pool
  // limit del tenant (max 3 conexiones). Con Promise.all concurrente, algunas
  // queries compartidas de getResumenIva devolvían valores parciales.
  const ingresos = await calcularIngresosPorRegimen(pool, tenantId, fi, ff, [], undefined, false, row.contribuyente_id);
  const egresos = await calcularEgresosPorRegimen(pool, tenantId, fi, ff, [], undefined, false, row.contribuyente_id);
  const resumenIva = await getResumenIva(pool, fi, ff, tenantId, false, row.contribuyente_id);

  const reg = row.regimen_fiscal;
  const ingOtf = ingresos.porRegimen.find(r => r.regimenClave === reg)?.monto || 0;
  const egrOtf = egresos.porRegimen.find(r => r.regimenClave === reg)?.monto || 0;
  const trasOtf = resumenIva.trasladadoPorRegimen.find(r => r.regimenClave === reg)?.monto || 0;
  const acrOtf = resumenIva.acreditablePorRegimen.find(r => r.regimenClave === reg)?.monto || 0;
  const retOtf = resumenIva.retenidoPorRegimen.find(r => r.regimenClave === reg)?.monto || 0;
  const resOtf = trasOtf - acrOtf - retOtf;

  const diffs: string[] = [];
  const ingStored = Number(row.ingresos_cobrados);
  const egrStored = Number(row.egresos_pagados);
  const trasStored = Number(row.iva_trasladado_total);
  const acrStored = Number(row.iva_acreditable);
  const retStored = Number(row.iva_retenido_cobrado);
  const resStored = Number(row.iva_resultado);

  if (!cmp(ingStored, ingOtf)) diffs.push(`ingresos: tabla=${fmt(ingStored)} vs otf=${fmt(ingOtf)}`);
  if (!cmp(egrStored, egrOtf)) diffs.push(`egresos: tabla=${fmt(egrStored)} vs otf=${fmt(egrOtf)}`);
  if (!cmp(trasStored, trasOtf)) diffs.push(`ivaTras: tabla=${fmt(trasStored)} vs otf=${fmt(trasOtf)}`);
  if (!cmp(acrStored, acrOtf)) diffs.push(`ivaAcr: tabla=${fmt(acrStored)} vs otf=${fmt(acrOtf)}`);
  if (!cmp(retStored, retOtf)) diffs.push(`ivaRet: tabla=${fmt(retStored)} vs otf=${fmt(retOtf)}`);
  if (!cmp(resStored, resOtf)) diffs.push(`ivaResultado: tabla=${fmt(resStored)} vs otf=${fmt(resOtf)}`);

  return { pass: diffs.length === 0, diffs };
}

async function main() {
  console.log('=== Validación metricas_mensuales (5 muestras aleatorias por contribuyente) ===\n');

  const tenants = await prisma.tenant.findMany({
    where: { active: true },
    select: { id: true, rfc: true, databaseName: true },
  });

  let totalMuestras = 0;
  let totalPass = 0;
  let totalFail = 0;

  for (const t of tenants) {
    const pool = await tenantDb.getPool(t.id, t.databaseName);
    const { rows: contribs } = await pool.query<{ entidad_id: string; nombre: string }>(
      `SELECT c.entidad_id, eg.nombre
       FROM contribuyentes c
       JOIN entidades_gestionadas eg ON eg.id = c.entidad_id
       WHERE EXISTS (
         SELECT 1 FROM metricas_mensuales m WHERE m.contribuyente_id = c.entidad_id
       )`,
    );

    if (contribs.length === 0) continue;
    console.log(`\n[${t.rfc}] ${contribs.length} contribuyentes con datos`);

    for (const c of contribs) {
      const { rows: samples } = await pool.query<StoredRow>(
        `SELECT contribuyente_id::text, anio, mes, regimen_fiscal,
                ingresos_cobrados, egresos_pagados,
                iva_trasladado_total, iva_acreditable, iva_retenido_cobrado, iva_resultado,
                cfdis_emitidos_count, cfdis_recibidos_count, cfdis_cancelados_count
         FROM metricas_mensuales
         WHERE contribuyente_id = $1
         ORDER BY random()
         LIMIT 5`,
        [c.entidad_id],
      );

      console.log(`  ${c.nombre} (${samples.length} muestras):`);
      for (const s of samples) {
        totalMuestras++;
        const { pass, diffs } = await validateRow(t.id, s);
        const mesLabel = `${s.anio}-${String(s.mes).padStart(2, '0')}`;
        const reg = s.regimen_fiscal || 'null';
        if (pass) {
          totalPass++;
          console.log(`    ✓ ${mesLabel} reg=${reg}  ingresos=$${fmt(Number(s.ingresos_cobrados))}`);
        } else {
          totalFail++;
          console.log(`    ✗ ${mesLabel} reg=${reg}  DIFFS:`);
          for (const d of diffs) console.log(`        - ${d}`);
        }
      }
    }
  }

  console.log(`\n=== Resumen ===`);
  console.log(`  Muestras totales: ${totalMuestras}`);
  console.log(`  PASS:             ${totalPass}`);
  console.log(`  FAIL:             ${totalFail}`);

  await prisma.$disconnect();
  process.exit(totalFail > 0 ? 1 : 0);
}

main().catch(async (err) => {
  console.error('Fatal:', err);
  await prisma.$disconnect().catch(() => {});
  process.exit(1);
});
