/**
 * Corrige la CUENTA DE CONTRAPARTIDA de asientos de CONTADO ya generados cuando
 * se cambió el mapeo (ej. CAJA_GENERAL pasó de "Recaudaciones Efectivo" a "Banco
 * Familiar"). Los asientos existentes NO se regeneran solos al cambiar el mapeo,
 * así que quedan apuntando a la cuenta vieja.
 *
 * Sólo cambia el `cuenta_id` de la línea de contrapartida ("... (contado)") de los
 * asientos del tipo indicado. NO toca montos → el asiento sigue balanceado.
 *
 * Instancia sólo PrismaClient (rápido, sin AppModule). DRY-RUN por defecto:
 * requiere `--apply` para escribir.
 *
 * Uso:
 *   # ver qué cambiaría (no escribe):
 *   pnpm reparent:contrapartida-contado -- --empresa=<UUID> --tipo=gasto
 *   # aplicar, acotando período:
 *   pnpm reparent:contrapartida-contado -- --empresa=<UUID> --tipo=gasto --desde=2026-05-01 --hasta=2026-07-31 --apply
 *
 * Flags:
 *   --empresa=<UUID>          (obligatorio)
 *   --tipo=gasto|venta|compra (default gasto) → prefijo de glosa a corregir
 *   --concepto=CAJA_GENERAL   (default) → cuenta destino resuelta del mapeo
 *   --desde=YYYY-MM-DD --hasta=YYYY-MM-DD (opcional)
 *   --apply                   (sin esto, sólo dry-run)
 */
import 'dotenv/config';
import { PrismaClient } from '@prisma/client';

const prisma = new PrismaClient();

const GLOSA_POR_TIPO: Record<string, string> = {
  gasto: 'Gasto (contado)%',
  venta: 'Factura venta (contado)%',
  compra: 'Factura compra (contado)%',
};

function parseArgs(argv: string[]) {
  const out: any = { tipo: 'gasto', concepto: 'CAJA_GENERAL', apply: false };
  for (const a of argv.slice(2)) {
    if (a === '--apply') out.apply = true;
    else if (a.startsWith('--empresa=')) out.empresa = a.split('=')[1]?.trim();
    else if (a.startsWith('--tipo=')) out.tipo = a.split('=')[1]?.trim();
    else if (a.startsWith('--concepto=')) out.concepto = a.split('=')[1]?.trim();
    else if (a.startsWith('--desde=')) out.desde = a.split('=')[1]?.trim();
    else if (a.startsWith('--hasta=')) out.hasta = a.split('=')[1]?.trim();
  }
  return out;
}

async function main() {
  const { empresa, tipo, concepto, desde, hasta, apply } = parseArgs(process.argv);
  if (!empresa) { console.error('Falta --empresa=<UUID>'); process.exit(1); }
  const glosaLike = GLOSA_POR_TIPO[tipo];
  if (!glosaLike) { console.error(`--tipo inválido: ${tipo} (usar gasto|venta|compra)`); process.exit(1); }

  // Cuenta destino = la mapeada hoy para el concepto (ej. CAJA_GENERAL → Banco Familiar)
  const mapeo = await prisma.cont_mapeo_cuentas.findUnique({
    where: { empresa_id_concepto: { empresa_id: empresa, concepto } },
    select: { cuenta_id: true },
  });
  if (!mapeo) { console.error(`No hay mapeo para el concepto "${concepto}" en esa empresa.`); process.exit(1); }
  const target = mapeo.cuenta_id;
  const cta = await prisma.cont_plan_cuentas.findUnique({ where: { id: target }, select: { codigo: true, descripcion: true } });
  console.log(`Empresa=${empresa} · tipo=${tipo} · concepto=${concepto}`);
  console.log(`Cuenta destino (${concepto}): ${cta?.codigo} · ${cta?.descripcion}`);

  // Filtro de fecha opcional
  const params: any[] = [empresa, glosaLike, target];
  let fechaSql = '';
  if (desde) { params.push(desde); fechaSql += ` AND a.fecha >= $${params.length}::date`; }
  if (hasta) { params.push(hasta); fechaSql += ` AND a.fecha <= $${params.length}::date`; }

  const whereBase = `
    a.empresa_id = $1::uuid
    AND a.glosa ILIKE $2
    AND d.descripcion ILIKE '%contado%'
    AND d.cuenta_id <> $3::uuid
    ${fechaSql}`;

  // Resumen de lo que se cambiaría, agrupado por la cuenta actual
  const resumen: any[] = await prisma.$queryRawUnsafe(`
    SELECT c.codigo, c.descripcion AS cuenta_actual,
           COUNT(*)::int AS lineas,
           COALESCE(SUM(d.haber_pyg),0) AS total_haber,
           MIN(a.fecha) AS primera, MAX(a.fecha) AS ultima
    FROM cont_asientos_det d
    JOIN cont_asientos a ON a.id = d.asiento_id
    JOIN cont_plan_cuentas c ON c.id = d.cuenta_id
    WHERE ${whereBase}
    GROUP BY c.codigo, c.descripcion
    ORDER BY lineas DESC`, ...params);

  const totalLineas = resumen.reduce((a, r) => a + Number(r.lineas), 0);
  console.log(`\n${apply ? '=== APLICANDO ===' : '=== DRY-RUN (no escribe) ==='}`);
  if (totalLineas === 0) { console.log('Nada para corregir: ninguna línea apunta a una cuenta distinta a la destino.'); return; }
  console.log(`Líneas de contrapartida a repuntar → ${cta?.codigo}:`);
  for (const r of resumen) {
    const fmt = (d: any) => (d ? new Date(d).toISOString().slice(0, 10) : '?');
    console.log(`  desde ${r.codigo} (${r.cuenta_actual}): ${r.lineas} líneas · Gs. ${Number(r.total_haber).toLocaleString('es-PY')} · ${fmt(r.primera)}..${fmt(r.ultima)}`);
  }
  console.log(`  TOTAL: ${totalLineas} líneas`);

  if (!apply) { console.log('\n(Volvé a correr con --apply para escribir.)'); return; }

  const upd: any = await prisma.$executeRawUnsafe(`
    UPDATE cont_asientos_det d
    SET cuenta_id = $3::uuid
    FROM cont_asientos a
    WHERE d.asiento_id = a.id AND ${whereBase}`, ...params);
  console.log(`\n✅ Actualizadas ${upd} líneas → ${cta?.codigo} · ${cta?.descripcion}. (montos sin cambios → asientos siguen balanceados)`);
}

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