// PRUEBA DE LA CONTINGENCIA Y DE LA CLAVE DEL SENIAT (2026-07-29)
//
// Nivel 2 del sprint de blindaje, puntos 6 y 7.
//
// CONTINGENCIA: cuando se cae todo (luz, internet, la computadora), el negocio
// factura a mano en su talonario. Si esas facturas no se cargan despues, el
// reporte del mes queda con un hueco del tamaño del apagon, y ese hueco es lo
// primero que pregunta un fiscal.
//
// CLAVE DEL SENIAT: la norma pide una clave de consulta PARA EL SENIAT. Si es la
// misma del dueño, entregarsela es entregarle la estacion completa.
import { mkdtempSync, rmSync } from 'node:fs';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
import { EstacionFiscal } from '../dist/estacion.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-cont-'));

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',
};

// El apagon fue anteayer: esas facturas son de ese dia, no de hoy.
const anteayer = new Date(Date.now() - 2 * 86400_000);
const enElApagon = (h) => new Date(anteayer.getFullYear(), anteayer.getMonth(), anteayer.getDate(), h, 0).toISOString();

const delTalonario = (numero, total, hora) => ({
  numero,
  emitido_en: enElApagon(hora),
  cliente: { nombre: 'Consumidor final' },
  total_usd: total,
  alicuota: 16,
  tasa_bs: 742.23,
});

try {
  guardarConfig(dir, CONFIG);
  const est = await EstacionFiscal.abrir(dir);

  console.log('\n== 1. Se cargan las facturas que se hicieron a mano ==');
  const una = await est.cargarTalonario(delTalonario('00001234', 23.2, 13));
  check(una.sellada === true, `entra la primera (${una.serie}-${una.numero})`);
  const dos = await est.cargarTalonario(delTalonario('00001235', 11.6, 14));
  check(dos.sellada === true, 'y la segunda');
  check(est.talonarioCargado().length === 2, 'las dos quedan listadas por su numero de papel');

  console.log('\n== 2. La misma factura no se carga dos veces ==');
  const repetida = await est.cargarTalonario(delTalonario('00001234', 23.2, 13));
  check(repetida.sellada === false, `avisa que ya estaba (${(repetida.problema ?? '').slice(0, 45)}...)`);
  check(est.talonarioCargado().length === 2, 'y el libro sigue con dos, no con tres');

  console.log('\n== 3. Lo que no cuadra, no entra ==');
  const sinNumero = await est.cargarTalonario({ ...delTalonario('', 10, 12) });
  check(sinNumero.sellada === false, 'sin el numero del papel no se carga');
  const futuro = await est.cargarTalonario({
    ...delTalonario('00009999', 10, 12),
    emitido_en: new Date(Date.now() + 86400_000).toISOString(),
  });
  check(futuro.sellada === false, 'ni una factura con fecha del futuro');
  const sinPlata = await est.cargarTalonario({ ...delTalonario('00008888', 0, 12) });
  check(sinPlata.sellada === false, 'ni una en cero');
  const alicuotaRara = await est.cargarTalonario({ ...delTalonario('00007777', 10, 12), alicuota: 12 });
  check(alicuotaRara.sellada === false, 'ni con una alicuota inventada');

  console.log('\n== 4. Cuentan en el MES QUE FUERON, no en el que se cargaron ==');
  // Esta es la razon de ser de todo esto: el reporte del mes del apagon tiene
  // que traerlas, aunque se hayan cargado despues.
  const aud = est.auditor('prueba', 'local');
  const delDia = aud.totales(
    new Date(anteayer.getFullYear(), anteayer.getMonth(), anteayer.getDate(), 0, 0),
    new Date(anteayer.getFullYear(), anteayer.getMonth(), anteayer.getDate(), 23, 59, 59),
  );
  check(delDia.documentos === 2, `el dia del apagon trae sus 2 facturas (${delDia.documentos})`);
  check(Math.abs(delDia.total_usd - 34.8) < 0.02, `con su plata (${delDia.total_usd} USD)`);
  check(Math.abs(delDia.iva_usd - 4.8) < 0.05, `y su IVA sacado hacia atras (${delDia.iva_usd} USD)`);
  const filas = aud.reporte(new Date(anteayer.getTime() - 86400_000), new Date());
  const fila = filas.find((f) => f.numero_control_imprenta === '00001234');
  check(!!fila, 'en el reporte sale con el numero que traia impreso el papel');
  check(fila?.tipo === 'factura_manual', `marcada como lo que es (${fila?.tipo})`);

  console.log('\n== 5. No se le manda a la imprenta ==');
  // El papel ya trae su numero y su control: este sistema no lo emitio.
  check(est.estado().pendientes === 0, 'no queda nada en la cola de transmision');

  console.log('\n== 6. El respaldo tambien se las lleva ==');
  check(est.estadoRespaldo().aqui === 2, 'estan en el libro y en el respaldo cifrado');

  console.log('\n== 7. La clave del SENIAT no puede ser la del negocio ==');
  const acceso = new Acceso(join(dir, 'estado', 'acceso.json'));
  acceso.fijarClave('dueno', 'la-clave-del-dueno-2026');
  check(acceso.esLaMismaClaveDe('dueno', 'la-clave-del-dueno-2026'), 'se reconoce cuando una clave ya es de otro');
  check(!acceso.esLaMismaClaveDe('dueno', 'otra-cosa-2026'), 'y cuando no lo es');
  acceso.fijarClave('auditor', 'la-del-seniat-2026');
  check(!acceso.esLaMismaClaveDe('dueno', 'la-del-seniat-2026'), 'la del SENIAT quedo distinta a la del dueño');
  // Y comprobar una clave ajena NO gasta intentos del otro rol.
  acceso.esLaMismaClaveDe('dueno', 'probando');
  acceso.esLaMismaClaveDe('dueno', 'probando');
  acceso.esLaMismaClaveDe('dueno', 'probando');
  check(acceso.entrar('dueno', 'la-clave-del-dueno-2026') === true, 'y comprobarla no le gasta intentos a nadie');
} catch (e) {
  console.log('  XX  ' + e.message);
  fallos++;
} finally {
  rmSync(dir, { recursive: true, force: true });
}

console.log(fallos === 0 ? '\nCONTINGENCIA Y CLAVE DEL SENIAT: 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
