// PRUEBA del ASISTENTE del cerebrito contra la BD VIVA (demo): levanta el
// programa sin configuracion en una carpeta temporal, recorre el flujo del
// instalador por su API (entrar -> probar impresora -> guardar -> estado ->
// reserva/mando -> reimprimir) y limpia todo. No toca registro ni escritorio.
// Env: SUPABASE_URL, SUPABASE_ANON_KEY, SUPABASE_SERVICE_KEY.
import { createClient } from '@supabase/supabase-js';
import { spawn } from 'node:child_process';
import { existsSync, mkdtempSync, rmSync } from 'node:fs';
import { tmpdir } from 'node:os';
import { dirname, join } from 'node:path';
import { fileURLToPath } from 'node:url';
import { crearImpresoraVirtual } from './impresora_virtual.mjs';
import { exigirPuertosLibres } from './puerto_libre.mjs';

// Si hay un cerebrito real corriendo en esta maquina, NO seguir: la suite
// terminaria mandandole entrar/guardar/reserva al de produccion.
await exigirPuertosLibres([8737, 8738]);

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);
}

const AQUI = dirname(fileURLToPath(import.meta.url));
const BASE_APP = join(AQUI, '..');
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 = 400) => {
  const tope = Date.now() + ms;
  while (Date.now() < tope) {
    if (await fn()) return true;
    await espera(paso);
  }
  return fn();
};
const api = async (ruta, cuerpo) => {
  const r = await fetch('http://127.0.0.1:8737/' + ruta, cuerpo ? { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(cuerpo) } : undefined);
  return { status: r.status, json: await r.json().catch(() => null), texto: null };
};

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 = 'asistente.test@gustitoxpress.app';
const CLAVE = 'AsistenteTest2026!';
let uid;
let hijo;
let impA;
let impB;
const carpeta = mkdtempSync(join(tmpdir(), 'cerebrito-asist-'));

const limpiar = async () => {
  try {
    hijo?.kill();
  } catch {
    /* ya murio */
  }
  await impA?.cerrar();
  await impB?.cerrar();
  if (uid) await svc.auth.admin.deleteUser(uid).catch(() => {});
  rmSync(carpeta, { recursive: true, force: true });
};

try {
  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: 'Asistente Test' });

  impA = await crearImpresoraVirtual(9410);
  impB = await crearImpresoraVirtual(9411);

  // Arrancar el programa SIN configuracion, en una carpeta virgen.
  hijo = spawn(process.execPath, [join(BASE_APP, 'dist', 'index.js'), '--silencioso'], {
    cwd: carpeta,
    env: { ...process.env, GX_URL: URL, GX_ANON: ANON },
    stdio: 'ignore',
  });
  const arriba = await hasta(async () => {
    try {
      const r = await fetch('http://127.0.0.1:8737/api/estado');
      return r.ok;
    } catch {
      return false;
    }
  }, 10_000);
  check(arriba, 'el programa arranca y el asistente responde en 127.0.0.1:8737');

  const pagina = await (await fetch('http://127.0.0.1:8737/')).text();
  check(pagina.includes('Cerebrito') && pagina.includes('Gustito'), 'la pagina del asistente carga con la marca');

  let e = (await api('api/estado')).json;
  check(e && e.configurado === false, 'primera vez: sin configuracion (modo asistente)');

  const mala = await api('api/entrar', { correo: EMAIL, clave: 'clave-mala' });
  check(mala.status === 400, 'clave mala rebota');

  const entrar = await api('api/entrar', { correo: EMAIL, clave: CLAVE });
  check(
    entrar.status === 200 && /Do.a Carmen/.test(entrar.json?.restaurante ?? ''),
    'entrar reconoce el negocio: ' + (entrar.json?.restaurante ?? '?'),
  );
  const nombresPuestos = (entrar.json?.puestos ?? []).map((p) => p.nombre);
  check(nombresPuestos.includes('Cocina'), 'trae los puestos del negocio: ' + nombresPuestos.join(', '));

  const prueba = await api('api/probar', { host: '127.0.0.1', puerto: 9410 });
  await espera(400);
  check(prueba.json?.ok === true && impA.tickets.join('').includes('PRUEBA DE IMPRESION'), 'la prueba de impresion salio por la impresora');

  // Contrato de las casillas (v2.6.2): cada impresora lleva sus PUESTOS
  // marcados (varios por maquina); '__despacho' es la casilla del respaldo.
  const guardar = await api('api/guardar', {
    impresoras: [
      { host: '127.0.0.1', puerto: 9410, puestos: ['Cocina'] },
      { host: '127.0.0.1', puerto: 9411, puestos: ['__despacho'] },
    ],
  });
  check(guardar.json?.ok === true, 'guardar y arrancar responde ok');
  check(existsSync(join(carpeta, 'cerebrito.config.json')), 'la configuracion quedo escrita junto al programa');

  const vivo = await hasta(async () => {
    const j = (await api('api/estado')).json;
    return j?.configurado && j?.motor?.canalVivo === true;
  }, 20_000);
  check(vivo, 'el puente arranco y el canal en vivo conecto');

  const reimp = await api('api/motor', { accion: 'reimprimir' });
  check(reimp.json && reimp.json.codigo === null, 'reimprimir sin comandas todavia responde tranquilo');

  await api('api/motor', { accion: 'reserva' });
  e = (await api('api/estado')).json;
  check(e?.enReserva === true && !e?.motor, 'poner en RESERVA apaga el puente (suplente)');

  await api('api/motor', { accion: 'mando' });
  const devuelta = await hasta(async () => {
    const j = (await api('api/estado')).json;
    return j?.enReserva === false && j?.motor?.canalVivo === true;
  }, 20_000);
  check(devuelta, 'ASUMIR EL MANDO lo prende de nuevo (titular)');
} catch (e2) {
  console.log('  XX  Excepcion: ' + e2.message);
  fallos++;
} finally {
  await limpiar();
  console.log('\nLimpieza hecha (proceso, impresoras, usuario y carpeta fuera)');
}
console.log(fallos === 0 ? '\nASISTENTE OK' : `\nASISTENTE: ${fallos} FALLO(S)`);
process.exit(fallos === 0 ? 0 : 1);
