/**
 * One-shot: genera el movimiento de caja de SALIDA (reversión) para los
 * recibos_cobro que fueron anulados ANTES del fix en CobrosService.anular(),
 * y que tenían un movimiento de ENTRADA en efectivo asociado a una sesión.
 *
 * Detección: recibos con estado='anulado', sesion_caja_id no nulo, que NO
 * tengan ya un movimiento de SALIDA con `referencia = recibo.id`.
 *
 * Idempotente: si ya existe la reversión, lo saltea.
 *
 * Uso:
 *   pnpm exec ts-node scripts/reversar-mov-caja-recibos-anulados.ts [--empresa=<id>] [--desde=YYYY-MM-DD] [--hasta=YYYY-MM-DD] [--dry]
 */
import 'dotenv/config';
import { NestFactory } from '@nestjs/core';
import { AppModule } from '../src/app.module';
import { PrismaService } from '../src/prisma/prisma.service';

type Args = {
  empresaId?: string;
  desde?: Date;
  hasta?: Date;
  dry: boolean;
};

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

async function main() {
  const args = parseArgs(process.argv);
  const app = await NestFactory.createApplicationContext(AppModule, {
    logger: ['error', 'warn', 'log'],
  });
  const prisma = app.get(PrismaService);

  const where: any = {
    estado: 'anulado',
    sesion_caja_id: { not: null },
  };
  if (args.empresaId) where.empresa_id = args.empresaId;
  if (args.desde || args.hasta) {
    where.updated_at = {};
    if (args.desde) where.updated_at.gte = args.desde;
    if (args.hasta) {
      const h = new Date(args.hasta);
      h.setHours(23, 59, 59, 999);
      where.updated_at.lte = h;
    }
  }

  const recibos = await prisma.recibos_cobro.findMany({
    where,
    include: { recibo_cobro_detalle: true },
    orderBy: { updated_at: 'asc' },
  });

  console.log(`Recibos anulados candidatos: ${recibos.length}${args.dry ? ' [DRY-RUN]' : ''}`);

  const medioEfectivo = await prisma.medio_pago.findFirst({ where: { codigo: 1 } });
  const tipoSalida = await prisma.tipo_movimiento_cajas.findFirst({
    where: { descripcion: { contains: 'Salida', mode: 'insensitive' } },
  });

  let creados = 0;
  let skipYaRevertidos = 0;
  let skipSinEfectivo = 0;
  let skipSinSesion = 0;
  let errores = 0;

  for (const recibo of recibos) {
    try {
      if (!recibo.sesion_caja_id) {
        skipSinSesion++;
        continue;
      }

      const montoEfectivo = recibo.recibo_cobro_detalle
        .filter((d) => medioEfectivo && d.medio_pago_id === medioEfectivo.id)
        .reduce((sum, d) => sum + Number(d.monto_pagado || 0), 0);

      if (montoEfectivo <= 0) {
        skipSinEfectivo++;
        continue;
      }

      // ¿Ya existe una salida con esa referencia?
      const yaExiste = await prisma.movimiento_cajas.findFirst({
        where: {
          referencia: recibo.id,
          tipo_movimiento_caja_id: tipoSalida?.id,
        },
      });
      if (yaExiste) {
        skipYaRevertidos++;
        continue;
      }

      if (args.dry) {
        console.log(
          `  [DRY] Recibo ${recibo.numero_recibo} (${recibo.id}) | sesion=${recibo.sesion_caja_id} | monto=${montoEfectivo}`,
        );
        creados++;
        continue;
      }

      await prisma.$transaction(async (tx) => {
        const sesion = await tx.sesiones_caja.findUnique({
          where: { id: recibo.sesion_caja_id! },
        });
        if (!sesion) return;

        await tx.movimiento_cajas.create({
          data: {
            sesion_id: sesion.id,
            empresa_id: String(recibo.empresa_id),
            tipo_movimiento_caja_id: tipoSalida?.id || null,
            monto: montoEfectivo,
            descripcion: `Anulación Recibo ${recibo.numero_recibo} (reversión histórica)`,
            referencia: recibo.id,
            fecha_mov: (recibo.updated_at || new Date()).toISOString(),
          },
        });

        const entradasActuales = Number(sesion.monto_entradas_manual) || 0;
        await tx.sesiones_caja.update({
          where: { id: sesion.id },
          data: {
            monto_entradas_manual: Math.max(0, entradasActuales - montoEfectivo),
            updated_at: new Date(),
          },
        });
      });

      creados++;
      console.log(`  ✔ Reversión creada para recibo ${recibo.numero_recibo} | monto=${montoEfectivo}`);
    } catch (err: any) {
      errores++;
      console.error(`  ✖ Error recibo ${recibo.numero_recibo}: ${err.message}`);
    }
  }

  console.log('\n=== Resumen ===');
  console.log(`  Reversiones creadas:    ${creados}`);
  console.log(`  Ya revertidos (skip):   ${skipYaRevertidos}`);
  console.log(`  Sin efectivo (skip):    ${skipSinEfectivo}`);
  console.log(`  Sin sesión (skip):      ${skipSinSesion}`);
  console.log(`  Errores:                ${errores}`);

  await app.close();
}

main().catch((err) => {
  console.error(err);
  process.exit(1);
});
