// PRUEBA DE LOS BLOQUES 4 Y 5: LA FACTURA QUE VE EL CLIENTE (2026-07-28)
// Se prueba el papel que sale por la termica comun del negocio y la version
// para el telefono. Lo mas importante: que NINGUN byte pase de 127 (las POS-80
// chinas se comen la letra siguiente y salen letras chinas, leccion cara del
// Cerebrito) y que el sello de comprobacion vaya impreso.
import { mkdtempSync, rmSync } from 'node:fs';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
import { LibroFiscal } from '../dist/libro.js';
import { armarDocumento, armarNotaCredito } from '../dist/armador.js';
import { facturaTermica, facturaParaTelefono, lineasFactura, aAscii } from '../dist/factura.js';

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

const EMISOR = {
  rif: 'J-50856106-6',
  razon_social: 'Arepa Power Restaurant',
  domicilio_fiscal: 'Av. Principal, Araure, Portuguesa',
  precios_con_iva: true,
  alicuota_general: 16,
};
const SELLO = {
  numeroControl: '00-00001234',
  urlSello: 'https://sello.gustitoxpress.app/v/0be5b2ddd7082cbb3ae0bb7f7aac346b',
  // Lo que la imprenta responde y el art. 7 de la 000102 obliga a imprimir.
  fechaAsignacion: '2026-07-31T02:15:44.000Z',
  rango: { desde: '1', hasta: '50000' },
  imprentaFiscal: {
    rif: 'J-11222333-4',
    razon_social: 'Imprenta Digital de Prueba C.A.',
    providencia: 'SNAT/2026/000999',
    providencia_fecha: '2026-01-15',
  },
};

try {
  const libro = new LibroFiscal({ ruta: join(dir, 'libro.jsonl'), serie: 'A', equipo: 'FISCAL-01' });
  const asiento = libro.asentar(
    'factura',
    armarDocumento(
      {
        origen_id: 'v-1',
        canal: 'mesa',
        cliente: { nombre: 'María Rodríguez Peña', cedula: 'V-22606827' },
        renglones: [
          { nombre: 'Arepa Dominó', cantidad: 2, precio_usd: 5, alicuota: 16 },
          { nombre: 'Jugo de parchita', cantidad: 1, precio_usd: 2, alicuota: 0 },
        ],
        tasa_bs: 742.23,
        pago: { metodo: 'pago_movil', referencia: '4821' },
      },
      EMISOR,
    ),
  );

  console.log('\n== 1. Ni un byte por encima de 127 (adios letras chinas) ==');
  const bytes = facturaTermica(asiento, SELLO);
  const altos = [...bytes].filter((b) => b > 127);
  check(altos.length === 0, `cero bytes altos en toda la factura (${altos.length})`);
  check(aAscii('Arepa Dominó con Guayanés y parchita') === 'Arepa Domino con Guayanes y parchita', 'los acentos se cambian, no se rompen');

  console.log('\n== 2. El papel lleva lo que la ley pide ==');
  const texto = lineasFactura(asiento, SELLO).join('\n');
  check(texto.includes('Arepa Power Restaurant'), 'quien emite');
  // Se comprueba EL DATO, no como esta redactado: el molde del papel se puede
  // reordenar (y se reordeno), pero ninguno de los 15 datos del art. 7 puede
  // desaparecer.
  check(/RIF:? J-50856106-6/.test(texto), 'con su RIF');
  check(texto.includes('Av. Principal, Araure'), 'y su domicilio fiscal');
  check(texto.includes('FACTURA'), 'dice que tipo de documento es');
  check(/\bA-1\b/.test(texto), 'su numero');
  check(/N\. de Control\s+00-00001234/.test(texto), 'y el numero de control que dio la imprenta (frase del art. 30)');
  check(/\d{2}\/\d{2}\/\d{4}\s+\d{2}:\d{2}:\d{2}/.test(texto), 'la fecha y hora de emision con SEGUNDOS (art. 7 num. 6)');
  check(texto.includes('Maria Rodriguez'), 'el cliente (sin acentos, pero legible)');
  check(texto.includes('V-22606827'), 'con su cedula');

  console.log('\n== 2b. Lo que solo sabe la imprenta (art. 7 nums. 5, 14 y 15) ==');
  check(/Control asignado el (31\/07\/2026|30\/07\/2026)/.test(texto), 'cuando se asigno el numero de control (num. 15; el dia depende de la zona horaria del equipo)');
  check(texto.includes('Controles asignados: del N. 1 al N. 50000'), 'el rango autorizado del que salio (num. 5)');
  check(texto.includes('Imprenta digital autorizada:') && texto.includes('Imprenta Digital de Prueba C.A.'), 'la razon social de la imprenta (num. 14)');
  check(/RIF:? J-11222333-4/.test(texto), 'con su RIF');
  check(texto.includes('Providencia SNAT/2026/000999 del 15/01/2026'), 'y su providencia de autorizacion con fecha');
  const textoPelado = lineasFactura(asiento, { numeroControl: '00-00001234', urlSello: SELLO.urlSello }).join('\n');
  check(!textoPelado.includes('Imprenta digital autorizada:') && !textoPelado.includes('Controles asignados:') && !textoPelado.includes('Fecha de asignacion:'), 'si la imprenta no manda esos datos, las lineas no salen (compatibilidad con puertas viejas)');

  console.log('\n== 3. El desglose del impuesto, renglon por renglon ==');
  check(/Base imponible 16%/.test(texto), 'separa la base gravada');
  check(/IVA 16%/.test(texto), 'y el impuesto');
  check(/Exento/.test(texto), 'y lo exento va aparte');
  check(/TOTAL Bs/.test(texto), 'con el total en bolivares');
  check(/Tasa del dia/.test(texto) && /USD 12[.,]00/.test(texto), 'y la tasa del dia con su equivalente en dolares');
  // El metodo de pago sale en cristiano ("Pago Movil"), no como lo guarda la
  // base ("pago_movil"): el papel lo lee un cliente, no un programador.
  check(/Pago Movil/.test(texto) && texto.includes('4821'), 'y como pago, con su referencia');

  console.log('\n== 4. El sello de comprobacion va en el papel ==');
  // La direccion escrita se quito a proposito (decision de Gilberto viendo el
  // papel): eran dos lineas de codigo ilegible que nadie iba a teclear. La
  // comprobacion se sostiene con el QR, que va aqui abajo. Si algun dia se
  // quiere devolver, se hace con partirUrl() que ya la corta al ancho del papel.
  check(texto.includes('Comprueba tu factura:'), 'el papel invita a comprobar la factura');
  // Comando ESC/POS del QR: GS ( k ... 31 50 30 (guardar contenido)
  const b = [...bytes];
  const hayQr = b.some((x, i) => x === 0x1d && b[i + 1] === 0x28 && b[i + 2] === 0x6b && b[i + 5] === 0x31 && b[i + 6] === 0x50);
  check(hayQr, 'y el codigo QR va como comando nativo de la impresora');
  check(bytes[bytes.length - 3] === 0x1d && bytes[bytes.length - 2] === 0x56, 'el papel se corta al final');
  const hayFuenteA = b.some((x, i) => x === 0x1b && b[i + 1] === 0x4d && b[i + 2] === 0x00);
  check(hayFuenteA, 'la fuente A va por comando explicito: 8,5 puntos, sobre el minimo del art. 31');

  console.log('\n== 5. Dice lo que es: representacion grafica ==');
  const plano = Buffer.from(bytes).toString('ascii');
  check(/Representacion grafica del documento digital/.test(plano), 'el papel aclara que el documento con valor es el digital');

  console.log('\n== 6. La version para el telefono ==');
  const tel = facturaParaTelefono(asiento, SELLO);
  check(tel.emisor.rif === 'J-50856106-6' && tel.documento.numero_control === '00-00001234', 'lleva emisor y numero de control');
  check(tel.renglones.length === 2, 'con sus renglones');
  check(Math.abs(tel.totales.base_bs + tel.totales.iva_bs - tel.totales.total_bs) < 0.02, 'base mas IVA cuadra con el total en bolivares');
  check(tel.totales.total_usd === 12, `y tambien el total en dolares (${tel.totales.total_usd})`);
  check(tel.sello.startsWith('https://sello.gustitoxpress.app/'), 'y el enlace del sello para tocarlo desde el telefono');
  check(tel.cliente.nombre === 'María Rodríguez Peña', 'en el telefono si van los acentos completos');
  check(tel.documento.fecha_asignacion === '2026-07-31T02:15:44.000Z', 'la fecha de asignacion del control viaja al telefono (num. 15)');
  check(tel.documento.rango?.desde === '1' && tel.documento.rango?.hasta === '50000', 'el rango tambien (num. 5)');
  check(tel.imprenta?.rif === 'J-11222333-4' && tel.imprenta?.providencia === 'SNAT/2026/000999', 'y la identidad de la imprenta con su providencia (num. 14)');

  console.log('\n== 7. Una nota de credito se ve como lo que es ==');
  const nc = libro.corregir({ tipo: 'factura', numero: 1 }, armarNotaCredito(asiento.documento, 'Cobro de mas'));
  const textoNc = lineasFactura(nc, { ...SELLO, numeroControl: '00-00001235' }).join('\n');
  check(textoNc.includes('NOTA DE CREDITO'), 'se titula nota de credito');
  check(textoNc.includes('Corrige: A-1'), 'y dice cual documento corrige');

  console.log('\n== 8. El modelo del negocio se SUMA, jamas desplaza el art. 7 ==');
  const MODELO = {
    mensaje_pie: 'Gracias por su compra. Síguenos en @arepapower',
    logo_url: 'https://img.gustitoxpress.app/logos/ap.png',
    color: '#C9A227',
  };
  const conModelo = lineasFactura(asiento, SELLO, MODELO);
  const sinModelo = lineasFactura(asiento, SELLO);
  check(conModelo.join('\n').includes('Gracias por su compra. Siguenos en @arepapower'), 'el mensaje del pie sale en el papel, convertido a ASCII');
  check(sinModelo.every((linea) => conModelo.includes(linea)), 'TODAS las lineas obligatorias siguen intactas con el modelo puesto');
  const bytesModelo = facturaTermica(asiento, SELLO, MODELO);
  check([...bytesModelo].filter((x) => x > 127).length === 0, 'ni un byte alto aunque el mensaje traiga acentos');
  const lineasLargo = lineasFactura(asiento, SELLO, { mensaje_pie: 'palabra '.repeat(60) }).filter((l) => l.trim().startsWith('palabra'));
  check(lineasLargo.length <= 4, `un mensaje larguisimo se corta en cuatro lineas (${lineasLargo.length})`);
  const telModelo = facturaParaTelefono(asiento, SELLO, MODELO);
  check(telModelo.marca?.logo_url === MODELO.logo_url && telModelo.marca?.color === '#C9A227', 'el telefono recibe logo y color para la version digital');
  check(telModelo.marca?.mensaje_pie === MODELO.mensaje_pie, 'y el mensaje con sus acentos completos');
  check(facturaParaTelefono(asiento, SELLO).marca === null, 'sin modelo configurado, la marca va vacia');
} catch (e) {
  console.log('  XX  ' + e.message);
  fallos++;
} finally {
  rmSync(dir, { recursive: true, force: true });
}

console.log(fallos === 0 ? '\nBLOQUES 4 Y 5 (FACTURA DEL CLIENTE): 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
