// PRUEBA DE ROBUSTEZ OFFLINE del cerebrito, contra la BD VIVA (demo):
//  1) Un pedido confirmado AHORA pero recibido hace 8 horas (el caso "se cayo
//     el internet toda la tarde"): el barrido lo tiene que rescatar igual
//     (filtro por confirmado_en, no por recibido_en).
//  2) La impresora esta APAGADA cuando llega la comanda: el papel NO se pierde,
//     espera en cola y sale solo cuando la impresora prende.
//  3) Un papel que se vencio (impresora apagada demasiado rato) se recupera
//     con "reintentar fallidas".
// Env: SUPABASE_URL, SUPABASE_ANON_KEY, SUPABASE_SERVICE_KEY.
import { createClient } from '@supabase/supabase-js';
import { mkdtempSync, rmSync } from 'node:fs';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
import { crearImpresoraVirtual } from './impresora_virtual.mjs';
import { iniciarCerebrito } from '../dist/motor.js';
import { ColaImpresion } from '../dist/impresoras.js';
import { comandaABytes } from '../dist/escpos.js';

const URL = process.env.SUPABASE_URL || process.env.VITE_SUPABASE_URL;
const ANON = process.env.SUPABASE_ANON_KEY || process.env.VITE_SUPABASE_ANON_KEY;
const SERVICE = process.env.SUPABASE_SERVICE_KEY;
if (!URL || !ANON || !SERVICE) {
  console.log('Faltan SUPABASE_URL / SUPABASE_ANON_KEY / SUPABASE_SERVICE_KEY');
  process.exit(1);
}

let fallos = 0;
const check = (c, m) => {
  console.log((c ? '  OK  ' : '  XX  ') + m);
  if (!c) fallos++;
};
const espera = (ms) => new Promise((r) => setTimeout(r, ms));
const hasta = async (fn, ms, paso = 500) => {
  const tope = Date.now() + ms;
  while (Date.now() < tope) {
    if (fn()) return true;
    await espera(paso);
  }
  return fn();
};
const plano = (t) => t.replace(/\s+/g, ' ');

const svc = createClient(URL, SERVICE, { auth: { persistSession: false } });
const { data: rest } = await svc.from('restaurantes').select('id').eq('slug', 'demo-gustito').maybeSingle();
const REST = rest.id;

const EMAIL = 'offline.test@gustitoxpress.app';
const CLAVE = 'OfflineTest2026!';
const marca = `GD-OF${String(Date.now()).slice(-5)}`;
let uid;
let pedidoId;
let motor;
let imp;
let imp2;
const carpeta = mkdtempSync(join(tmpdir(), 'cerebrito-off-'));

const limpiar = async () => {
  try {
    motor?.detener();
  } catch {
    /* ya */
  }
  await imp?.cerrar();
  await imp2?.cerrar();
  if (pedidoId) await svc.from('pedidos').delete().eq('id', pedidoId);
  if (uid) await svc.auth.admin.deleteUser(uid).catch(() => {});
  rmSync(carpeta, { recursive: true, force: true });
};

try {
  // Puestos demo (idempotente) + producto de comida.
  const { data: puestosDemo } = await svc
    .from('puestos_impresion')
    .upsert(
      [
        { restaurante_id: REST, nombre: 'Cocina', orden: 1, es_defecto: true },
        { restaurante_id: REST, nombre: 'Barra', orden: 2, es_defecto: false },
      ],
      { onConflict: 'restaurante_id,nombre' },
    )
    .select('id,nombre');
  check((puestosDemo ?? []).length >= 2, 'puestos del demo listos');
  const { data: prods } = await svc
    .from('productos')
    .select('id,nombre,categorias(nombre)')
    .eq('restaurante_id', REST)
    .eq('disponible', true);
  const comida = (prods ?? []).find((p) => !/bebida/i.test(p.categorias?.nombre ?? ''));

  // Usuario encargado temporal.
  const { data: lista } = await svc.auth.admin.listUsers();
  for (const u of lista?.users ?? []) if (u.email === EMAIL) await svc.auth.admin.deleteUser(u.id);
  const { data: u } = await svc.auth.admin.createUser({ email: EMAIL, password: CLAVE, email_confirm: true });
  uid = u.user.id;
  await svc.from('perfiles').upsert({ id: uid, rol: 'encargado', restaurante_id: REST, nombre: 'Offline Test' });

  // (1) Pedido "de la caida larga": recibido hace 8 HORAS, confirmado AHORA,
  // creado ANTES de arrancar el motor (el canal en vivo no lo va a avisar).
  const { data: pv, error: ePv } = await svc
    .from('pedidos')
    .insert({
      restaurante_id: REST,
      codigo: marca,
      cliente_nombre: 'Cliente Caida Larga',
      cliente_telefono: '04140000007',
      tipo_entrega: 'pickup',
      estado: 'confirmado',
      metodo_pago: 'efectivo',
      subtotal_usd: 6,
      total_usd: 6,
      recibido_en: new Date(Date.now() - 8 * 3600_000).toISOString(),
      confirmado_en: new Date().toISOString(),
    })
    .select('id')
    .maybeSingle();
  if (ePv) throw new Error('insert pedido: ' + ePv.message);
  pedidoId = pv.id;
  await svc.from('pedido_items').insert({
    pedido_id: pedidoId,
    producto_id: comida.id,
    nombre: comida.nombre,
    precio_usd: 6,
    cantidad: 1,
  });

  // (2) Motor con la impresora APAGADA (nadie escucha el puerto 9420 todavia).
  motor = await iniciarCerebrito(
    {
      supabaseUrl: URL,
      anonKey: ANON,
      correo: EMAIL,
      clave: CLAVE,
      impresoras: [{ puesto: 'Cocina', tipo: 'tcp', host: '127.0.0.1', puerto: 9420 }],
      carpetaEstado: carpeta,
    },
    { log: (m) => console.log('   [motor] ' + m) },
  );

  // El barrido (cada 20s) tiene que rescatar el pedido viejo-recibido.
  const rescatado = await hasta(() => motor.estado().ultimas.includes(marca), 40_000);
  check(rescatado, 'el barrido RESCATO el pedido confirmado en la caida (recibido hace 8h)');
  check(motor.estado().cola.pendientes >= 1, 'con la impresora apagada el papel espera en cola (no se pierde)');

  // (3) Prende la impresora: el papel tiene que salir solo.
  imp = await crearImpresoraVirtual(9420);
  const salio = await hasta(() => imp.tickets.length >= 1, 60_000);
  check(salio, 'al prender la impresora, la comanda salio sola (paciencia de la cola)');
  check(plano(imp.tickets.join(' ')).includes(`#${marca}`), 'y es la comanda correcta: #' + marca);
  check(motor.estado().cola.pendientes === 0, 'la cola quedo limpia');

  // (4) Papeles VENCIDOS: cola aparte con poca paciencia a proposito.
  const logsCola = [];
  const cola = new ColaImpresion(
    [{ puesto: 'Prueba', tipo: 'tcp', host: '127.0.0.1', puerto: 9421 }],
    (m) => logsCola.push(m),
    2, // solo 2 intentos: se vence rapido para probar el rescate
  );
  cola.encolar('Prueba', comandaABytes([{ texto: 'PAPEL VENCIDO', grande: true }]), 'papel-vencido');
  const vencio = await hasta(() => cola.estado().fallidas === 1, 30_000);
  check(vencio, 'un papel sin impresora demasiado rato se marca como fallido (no desaparece)');
  imp2 = await crearImpresoraVirtual(9421);
  const n = cola.reintentarFallidas();
  check(n === 1, '"reintentar fallidas" lo reencola');
  const rescatado2 = await hasta(() => imp2.tickets.join('').includes('PAPEL VENCIDO'), 30_000);
  check(rescatado2, 'y sale impreso al reintentar');
  cola.detener();
} catch (e) {
  console.log('  XX  Excepcion: ' + e.message);
  fallos++;
} finally {
  await limpiar();
  console.log('\nLimpieza hecha');
}
console.log(fallos === 0 ? '\nROBUSTEZ OFFLINE OK' : `\nOFFLINE: ${fallos} FALLO(S)`);
process.exit(fallos === 0 ? 0 : 1);
