/**
 * One-shot: aplica NCs históricas contra factura_cuotas para clientes en modo
 * ESTRICTO. Idempotente: si ya hay filas en nc_aplicaciones_cuota para una NC,
 * la saltea. Por defecto corre en DRY-RUN (solo reporta lo que haría).
 *
 * Detección de NCs candidatas:
 *   - factura_cab_id IS NOT NULL
 *   - rec_aplicacion_reversada != true
 *   - estado != 'Anulado' / 'Rechazado'
 *   - empresa con rec_config_empresa.nc_modo_default = 'ESTRICTO' (o sin config → default ESTRICTO)
 *   - sin registros en nc_aplicaciones_cuota
 *
 * Uso:
 *   pnpm exec ts-node scripts/backfill-nc-aplicaciones-cuota.ts [--empresa=<id>] [--apply]
 *
 * Sin --apply → dry-run (default seguro).
 * Con --apply → escribe cambios reales.
 */
import 'dotenv/config';
import { PrismaClient } from '@prisma/client';

type Args = {
  empresaId?: string;
  apply: boolean;
};

function parseArgs(argv: string[]): Args {
  const out: Args = { apply: false };
  for (const a of argv.slice(2)) {
    if (a === '--apply') out.apply = true;
    else if (a.startsWith('--empresa=')) out.empresaId = a.split('=')[1];
  }
  return out;
}

type NCCandidate = {
  id: string;
  empresa_id: string;
  factura_cab_id: string;
  numero: string;
  monto_nc: number;
  estado: string | null;
};

async function main() {
  const args = parseArgs(process.argv);
  const prisma = new PrismaClient();

  const mode = args.apply ? 'APPLY' : 'DRY-RUN';
  console.log(`\n=== Backfill nc_aplicaciones_cuota (${mode}) ===\n`);
  if (args.empresaId) console.log(`Filtrando por empresa=${args.empresaId}`);

  // 1) Empresas en modo ESTRICTO (o sin config = default ESTRICTO)
  const configs = await prisma.rec_config_empresa.findMany({
    select: { empresa_id: true, nc_modo_default: true },
  });
  const modoPorEmpresa = new Map(configs.map((c) => [c.empresa_id, c.nc_modo_default]));

  // 2) NCs candidatas
  const ncs = await prisma.nota_credito_cab.findMany({
    where: {
      factura_cab_id: { not: null },
      ...(args.empresaId ? { empresa_id: args.empresaId } : {}),
      NOT: {
        OR: [
          { estado: 'Anulado' },
          { estado: 'Rechazado' },
          { rec_aplicacion_reversada: true } as any,
        ],
      },
    },
    select: {
      id: true,
      empresa_id: true,
      factura_cab_id: true,
      dest: true,
      dpunexp: true,
      dnumdoc: true,
      estado: true,
    },
    orderBy: { created_at: 'asc' },
  });

  const candidatas: NCCandidate[] = [];
  for (const nc of ncs) {
    if (!nc.empresa_id) continue;
    const modo = (modoPorEmpresa.get(nc.empresa_id) ?? 'ESTRICTO').toUpperCase();
    if (modo !== 'ESTRICTO') continue;

    const yaAplicada = await prisma.nc_aplicaciones_cuota.count({
      where: { nota_credito_cab_id: nc.id },
    });
    if (yaAplicada > 0) continue;

    const subtotales = await prisma.nota_credito_subtotal.findMany({
      where: { nota_credito_cab_id: nc.id },
      select: { dtotgralope: true },
    });
    const monto = subtotales.reduce((acc, s) => acc + Number(s.dtotgralope || 0), 0);
    if (monto <= 0) continue;

    if (!nc.factura_cab_id) continue;
    candidatas.push({
      id: nc.id,
      empresa_id: nc.empresa_id,
      factura_cab_id: nc.factura_cab_id,
      numero: `${nc.dest ?? '???'}-${nc.dpunexp ?? '???'}-${nc.dnumdoc ?? '???'}`,
      monto_nc: monto,
      estado: nc.estado,
    });
  }

  console.log(`\nNCs candidatas encontradas: ${candidatas.length}\n`);

  let totalCuotasAfectadas = 0;
  let totalCuotasCanceladas = 0;
  let totalMontoAplicado = 0;
  let ncsConSobrante = 0;

  for (const nc of candidatas) {
    const factura = await prisma.factura_cab.findUnique({
      where: { id: nc.factura_cab_id },
      select: {
        dest: true,
        dpunexp: true,
        dnumdoc: true,
        total_factura: true,
      },
    });
    const facturaNum = factura
      ? `${factura.dest}-${factura.dpunexp}-${factura.dnumdoc}`
      : '?';

    const cuotas = await prisma.factura_cuotas.findMany({
      where: {
        factura_cab_id: nc.factura_cab_id,
        estado: { notIn: ['pagado', 'Pagado'] },
      },
      orderBy: { nro_cuota: 'asc' },
    });

    if (cuotas.length === 0) {
      console.log(
        `  · NC ${nc.numero} → factura ${facturaNum} — sin cuotas pendientes, skip`,
      );
      continue;
    }

    const aplicaciones: { cuotaId: string; nroCuota: number | null; monto: number; nuevoSaldo: number; queda: string }[] = [];
    let restante = nc.monto_nc;

    for (const cuota of cuotas) {
      if (restante <= 0) break;
      const saldoCuota =
        cuota.saldo_pendiente && Number(cuota.saldo_pendiente) > 0
          ? Number(cuota.saldo_pendiente)
          : Number(cuota.dmoncuota);
      if (saldoCuota <= 0) continue;

      const aplicar = Math.min(restante, saldoCuota);
      const nuevoSaldo = saldoCuota - aplicar;

      aplicaciones.push({
        cuotaId: cuota.id,
        nroCuota: cuota.nro_cuota,
        monto: aplicar,
        nuevoSaldo,
        queda: nuevoSaldo <= 0.009 ? 'pagado' : 'pendiente',
      });
      restante -= aplicar;
    }

    if (aplicaciones.length === 0) continue;

    const sobrante = restante > 0.009 ? restante : 0;
    if (sobrante > 0) ncsConSobrante++;

    console.log(
      `  · NC ${nc.numero} (${nc.monto_nc.toFixed(2)}) → factura ${facturaNum}`,
    );
    for (const a of aplicaciones) {
      totalCuotasAfectadas++;
      if (a.queda === 'pagado') totalCuotasCanceladas++;
      totalMontoAplicado += a.monto;
      console.log(
        `      cuota #${a.nroCuota ?? '?'}: aplicar ${a.monto.toFixed(2)} → saldo=${a.nuevoSaldo.toFixed(2)} (${a.queda})`,
      );
    }
    if (sobrante > 0) {
      console.log(
        `      sobrante NC: ${sobrante.toFixed(2)} (queda como saldo a favor via factura_cab.saldo_disponible existente)`,
      );
    }

    if (args.apply) {
      await prisma.$transaction(async (tx) => {
        for (const a of aplicaciones) {
          await tx.factura_cuotas.update({
            where: { id: a.cuotaId },
            data: {
              saldo_pendiente: a.nuevoSaldo,
              estado: a.queda,
              updated_at: new Date(),
            },
          });
          await tx.nc_aplicaciones_cuota.create({
            data: {
              empresa_id: nc.empresa_id,
              nota_credito_cab_id: nc.id,
              factura_cuota_id: a.cuotaId,
              monto_aplicado: a.monto,
            },
          });
        }
      });
    }
  }

  console.log(`\n=== Resumen (${mode}) ===`);
  console.log(`NCs procesadas:         ${candidatas.length}`);
  console.log(`Cuotas afectadas:       ${totalCuotasAfectadas}`);
  console.log(`Cuotas canceladas:      ${totalCuotasCanceladas}`);
  console.log(`Monto total aplicado:   ${totalMontoAplicado.toFixed(2)}`);
  console.log(`NCs con sobrante:       ${ncsConSobrante}`);
  if (!args.apply) {
    console.log(`\nDRY-RUN — no se escribieron cambios. Corré con --apply para aplicar.\n`);
  } else {
    console.log(`\nAPLICADO ✅\n`);
  }

  await prisma.$disconnect();
}

main()
  .then(() => process.exit(0))
  .catch((e) => {
    console.error('Falló el backfill:', e);
    process.exit(1);
  });
