// PRUEBA DE LA PANTALLA DE RETENCIONES (2026-07-31)
//
// El nucleo (retenciones.ts) existia con su prueba propia, pero no habia
// DONDE usarlo: ninguna ruta del servidor lo montaba, asi que el dueño no
// tenia como registrar un comprobante recibido. Esta prueba cubre la pantalla
// con el servidor de verdad andando:
//   1. Sin sesion no se ve; el cajero no la ve ni en el menu; el dueño si.
//   2. Registrar un comprobante valido queda en pantalla y en el resumen.
//   3. Lo que no cuadra (RIF malo, duplicado) rebota con su porque.
//   4. Anular deja contramarca, saca el comprobante del credito y no se
//      puede anular dos veces.
//   5. Cada periodo cuenta lo suyo (un comprobante viejo no infla el mes).
//   6. El CSV sale solo para quien exporta, con el nombre del archivo y las
//      celdas neutralizadas (un agente llamado "=SUMA(...)" no ejecuta nada).
//   7. Todo movimiento queda en la bitacora con el nombre del rol.
import { mkdtempSync, readFileSync, rmSync } from 'node:fs';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
import { iniciarServidor } from '../dist/servidor.js';
import { guardarConfig } from '../dist/config.js';
import { Acceso } from '../dist/acceso.js';

let fallos = 0;
const check = (cond, msg) => {
  console.log((cond ? '  OK  ' : '  XX  ') + msg);
  if (!cond) fallos++;
};

const dir = mkdtempSync(join(tmpdir(), 'fiscal-ret-pantalla-'));
const PUERTO = 8759;
const base = `http://127.0.0.1:${PUERTO}`;
let servidor;

const CLAVE_DUENO = 'la-del-dueno-2026';
const CLAVE_CAJERO = 'la-del-cajero-2026';

const CONFIG = {
  emisor: {
    rif: 'J-50856106-6',
    razon_social: 'Arepa Power de prueba',
    domicilio_fiscal: 'Araure, Portuguesa',
    precios_con_iva: true,
    alicuota_general: 16,
  },
  serie: 'A',
  sucursal: '1',
  equipo: 'FISCAL-01',
  imprenta: { adaptador: 'practica', nombre: 'Practica' },
  respaldo: null,
  usando: 'imprenta',
  hora_url: 'http://127.0.0.1:9/no-existe',
};

const forma = (datos, galleta) => ({
  method: 'POST',
  headers: { 'content-type': 'application/x-www-form-urlencoded', ...(galleta ? { cookie: galleta } : {}) },
  body: new URLSearchParams(datos).toString(),
  redirect: 'manual',
});

async function entrar(rol, clave) {
  const r = await fetch(`${base}/entrar`, forma({ rol, clave }));
  const set = r.headers.get('set-cookie');
  return set ? set.split(';')[0] : '';
}

const mesActual = new Date().toISOString().slice(0, 7);
const hoy = new Date().toISOString().slice(0, 10);
// Un comprobante del mes pasado, para probar que cada periodo cuenta lo suyo.
const mesPasadoFecha = new Date(Date.now() - 32 * 86400_000).toISOString().slice(0, 10);
const mesPasado = mesPasadoFecha.slice(0, 7);

const comprobante = (extra = {}) => ({
  numero: '20260700000123',
  fecha: hoy,
  agente_rif: 'J-50856106-6',
  agente_razon_social: 'Distribuidora El Llano C.A.',
  facturas: 'A-00000012',
  porcentaje: '75',
  iva_retenido_bs: '580.40',
  ...extra,
});

try {
  guardarConfig(dir, CONFIG);
  const acceso = new Acceso(join(dir, 'estado', 'acceso.json'));
  acceso.fijarClave('dueno', CLAVE_DUENO);
  acceso.fijarClave('cajero', CLAVE_CAJERO);
  servidor = await iniciarServidor({ base: dir, puerto: PUERTO, log: () => {} });

  console.log('\n== 1. La puerta: sin sesion no, cajero no, dueño si ==');
  const suelto = await fetch(`${base}/retenciones`, { redirect: 'manual' });
  check(suelto.status === 302 && suelto.headers.get('location') === '/entrar', `sin sesion manda a entrar (HTTP ${suelto.status})`);
  const cajero = await entrar('cajero', CLAVE_CAJERO);
  const cajeroVe = await fetch(`${base}/retenciones`, { headers: { cookie: cajero } });
  check(cajeroVe.status === 403, `el cajero no puede (HTTP ${cajeroVe.status})`);
  const menuCajero = await (await fetch(`${base}/`, { headers: { cookie: cajero } })).text();
  check(!menuCajero.includes('href="/retenciones"'), 'y ni le sale en el menu');
  const dueno = await entrar('dueno', CLAVE_DUENO);
  const pantalla = await fetch(`${base}/retenciones`, { headers: { cookie: dueno } });
  const cuerpo = await pantalla.text();
  check(pantalla.status === 200 && cuerpo.includes('Registrar un comprobante'), 'el dueño ve la pantalla');
  check(cuerpo.includes('href="/retenciones"'), 'con su seccion en el menu');
  check(cuerpo.includes('Ninguno registrado en este periodo'), 'que arranca vacia');

  console.log('\n== 2. Registrar un comprobante valido ==');
  const alta = await fetch(`${base}/retenciones`, forma(comprobante(), dueno));
  check(alta.status === 302 && (alta.headers.get('location') ?? '').includes(`periodo=${mesActual}`), 'entra y devuelve al periodo del comprobante');
  const conUno = await (await fetch(`${base}/retenciones`, { headers: { cookie: dueno } })).text();
  check(conUno.includes('20260700000123'), 'el numero queda en pantalla');
  check(conUno.includes('Distribuidora El Llano C.A.'), 'con su agente');
  check(conUno.includes('580.40'), 'y su monto en el credito del periodo');

  console.log('\n== 3. Lo que no cuadra, rebota con su porque ==');
  const rifMalo = await fetch(`${base}/retenciones`, forma(comprobante({ numero: 'X-1', agente_rif: 'J-12345678-0' }), dueno));
  check(rifMalo.status === 400 && (await rifMalo.text()).includes('RIF del agente'), 'el RIF con verificador malo no entra');
  const repetido = await fetch(`${base}/retenciones`, forma(comprobante(), dueno));
  check(repetido.status === 400 && (await repetido.text()).includes('ya esta registrado'), 'el duplicado avisa que ya estaba');
  const cajeroRegistra = await fetch(`${base}/retenciones`, forma(comprobante({ numero: 'C-1' }), cajero));
  check(cajeroRegistra.status === 403, 'y el cajero tampoco registra por POST directo');

  console.log('\n== 4. Anular deja contramarca y saca el credito ==');
  const sinMotivo = await fetch(`${base}/retenciones/anular`, forma({ numero: '20260700000123', motivo: '  ' }, dueno));
  check(sinMotivo.status === 400, 'sin motivo no se anula');
  const anulado = await fetch(`${base}/retenciones/anular`, forma({ numero: '20260700000123', motivo: 'monto mal transcrito' }, dueno));
  check(anulado.status === 302, 'con motivo si');
  const vacio = await (await fetch(`${base}/retenciones`, { headers: { cookie: dueno } })).text();
  check(vacio.includes('Ninguno registrado en este periodo'), 'el comprobante salio de la lista');
  const dosVeces = await fetch(`${base}/retenciones/anular`, forma({ numero: '20260700000123', motivo: 'otra vez' }, dueno));
  check(dosVeces.status === 400 && (await dosVeces.text()).includes('ya esta anulado'), 'y no se anula dos veces');

  console.log('\n== 5. Cada periodo cuenta lo suyo ==');
  const viejo = await fetch(`${base}/retenciones`, forma(comprobante({ numero: 'V-9', fecha: mesPasadoFecha, iva_retenido_bs: '100.00' }), dueno));
  check(viejo.status === 302 && (viejo.headers.get('location') ?? '').includes(`periodo=${mesPasado}`), 'el comprobante viejo cae en SU periodo');
  const esteMes = await (await fetch(`${base}/retenciones?periodo=${mesActual}`, { headers: { cookie: dueno } })).text();
  check(!esteMes.includes('>V-9<'), 'el mes actual no lo muestra');
  const aquelMes = await (await fetch(`${base}/retenciones?periodo=${mesPasado}`, { headers: { cookie: dueno } })).text();
  check(aquelMes.includes('>V-9<') && aquelMes.includes('100.00'), 'el mes del comprobante si, con su credito');

  console.log('\n== 6. El CSV del periodo, con sus celdas neutralizadas ==');
  const formula = await fetch(`${base}/retenciones`, forma(comprobante({ numero: 'F-7', agente_razon_social: '=SUMA(A1:A9)' }), dueno));
  check(formula.status === 302, 'entra un agente con nombre malicioso (el nombre lo escribe cualquiera)');
  const csvR = await fetch(`${base}/retenciones.csv?periodo=${mesActual}`, { headers: { cookie: dueno } });
  const csv = await csvR.text();
  check(csvR.status === 200 && (csvR.headers.get('content-disposition') ?? '').includes(`retenciones-${mesActual}.csv`), 'el archivo baja con su nombre');
  check(csv.includes('F-7'), 'trae el comprobante del periodo');
  check(csv.includes("'=SUMA(A1:A9)"), 'y la formula sale neutralizada, no ejecutable');
  check(!csv.includes('20260700000123'), 'el anulado no viaja en el archivo');
  const csvCajero = await fetch(`${base}/retenciones.csv?periodo=${mesActual}`, { headers: { cookie: cajero } });
  check(csvCajero.status === 403, 'el cajero no se lleva el archivo');

  console.log('\n== 7. Todo quedo en la bitacora con nombre ==');
  const bitacora = readFileSync(join(dir, 'estado', 'bitacora.jsonl'), 'utf8');
  const eventos = bitacora.trim().split('\n').map((l) => JSON.parse(l));
  const registradas = eventos.filter((e) => e.accion === 'retencion_registrada');
  const anuladas = eventos.filter((e) => e.accion === 'retencion_anulada');
  check(registradas.length === 3, `las 3 altas quedaron escritas (${registradas.length})`);
  check(registradas.every((e) => e.quien === 'Dueño'), 'con el nombre del rol que las hizo');
  check(anuladas.length === 1 && anuladas[0].detalle?.motivo === 'monto mal transcrito', 'y la anulacion con su motivo');
} catch (e) {
  console.log('  XX  ' + e.message);
  fallos++;
} finally {
  servidor?.cerrar();
  rmSync(dir, { recursive: true, force: true });
}

console.log(fallos === 0 ? '\nPANTALLA DE RETENCIONES: TODO VERDE' : `\n${fallos} FALLO(S)`);
process.exitCode = fallos === 0 ? 0 : 1; // salida natural: process.exit() con undici cerrando revienta libuv en Node 24/Windows
