// PRUEBA DEL BLOQUE 3: LA COLA DE TRANSMISION (2026-07-28)
// Lo que se prueba es el dia malo, no el bueno: que se caiga el internet a
// mitad del servicio, que la imprenta rechace un documento, que se apague la
// laptop con cosas sin transmitir. En todos esos casos el negocio tiene que
// poder seguir vendiendo y nada se puede perder ni duplicar.
import { mkdtempSync, rmSync } from 'node:fs';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
import { LibroFiscal } from '../dist/libro.js';
import { armarDocumento } from '../dist/armador.js';
import { ColaTransmision } from '../dist/cola.js';

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

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

/** Imprenta de mentira que se puede tumbar y levantar a voluntad. */
class ImprentaFalsa {
  constructor() {
    this.real = true;
    this.nombre = 'Falsa';
    this.caida = false;
    this.rechaza = new Set();
    this.recibidos = [];
  }
  async emitir(asiento) {
    if (this.caida) throw new Error('conexion perdida con la imprenta');
    if (this.rechaza.has(asiento.numero)) throw new Error('La imprenta rechazo el documento: datos invalidos');
    this.recibidos.push(asiento.seq);
    return {
      numeroControl: `00-${String(asiento.control).padStart(8, '0')}`,
      numeroDocumento: String(asiento.numero),
      fechaAsignacion: new Date().toISOString(),
    };
  }
}

try {
  const libro = new LibroFiscal({ ruta: RUTA_LIBRO, serie: 'A', equipo: 'FISCAL-01' });
  const imprenta = new ImprentaFalsa();
  const cola = new ColaTransmision({ ruta: RUTA_COLA, imprenta });

  console.log('\n== 1. Con internet: el documento sale de una ==');
  const a1 = libro.asentar('factura', armarDocumento(venta('v1'), EMISOR));
  const r1 = await cola.encolar(a1);
  check(r1.estado === 'enviado', `queda enviado (${r1.estado})`);
  check(r1.numeroControl === '00-00000001', `con su numero de control de la imprenta (${r1.numeroControl})`);
  check(cola.cuantosPendientes() === 0, 'sin nada pendiente');

  console.log('\n== 2. Se cae el internet: el negocio SIGUE vendiendo ==');
  imprenta.caida = true;
  const a2 = libro.asentar('factura', armarDocumento(venta('v2'), EMISOR));
  const a3 = libro.asentar('factura', armarDocumento(venta('v3'), EMISOR));
  const r2 = await cola.encolar(a2);
  const r3 = await cola.encolar(a3);
  check(r2.estado === 'pendiente' && r3.estado === 'pendiente', 'los dos quedan pendientes, sin explotar');
  check(a2.numero === 2 && a3.numero === 3, 'las ventas se sellaron igual, con su numeracion corrida');
  check(cola.cuantosPendientes() === 2, `quedan 2 esperando (${cola.cuantosPendientes()})`);
  check(libro.verificar().integro, 'y el libro sigue sano');

  console.log('\n== 3. Vuelve el internet: sale todo, en orden ==');
  imprenta.caida = false;
  const res = await cola.procesar();
  check(res.enviados === 2, `salieron los 2 que faltaban (${res.enviados})`);
  check(imprenta.recibidos.join(',') === '1,2,3', `y en el orden en que se vendieron (${imprenta.recibidos.join(',')})`);
  check(cola.cuantosPendientes() === 0, 'la cola queda limpia');

  console.log('\n== 4. Nada se transmite dos veces ==');
  const repetido = await cola.encolar(a2);
  check(repetido.estado === 'enviado' && imprenta.recibidos.length === 3, 'reencolar algo ya enviado no lo manda de nuevo');

  console.log('\n== 5. Si la imprenta RECHAZA, no se reintenta al infinito ==');
  const a4 = libro.asentar('factura', armarDocumento(venta('v4'), EMISOR));
  imprenta.rechaza.add(a4.numero);
  const r4 = await cola.encolar(a4);
  check(r4.estado === 'rechazado', `queda marcado como rechazado (${r4.estado})`);
  check(cola.rechazados().length === 1, 'y aparece en la lista de lo que hay que revisar');
  check(cola.cuantosPendientes() === 0, 'sin trancar la fila de los demas');
  const a5 = libro.asentar('factura', armarDocumento(venta('v5'), EMISOR));
  const r5 = await cola.encolar(a5);
  check(r5.estado === 'enviado', 'el siguiente documento sale normal');
  check(libro.buscar('factura', a4.numero) !== null, 'el documento rechazado NO se borra del libro');

  console.log('\n== 6. Se apaga la laptop con cosas sin transmitir ==');
  imprenta.caida = true;
  const a6 = libro.asentar('factura', armarDocumento(venta('v6'), EMISOR));
  await cola.encolar(a6);
  check(cola.cuantosPendientes() === 1, 'queda 1 sin salir');
  // Apagon: se arranca todo de nuevo desde el disco
  const libro2 = new LibroFiscal({ ruta: RUTA_LIBRO, serie: 'A', equipo: 'FISCAL-01' });
  const imprenta2 = new ImprentaFalsa();
  const cola2 = new ColaTransmision({ ruta: RUTA_COLA, imprenta: imprenta2 });
  const recuperados = cola2.recuperar(libro2.todos());
  check(recuperados === 1, `al reiniciar, recupera el que falto (${recuperados})`);
  check(cola2.ver(a1.seq)?.estado === 'enviado', 'y NO reenvia los que ya habian salido');
  const res2 = await cola2.procesar();
  check(res2.enviados === 1 && imprenta2.recibidos.join(',') === String(a6.seq), 'lo transmite apenas hay internet');
  check(cola2.ver(a6.seq)?.estado === 'enviado', 'y queda enviado');

  console.log('\n== 7. El estado de envio NO toca el libro ==');
  check(libro2.verificar().integro, 'el libro sigue integro despues de todos los envios');
  const asientoLibro = libro2.buscar('factura', 1);
  check(asientoLibro.hash === a1.hash, 'el asiento sellado quedo exactamente igual que el primer dia');
} catch (e) {
  console.log('  XX  ' + e.message);
  fallos++;
} finally {
  rmSync(dir, { recursive: true, force: true });
}

console.log(fallos === 0 ? '\nBLOQUE 3 (COLA): 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
