import 'dotenv/config';
import { PrismaClient, Prisma } from '@prisma/client';

/**
 * Reconstruye la cuenta a cobrar de facturas a credito que quedaron sin ella.
 *
 * `facturas.service.ts` solo crea la cuenta a cobrar dentro de
 * `if (cuotas && cuotas.length > 0)`. Si una factura a credito se emite sin cuotas, no
 * se crea la deuda: la factura queda con saldo vacio, el listado la muestra como pagada
 * y el wizard de cobros dice que el cliente no tiene facturas pendientes. La venta a
 * credito existe pero es incobrable desde el sistema.
 *
 * Este script la reconstruye con los datos de la propia factura: el total sale de
 * factura_subtotales y el vencimiento del plazo ya registrado en `dplazocre`. Cuando la
 * factura tampoco tiene cuotas, se genera una sola por el total — que es como opera el
 * resto del sistema para credito simple.
 *
 * Es idempotente: saltea las facturas que ya tienen cuenta.
 *
 * Uso:
 *   pnpm reparar:cxc -- --empresa=<uuid> --dry
 *   pnpm reparar:cxc -- --empresa=<uuid>
 */

const prisma = new PrismaClient();

const arg = (n: string) => process.argv.slice(2).find((a) => a.startsWith(`--${n}=`))?.split('=').slice(1).join('=');
const DRY = process.argv.includes('--dry');

/** "30 Dias" / "45 Días" / "8 dias" -> 30 / 45 / 8. Default 30. */
function diasDePlazo(plazo: string | null): number {
  const m = String(plazo ?? '').match(/\d+/);
  const n = m ? Number(m[0]) : NaN;
  return Number.isFinite(n) && n > 0 ? n : 30;
}

async function main() {
  const empresaId = arg('empresa');
  if (!empresaId) throw new Error('Falta --empresa=<uuid>');

  const candidatas = await prisma.factura_cab.findMany({
    where: {
      empresa_id: empresaId,
      icondcred: { not: null },
      estado: { not: 'Anulado' },
      cuentas_cobrar: { none: {} },
    },
    select: {
      id: true, dest: true, dpunexp: true, dnumdoc: true, dfeemide: true,
      cliente_id: true, moneda_id: true, dplazocre: true,
      factura_cuotas: { select: { id: true, dmoncuota: true, dvenccuo: true } },
      factura_subtotales: { select: { dtotgralope: true } },
    },
  });

  console.log(`Facturas a credito sin cuenta a cobrar: ${candidatas.length}`);
  if (candidatas.length === 0) return;
  console.log(`Modo: ${DRY ? 'DRY-RUN' : 'REAL'}\n`);

  let reparadas = 0;
  const sinTotal: string[] = [];

  for (const f of candidatas) {
    const nro = `${f.dest}-${f.dpunexp}-${f.dnumdoc}`;
    const total = Number(f.factura_subtotales?.[0]?.dtotgralope ?? 0);

    // Sin total no hay nada que cobrar: mejor dejarla marcada que inventar un monto.
    if (!(total > 0)) { sinTotal.push(nro); continue; }

    const moneda = f.moneda_id
      ? await prisma.moneda.findUnique({ where: { id: f.moneda_id }, select: { codigo: true } })
      : null;
    const monedaIso = (moneda?.codigo ?? 'PYG').slice(0, 3);

    const emision = f.dfeemide ?? new Date();
    const dias = diasDePlazo(f.dplazocre);
    const vencimiento = new Date(emision);
    vencimiento.setDate(vencimiento.getDate() + dias);

    const cuotasExistentes = f.factura_cuotas ?? [];
    const vencCuenta = cuotasExistentes.length
      ? cuotasExistentes.reduce((max, c) => (c.dvenccuo > max ? c.dvenccuo : max), cuotasExistentes[0].dvenccuo)
      : vencimiento;
    const montoCuenta = cuotasExistentes.length
      ? cuotasExistentes.reduce((s, c) => s + Number(c.dmoncuota), 0)
      : total;

    console.log(
      `${nro}  ${emision.toISOString().slice(0, 10)}  ${monedaIso} ${montoCuenta.toLocaleString('es-PY')}` +
      `  vence ${vencCuenta.toISOString().slice(0, 10)} (${f.dplazocre ?? `${dias} dias`})` +
      `${cuotasExistentes.length ? `  [${cuotasExistentes.length} cuotas ya existentes]` : '  [+1 cuota]'}`,
    );

    if (!DRY) {
      await prisma.$transaction(async (tx) => {
        const cuenta = await tx.cuentas_cobrar.create({
          data: {
            empresa_id: empresaId,
            cliente_id: f.cliente_id,
            factura_venta_id: f.id,
            fecha_emision: emision,
            fecha_vencimiento: vencCuenta,
            moneda_iso: monedaIso,
            monto_total: new Prisma.Decimal(montoCuenta),
            saldo_pendiente: new Prisma.Decimal(montoCuenta),
            estado: 'pendiente',
          },
          select: { id: true },
        });

        if (cuotasExistentes.length === 0) {
          await tx.factura_cuotas.create({
            data: {
              factura_cab_id: f.id,
              cuenta_id: cuenta.id,
              nro_cuota: 1,
              cmonecuo: monedaIso,
              dmoncuota: new Prisma.Decimal(total),
              dvenccuo: vencimiento,
              saldo_pendiente: new Prisma.Decimal(total),
            },
          });
        } else {
          // Cuotas huerfanas: se enganchan a la cuenta recien creada.
          await tx.factura_cuotas.updateMany({
            where: { factura_cab_id: f.id },
            data: { cuenta_id: cuenta.id },
          });
        }

        await tx.factura_cab.update({
          where: { id: f.id },
          data: { saldo_pendiente: new Prisma.Decimal(montoCuenta) },
        });
      });
    }
    reparadas++;
  }

  console.log('\n─── Resumen ───');
  console.log(`Reparadas    : ${reparadas}${DRY ? ' (simulado)' : ''}`);
  if (sinTotal.length) console.log(`Sin total    : ${sinTotal.length} -> ${sinTotal.join(', ')}`);
}

main()
  .then(() => prisma.$disconnect())
  .catch(async (e) => { console.error(e.message ?? e); await prisma.$disconnect(); process.exit(1); });
