// PRUEBA DEL RASTRO DEL PANEL Y DE LOS OPERADORES (2026-07-29)
//
// Nivel 2 del sprint de blindaje, contra el panel publicado.
//
// Antes: suspender a un cliente, generarle una llave o habilitarle la
// facturacion a un negocio no quedaba escrito en ningun lado, y el "quien
// vinculo" era un texto que mandaba el NAVEGADOR (cualquiera escribia lo que
// quisiera). Ademas habia un solo operador y no existia forma de crear otro sin
// meter SQL a mano: si a esa persona le pasa algo, la imprenta se queda parada.
//
// Limpia todo lo que crea.
import { readFileSync } from 'node:fs';

const REF = 'szzhhlevtrcambcoytiz';
const PAT = readFileSync(new URL('../.sb_token', import.meta.url), 'utf8').trim();
const PANEL = process.env.PANEL ?? 'https://sello.gustitoxpress.app';
const USUARIO = process.env.PANEL_USUARIO ?? 'gilberto';
const CLAVE =
  process.env.PANEL_CLAVE ?? readFileSync(new URL('../.panel_clave', import.meta.url), 'utf8').trim();
const RIF = 'V-22606827-5';

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

async function sql(query) {
  const r = await fetch(`https://api.supabase.com/v1/projects/${REF}/database/query`, {
    method: 'POST',
    headers: { Authorization: `Bearer ${PAT}`, 'Content-Type': 'application/json' },
    body: JSON.stringify({ query }),
  });
  const t = await r.text();
  if (!r.ok) throw new Error(t.slice(0, 300));
  return JSON.parse(t);
}
const uno = async (q) => (await sql(q))[0];
const lit = (s) => `'${String(s).replace(/'/g, "''")}'`;

const DESDE = new Date().toISOString();
const limpiar = () =>
  sql(`delete from fiscal.llaves where emisor_id in (select id from fiscal.emisores where rif=${lit(RIF)});
       delete from fiscal.series where emisor_id in (select id from fiscal.emisores where rif=${lit(RIF)});
       delete from fiscal.emisores where rif=${lit(RIF)};
       delete from fiscal.intentos where tipo='panel' and quien like 'prueba%';
       delete from fiscal.operadores where usuario like 'prueba%';`);

let galleta = '';
async function api(accion, datos) {
  const r = await fetch(`${PANEL}/api/admin`, {
    method: 'POST',
    headers: { 'content-type': 'application/json', ...(galleta ? { cookie: galleta } : {}) },
    body: JSON.stringify({ accion, datos: datos || {} }),
  });
  const set = r.headers.get('set-cookie');
  if (set) galleta = set.split(';')[0];
  return { estado: r.status, cuerpo: await r.json().catch(() => ({})) };
}

try {
  await limpiar();
  await api('entrar', { usuario: USUARIO, clave: CLAVE });

  console.log('\n== 1. Lo que se hace en el panel queda escrito ==');
  await api('guardar_cliente', {
    rif: RIF,
    razon_social: 'Comercial de prueba rastro',
    domicilio: 'Araure, Portuguesa',
    sistema: 'Estacion Fiscal',
    serie: 'C',
    rif_comprobado: true,
  });
  await api('crear_llave', { rif: RIF, nombre: 'llave de prueba' });
  await api('activar_cliente', { rif: RIF, activo: false });
  const movs = (await api('movimientos', { limite: 50 })).cuerpo.datos;
  // Solo los de esta corrida: el rastro NO se puede borrar, asi que las corridas
  // viejas siguen ahi para siempre (que es justamente la gracia).
  const mios = movs.filter((m) => JSON.stringify(m.detalle).includes(RIF) && m.en >= DESDE);
  check(mios.some((m) => m.accion === 'cliente_guardado'), 'quedo el alta del cliente');
  check(mios.some((m) => m.accion === 'llave_creada'), 'quedo la llave que se genero');
  check(mios.some((m) => m.accion === 'cliente_suspendido'), 'y quedo la suspension');

  console.log('\n== 2. Y queda con el nombre del que entro, no con el que diga el navegador ==');
  check(mios.every((m) => m.quien === 'Gilberto Gonzalez'), `todos firmados por quien tiene la sesion (${mios[0]?.quien})`);
  check(!!mios[0]?.desde, `con la direccion de donde se hizo (${mios[0]?.desde})`);
  // Se intenta firmar un movimiento con otro nombre, como haria alguien con la
  // consola del navegador abierta.
  await api('activar_cliente', { rif: RIF, activo: true, quien: 'El Chapulin Colorado' });
  const despues = (await api('movimientos', { limite: 10 })).cuerpo.datos;
  check(
    !despues.some((m) => m.quien === 'El Chapulin Colorado'),
    'mandar un "quien" a mano por el cuerpo no sirve de nada',
  );
  check(despues[0].quien === 'Gilberto Gonzalez', 'sigue firmando la sesion');

  console.log('\n== 3. El rastro no se puede editar ni borrar ==');
  let problema = '';
  try {
    await sql(`update fiscal.eventos_panel set quien='otro' where en >= ${lit(DESDE)};`);
  } catch (e) {
    problema = String(e.message);
  }
  check(/solo agrega/i.test(problema), 'un UPDATE revienta contra el candado');
  problema = '';
  try {
    await sql(`delete from fiscal.eventos_panel where en >= ${lit(DESDE)};`);
  } catch (e) {
    problema = String(e.message);
  }
  check(/solo agrega/i.test(problema), 'y un DELETE tambien');

  console.log('\n== 4. La imprenta deja de depender de una sola persona ==');
  const corta = await api('guardar_operador', { usuario: 'pruebasocio', nombre: 'Socio de prueba', clave: 'corta' });
  check(corta.estado === 400, `una clave corta no se acepta (HTTP ${corta.estado})`);
  const raro = await api('guardar_operador', { usuario: 'con espacios', nombre: 'X', clave: 'clavedeprueba2026' });
  check(raro.estado === 400, 'un usuario con espacios tampoco');
  const nuevo = await api('guardar_operador', {
    usuario: 'pruebasocio',
    nombre: 'Socio de prueba',
    clave: 'clavedelsocio2026',
  });
  check(nuevo.estado === 200, 'se crea el segundo operador');
  const lista = (await api('operadores')).cuerpo.datos;
  check(lista.some((o) => o.usuario === 'pruebasocio' && o.activo), 'sale en la lista, activo');

  console.log('\n== 5. El segundo operador entra de verdad ==');
  const galletaVieja = galleta;
  galleta = '';
  const entra = await api('entrar', { usuario: 'pruebasocio', clave: 'clavedelsocio2026' });
  check(entra.estado === 200 && entra.cuerpo.nombre === 'Socio de prueba', `entra con lo suyo (${entra.cuerpo.nombre})`);
  const suMov = await api('activar_cliente', { rif: RIF, activo: false });
  check(suMov.estado === 200, 'y puede trabajar');
  const rastro = (await api('movimientos', { limite: 5 })).cuerpo.datos;
  check(rastro[0].quien === 'Socio de prueba', `sus movimientos salen con SU nombre (${rastro[0].quien})`);

  console.log('\n== 6. Nadie se deja a si mismo afuera ==');
  const solo = await api('activar_operador', { usuario: 'pruebasocio', activo: false });
  check(solo.estado === 400 && /ti mismo/i.test(solo.cuerpo.error ?? ''), `no puede apagarse el solo (${solo.cuerpo.error})`);
  galleta = galletaVieja;
  const apagado = await api('activar_operador', { usuario: 'pruebasocio', activo: false });
  check(apagado.estado === 200, 'pero el otro operador si lo puede apagar');
  galleta = '';
  const yaNo = await api('entrar', { usuario: 'pruebasocio', clave: 'clavedelsocio2026' });
  check(yaNo.estado === 401, `y apagado ya no entra (HTTP ${yaNo.estado})`);
} catch (e) {
  console.log('  XX  ' + String(e).slice(0, 250));
  fallos++;
} finally {
  await limpiar();
}

console.log(fallos === 0 ? '\nRASTRO DEL PANEL: TODO VERDE' : `\n${fallos} FALLO(S)`);
process.exit(fallos === 0 ? 0 : 1);
