// EL CEREBRITO LE ENTREGA LA VENTA A LA ESTACION FISCAL, DIRECTO (2026-07-29)
//
// Prueba de integracion COMPLETA y 100% local (sin nube, sin puertos del
// cerebrito real): se levanta una Estacion Fiscal DE VERDAD (el servidor de
// apps/fiscal, imprenta de practica) en un puerto de prueba, y un salon del
// cerebrito con su impresora virtual. Lo que se comprueba:
//
//   1. El apreton de manos: la llave de maquina dice a que negocio le hablas.
//   2. Venta directa de caja -> se sella sola y el numero queda anotado en la
//      venta del buzon (persistido en disco).
//   3. Mesa cobrada -> UN documento por cuenta (igual que la via de la nube).
//   4. Convergencia de las dos vias: la misma venta llegando "por la nube"
//      (misma forma, mismo origen_id) recibe el documento QUE YA EXISTE.
//   5. Estacion caida: el cobro NO se cae, la venta queda en cola local, y al
//      volver la Estacion se sella sola en la siguiente ronda.
//   6. Un reinicio del cerebrito no pierde los sellos (persistencia atomica).
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
import { crearImpresoraVirtual } from './impresora_virtual.mjs';
import { ColaImpresion } from '../dist/impresoras.js';
import { crearSalon } from '../dist/salon.js';
import { crearPuenteFiscal } from '../dist/puenteFiscal.js';
import { cargarEspejo } from '../dist/espejo.js';
import { iniciarServidor } from '../../fiscal/dist/servidor.js';
import { Acceso } from '../../fiscal/dist/acceso.js';

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

const PUERTO_FISCAL = 8753; // de prueba: no pisa 8737/8738 (cerebrito real) ni 8739 (estacion real)
const baseFiscal = `http://127.0.0.1:${PUERTO_FISCAL}`;
const dirFiscal = mkdtempSync(join(tmpdir(), 'fiscal-int-'));
const dirCer = mkdtempSync(join(tmpdir(), 'cerebrito-int-'));

// ---- La Estacion Fiscal de verdad, con su portero y su llave de maquina ----
const CLAVE_DUENO = 'clave-del-dueno-2026';
const acceso = new Acceso(join(dirFiscal, 'estado', 'acceso.json'));
acceso.fijarClave('dueno', CLAVE_DUENO);
const LLAVE = acceso.llaveMaquina();

let galleta = '';
async function configurarEstacion() {
  const e = await fetch(`${baseFiscal}/entrar`, {
    method: 'POST',
    headers: { 'content-type': 'application/x-www-form-urlencoded' },
    body: new URLSearchParams({ rol: 'dueno', clave: CLAVE_DUENO }).toString(),
    redirect: 'manual',
  });
  galleta = (e.headers.get('set-cookie') ?? '').split(';')[0];
  const r = await fetch(`${baseFiscal}/configurar`, {
    method: 'POST',
    headers: { cookie: galleta, 'content-type': 'application/x-www-form-urlencoded' },
    body: new URLSearchParams({
      rif: 'J-50856106-6',
      razon_social: 'Arepa Power Restaurant',
      domicilio_fiscal: 'Araure, Portuguesa',
      alicuota_general: '16',
      serie: 'A',
      equipo: 'INT-01',
    }).toString(),
    redirect: 'manual',
  });
  return r.status;
}

// ---- El salon del cerebrito, con espejo local e impresora virtual ----
const espejoFalso = {
  restaurante: { id: 'r-prueba', nombre: 'Arepa Power', prefijo: 'AP', num_mesas: 3, tasa_bs: 742.23 },
  puestos: [{ id: 'p1', nombre: 'Cocina', orden: 1, es_defecto: true, activo: true }],
  categorias: [{ id: 'c1', nombre: 'Arepas', orden: 1, a_cocina: true, puesto_id: null }],
  productos: [
    { id: 'prod-arepa', nombre: 'Arepa', precio_usd: 5, categoria_id: 'c1', puesto_id: null, disponible: true, opciones: null },
    { id: 'prod-refresco', nombre: 'Refresco', precio_usd: 2, categoria_id: 'c1', puesto_id: null, disponible: true, opciones: null },
  ],
  actualizado_en: new Date().toISOString(),
};
mkdirSync(join(dirCer, 'local'), { recursive: true });
writeFileSync(join(dirCer, 'local', 'espejo.json'), JSON.stringify(espejoFalso));

const log = () => {};
let servidor;
let impresora;
let puente;

try {
  servidor = await iniciarServidor({ base: dirFiscal, puerto: PUERTO_FISCAL, log: () => {} });
  check((await configurarEstacion()) === 302, 'la Estacion de prueba queda configurada');

  impresora = await crearImpresoraVirtual(9109);
  const cola = new ColaImpresion([{ puesto: 'Cocina', tipo: 'tcp', host: '127.0.0.1', puerto: 9109 }], log);
  const config = {
    supabaseUrl: '',
    anonKey: '',
    correo: '',
    clave: '',
    impresoras: [{ puesto: 'Cocina', tipo: 'tcp', host: '127.0.0.1', puerto: 9109 }],
    estacionFiscal: { url: baseFiscal, llave: LLAVE },
  };
  puente = crearPuenteFiscal({ config: () => config, salon: () => salon, log });
  let salon = crearSalon({
    base: dirCer,
    espejo: () => cargarEspejo(dirCer),
    cola,
    log,
    hayDespacho: false,
    alVentaCerrada: () => puente.disparar(),
  });

  console.log('\n== 1. El apreton de manos ==');
  const mano = await puente.probar();
  check(mano.ok === true, 'la llave sirve');
  check(mano.rif === 'J-50856106-6', `y dice a que negocio le habla (${mano.rif})`);

  console.log('\n== 2. Venta directa de caja -> sellada sola ==');
  salon.ventaDirecta({ items: [{ producto_id: 'prod-arepa', cantidad: 2 }, { producto_id: 'prod-refresco', cantidad: 1 }], metodo: 'efectivo' });
  await puente.sellarAhora();
  const v1 = salon.ventasPendientes()[0];
  check(!!v1.fiscal, 'la venta del buzon tiene su sello');
  check(v1.fiscal?.serie === 'A' && v1.fiscal?.numero === 1, `documento ${v1.fiscal?.serie}-${v1.fiscal?.numero}`);
  check(!!v1.fiscal?.control, `con numero de control (${v1.fiscal?.control})`);
  check(v1.pedidos[0].total_usd === 12, `y el total viaja explicito ($${v1.pedidos[0].total_usd})`);

  console.log('\n== 3. Mesa cobrada -> UN documento por cuenta ==');
  salon.tomarPedidoMesa({ mesa: '2', items: [{ producto_id: 'prod-arepa', cantidad: 1 }], mesero: 'Mariangel' });
  salon.tomarPedidoMesa({ mesa: '2', items: [{ producto_id: 'prod-refresco', cantidad: 2 }] });
  salon.cobrarMesa({ mesa: '2', metodo: 'pago_movil', referencia: '1234', propina_usd: 1 });
  await puente.sellarAhora();
  const v2 = salon.ventasPendientes().find((v) => v.cuenta);
  check(v2?.fiscal?.numero === 2, `la cuenta completa es el documento A-${v2?.fiscal?.numero}`);

  console.log('\n== 4. La misma venta "llegando por la nube" NO duplica ==');
  // La cola de la nube manda la MISMA forma con el MISMO origen_id (los uuid
  // viajan identicos porque sincronizar_venta_local inserta con ellos).
  const p1 = v1.pedidos[0];
  const r = await (await fetch(`${baseFiscal}/api/venta`, {
    method: 'POST',
    headers: { authorization: `Bearer ${LLAVE}`, 'content-type': 'application/json' },
    body: JSON.stringify({
      tipo: 'pedido',
      origen_id: p1.id,
      venta: {
        codigo: p1.codigo, fecha: p1.entregado_en, tipo_entrega: 'pickup', mesa: null,
        metodo_pago: 'efectivo', origen: 'caja', tasa_bs: p1.tasa_bs, total_usd: p1.total_usd,
        costo_delivery_usd: 0,
        comprador: { nombre: null, rif: null, razon_social: null, direccion: null, correo: null, telefono: null },
        renglones: p1.items.map((it) => ({ nombre: it.nombre, cantidad: it.cantidad, precio_usd: it.precio_usd })),
      },
    }),
  })).json();
  check(r.sellada === true && r.numero === 1, `la nube recibe el documento que YA existia (A-${r.numero})`);

  console.log('\n== 5. Estacion caida: el cobro no se cae y al volver se sella ==');
  // Se simula la caida apuntando el puente a un puerto muerto (mas determinista
  // que cerrar el servidor: el pool de conexiones de fetch mantiene sockets
  // vivos hacia el origen viejo y el cierre no corta lo ya conectado).
  config.estacionFiscal = { url: 'http://127.0.0.1:8754', llave: LLAVE };
  salon.ventaDirecta({ items: [{ producto_id: 'prod-arepa', cantidad: 1 }], metodo: 'efectivo' });
  await puente.sellarAhora();
  const v3 = salon.ventasPendientes().find((v) => !v.fiscal);
  check(!!v3, 'la venta queda en cola local, sin sellar');
  check(puente.estado().conectada === false, 'y el puente sabe que la Estacion no responde');
  config.estacionFiscal = { url: baseFiscal, llave: LLAVE };
  await puente.sellarAhora();
  const v3b = salon.ventasPendientes().find((v) => v.id === v3.id);
  check(v3b?.fiscal?.numero === 3, `al volver la Estacion, se sella sola (A-${v3b?.fiscal?.numero})`);
  check(puente.estado().conectada === true, 'y el puente vuelve a verla conectada');

  console.log('\n== 6. Un reinicio del cerebrito no pierde los sellos ==');
  const salon2 = crearSalon({ base: dirCer, espejo: () => cargarEspejo(dirCer), cola, log, hayDespacho: false });
  const selladas = salon2.ventasPendientes().filter((v) => v.fiscal).length;
  check(selladas === 3, `las ${selladas} ventas selladas siguen selladas tras "reiniciar"`);
  check(salon2.ventasSinSellar().length === 0, 'y no queda nada por sellar');

  console.log(fallos === 0 ? '\nTODO EN VERDE' : `\n${fallos} FALLOS`);
} catch (e) {
  console.error('\nREVENTO:', e);
  fallos++;
} finally {
  try { puente?.detener(); } catch { /* ya */ }
  try { servidor?.cerrar(); } catch { /* ya */ }
  try { await impresora?.cerrar(); } catch { /* ya */ }
  rmSync(dirFiscal, { recursive: true, force: true });
  rmSync(dirCer, { recursive: true, force: true });
}
process.exit(fallos ? 1 : 0);
