// PRUEBA DEL BLOQUE 6: LA PUERTA DEL FISCAL (2026-07-28)
// Providencia 000121, art. 3 num. 8: hay que darle al SENIAT una clave de
// consulta con acceso al sistema. Lo que se prueba aca es que esa puerta deje
// mirar TODO y tocar NADA, y que quede constancia de lo que se miro.
import { mkdtempSync, rmSync } from 'node:fs';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
import { LibroFiscal } from '../dist/libro.js';
import { Bitacora } from '../dist/bitacora.js';
import { ColaTransmision } from '../dist/cola.js';
import { Auditor, celdaCsv } from '../dist/auditor.js';
import { Acceso } from '../dist/acceso.js';
import { armarDocumento, armarNotaCredito } from '../dist/armador.js';
import { ImprentaPractica } from '../dist/imprenta.js';

let fallos = 0;
const check = (cond, msg) => {
  console.log((cond ? '  OK  ' : '  XX  ') + msg);
  if (!cond) fallos++;
};
const dir = mkdtempSync(join(tmpdir(), 'fiscal-aud-'));

const EMISOR = {
  rif: 'J-50856106-6',
  razon_social: 'Emprendimiento de prueba',
  domicilio_fiscal: 'Araure',
  precios_con_iva: true,
  alicuota_general: 16,
};
const venta = (id, precio, alicuota = 16) => ({
  origen_id: id,
  canal: 'mesa',
  cliente: { nombre: 'Consumidor final' },
  renglones: [{ nombre: 'Arepa', cantidad: 1, precio_usd: precio, alicuota }],
  tasa_bs: 742.23,
  pago: { metodo: 'efectivo' },
});

try {
  const libro = new LibroFiscal({ ruta: join(dir, 'libro.jsonl'), serie: 'A', equipo: 'FISCAL-01' });
  const bitacora = new Bitacora(join(dir, 'bitacora.jsonl'));
  const cola = new ColaTransmision({ ruta: join(dir, 'transmision.jsonl'), imprenta: new ImprentaPractica() });

  const a1 = libro.asentar('factura', armarDocumento(venta('v1', 10), EMISOR));
  const a2 = libro.asentar('factura', armarDocumento(venta('v2', 5, 0), EMISOR));
  await cola.encolar(a1);
  await cola.encolar(a2);
  libro.corregir({ tipo: 'factura', numero: 1 }, armarNotaCredito(a1.documento, 'Cobro de mas'));

  console.log('\n== 1. La clave del auditor va guardada, no en texto plano ==');
  const acceso = new Acceso(join(dir, 'acceso.json'));
  acceso.fijarClave('auditor', 'clave-del-fiscal-2026');
  const crudo = (await import('node:fs')).readFileSync(join(dir, 'acceso.json'), 'utf8');
  check(!crudo.includes('clave-del-fiscal-2026'), 'la clave NO aparece escrita en el archivo');
  check(acceso.entrar('auditor', 'clave-del-fiscal-2026'), 'con la clave correcta entra');
  check(!acceso.entrar('auditor', 'otra-cosa-cualquiera'), 'con una clave mala no entra');
  let corta = false;
  try {
    acceso.fijarClave('auditor', '1234');
  } catch {
    corta = true;
  }
  check(corta, 'no deja poner una clave de cuatro digitos');

  console.log('\n== 2. Frena a quien anda probando claves ==');
  let avisoDeFreno = false;
  for (let i = 0; i < 6; i++) {
    try {
      acceso.entrar('auditor', 'mala');
    } catch (e) {
      // Al pasarse de intentos deja de responder y lo dice: eso es lo correcto.
      avisoDeFreno = /demasiados intentos/i.test(e.message);
    }
  }
  check(acceso.frenado('auditor'), 'tras varios intentos fallidos, queda frenado un rato');
  check(avisoDeFreno, 'y avisa por que dejo de responder, en vez de decir clave mala');

  console.log('\n== 3. El fiscal consulta y exporta ==');
  const auditor = new Auditor({ libro, bitacora, cola, quien: 'fiscal.seniat', desde: '10.0.0.5' });
  const ayer = new Date(Date.now() - 86400000);
  const mañana = new Date(Date.now() + 86400000);
  const docs = auditor.consultar(ayer, mañana);
  check(docs.length === 3, `ve los 3 documentos del rango (${docs.length})`);
  check(auditor.consultar(new Date('2020-01-01'), new Date('2020-12-31')).length === 0, 'y nada fuera del rango');

  const csv = auditor.exportarCsv(ayer, mañana);
  const lineas = csv.split('\n');
  check(lineas[0].startsWith('fecha;tipo;serie;numero;control'), 'el reporte plano sale con sus columnas');
  check(lineas.length === 4, `una linea por documento mas el encabezado (${lineas.length})`);
  check(csv.includes('PRACTICA-'), 'e incluye el numero de control que dio la imprenta');
  check(csv.includes('nota_credito'), 'con las notas de credito incluidas');

  console.log('\n== 4. Los totales del periodo, con su desglose ==');
  const t = auditor.totales(ayer, mañana);
  check(t.documentos === 3, `cuenta los 3 documentos (${t.documentos})`);
  check(t.por_alicuota.some((x) => x.alicuota === 16) && t.por_alicuota.some((x) => x.alicuota === 0), 'separa lo gravado de lo exento');
  // La nota de credito resta: 10 (factura) + 5 (exenta) - 10 (nota) = 5
  check(Math.round(t.total_usd * 100) === 500, `y la nota de credito RESTA del total (${t.total_usd})`);

  console.log('\n== 5. El fiscal puede comprobar el libro por su cuenta ==');
  const integridad = auditor.comprobarIntegridad();
  check(integridad.integro, 'la comprobacion de integridad la puede correr el mismo');

  console.log('\n== 6. Mira todo, toca nada ==');
  check(auditor.sellar === undefined && auditor.anular === undefined && auditor.configurar === undefined, 'la puerta del fiscal NO tiene con que sellar, anular ni configurar');
  check(libro.verificar().integro, 'y despues de toda la inspeccion el libro sigue igual');

  console.log('\n== 7. Queda constancia de lo que miro ==');
  const eventos = bitacora.todos();
  const suyos = eventos.filter((e) => e.quien === 'fiscal.seniat');
  check(suyos.length >= 5, `sus consultas quedaron anotadas (${suyos.length} eventos)`);
  check(suyos.every((e) => e.desde === '10.0.0.5'), 'con la direccion desde la que entro');
  check(suyos.some((e) => e.accion === 'exportacion'), 'incluida la exportacion que se llevo');
  check(bitacora.verificar().integro, 'y la bitacora sigue encadenada');

  console.log('\n== 8. El registro de EVENTOS tambien se consulta (art. 3, num. 8) ==');
  const evs = auditor.eventos(ayer, mañana);
  check(evs.length > 0, `los eventos del rango salen (${evs.length})`);
  check(evs.some((e) => e.accion === 'consulta_fiscal'), 'incluidas las consultas previas del propio fiscal');
  const rastro = bitacora.todos();
  check(rastro[rastro.length - 1].accion === 'consulta_eventos', 'y consultar eventos deja su propio rastro');
  check(auditor.eventos(new Date('2020-01-01'), new Date('2020-12-31')).length === 0, 'nada fuera del rango');
  const evCsv = auditor.exportarEventosCsv(ayer, mañana);
  check(evCsv.startsWith('seq;fecha;quien;desde;accion'), 'el CSV de eventos sale con sus columnas');
  const integridadEv = auditor.comprobarIntegridadEventos();
  check(integridadEv.integro, 'la cadena de eventos se comprueba integra por el mismo');

  console.log('\n== 9. El archivo no le mete una formula a quien lo abra ==');
  // El nombre del cliente lo escribe el comensal: es texto de afuera. Excel
  // ejecuta toda celda que empiece por = + - @, y ese archivo ahora lo abre
  // tambien el contador del negocio.
  check(celdaCsv('=1+1') === "'=1+1", 'un nombre que empieza por = sale como texto');
  check(celdaCsv('@SUM(A1)') === "'@SUM(A1)", 'lo mismo con @');
  check(celdaCsv('-5') === "'-5", 'y con el guion, que es el truco viejo');
  check(celdaCsv('+A1') === "'+A1", 'y con el mas');
  check(celdaCsv(5.5) === '5.5', 'pero un numero de verdad sale intacto');
  check(celdaCsv('Arepa Power') === 'Arepa Power', 'y un nombre normal no se toca');
  check(celdaCsv('=A1;B2') === `"'=A1;B2"`, 'si ademas trae punto y coma, va entrecomillado');
  check(celdaCsv(null) === '', 'lo vacio queda vacio');
} catch (e) {
  console.log('  XX  ' + e.message);
  fallos++;
} finally {
  rmSync(dir, { recursive: true, force: true });
}

console.log(fallos === 0 ? '\nBLOQUE 6 (PUERTA DEL FISCAL): 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
