// LA ANULACION VIAJA POR LA COLA (2026-07-31, Bloque B)
//
// Lo que se prueba, contra la BD viva y la puerta publicada:
//   1. Un pedido cancelado ANTES de sellarse se descarta de la cola con rastro:
//      una venta cancelada no se factura, y tampoco queda huerfana.
//   2. Un pedido cancelado DESPUES de sellado pide su nota de credito SOLO.
//   3. La Estacion hala la solicitud por la puerta y responde con la nota.
//   4. Sin sesion del negocio, anular_venta_fiscal no deja pasar a nadie.
//   5. La solicitud manual exige documento sellado, y no se pide dos veces.
//   6. El latido cuenta las anulaciones como trabajo.
//   7. Una solicitud imposible se rinde a los 5 intentos y queda en error.
// 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 PUERTA = process.env.PUERTA ?? 'https://sello.gustitoxpress.app';

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 RIF = 'V-22606827-5';

const estacion = (llave, accion, datos = {}) =>
  fetch(`${PUERTA}/api/estacion`, {
    method: 'POST',
    headers: { authorization: `Bearer ${llave}`, 'content-type': 'application/json' },
    body: JSON.stringify({ accion, rif: RIF, datos }),
  });

let REST = null;

const limpiar = async () => {
  if (REST) {
    await sql(`delete from fiscal.anulaciones_gustito where restaurante_id = ${lit(REST)};
               delete from fiscal.cola_gustito where restaurante_id = ${lit(REST)};
               delete from fiscal.negocios where restaurante_id = ${lit(REST)};
               delete from public.pedido_items where pedido_id in (
                 select id from public.pedidos where restaurante_id=${lit(REST)} and codigo like 'FXNC%');
               delete from public.pedidos where restaurante_id=${lit(REST)} and codigo like 'FXNC%';
               update public.restaurantes set rif=null, razon_social=null, domicilio_fiscal=null where id=${lit(REST)};`);
  }
  await sql(`delete from fiscal.estaciones where emisor_id in (select id from fiscal.emisores where rif=${lit(RIF)});
             delete from fiscal.intentos where quien=${lit(RIF)};
             delete from fiscal.llaves where emisor_id in (select id from fiscal.emisores where rif=${lit(RIF)});
             delete from fiscal.documentos 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)};`);
};

const pedido = async (codigo) => {
  const p = await uno(`insert into public.pedidos (restaurante_id, codigo, cliente_nombre, cliente_telefono,
                         tipo_entrega, estado, entregado_en, subtotal_usd, total_usd, tasa_bs, metodo_pago)
                       values (${lit(REST)}, ${lit(codigo)}, 'Cliente', '0', 'pickup', 'entregado', now(),
                               10, 10, 746, 'efectivo') returning id`);
  await sql(`insert into public.pedido_items (pedido_id, nombre, precio_usd, cantidad)
             values (${lit(p.id)}, 'Arepa', 5, 2)`);
  return p.id;
};

try {
  const rest = await uno(`select id from public.restaurantes where slug='demo-gustito'`);
  if (!rest) throw new Error('No existe el restaurante demo');
  REST = rest.id;
  await limpiar();

  // El negocio queda habilitado, igual que en la prueba del interruptor.
  await sql(`update public.restaurantes set rif=${lit(RIF)} where id=${lit(REST)}`);
  await sql(`select fiscal.guardar_cliente(${lit(RIF)}, 'Restaurante de prueba', 'Araure, Portuguesa',
              null, null, null, null, 'Gustito Xpress', 'A', false, null)`);
  await sql(`select fiscal.vincular_negocio(${lit(REST)}, ${lit(RIF)}, 'A', 'gilberto', 'prueba anulacion')`);
  await sql(`update fiscal.emisores set rif_comprobado=true where rif=${lit(RIF)}`);
  const llave = (await uno(`select fiscal.crear_llave(${lit(RIF)}, 'estacion de prueba') as k`)).k;

  console.log('\n== 1. Cancelada ANTES de sellarse: se descarta con rastro ==');
  const p1 = await pedido('FXNC-1');
  check((await uno(`select count(*)::int as n from fiscal.cola_gustito where origen_id=${lit(p1)}`)).n === 1,
    'la venta entro a la cola al entregarse');
  await sql(`update public.pedidos set estado='cancelado' where id=${lit(p1)}`);
  const c1 = await uno(`select estado, ultimo_error from fiscal.cola_gustito where origen_id=${lit(p1)}`);
  check(c1.estado === 'descartada', `la cola la descarta sin borrarla (${c1.estado})`);
  check(/cancelada/i.test(c1.ultimo_error ?? ''), `con el motivo escrito (${c1.ultimo_error})`);
  check((await uno(`select count(*)::int as n from fiscal.anulaciones_gustito where origen_id=${lit(p1)}`)).n === 0,
    'y NO se pide nota de credito de algo que nunca se facturo');

  console.log('\n== 2. Cancelada DESPUES de sellada: la nota se pide sola ==');
  const p2 = await pedido('FXNC-2');
  // La Estacion se la lleva y confirma el sellado, como en la vida real.
  const halada = await (await estacion(llave, 'pendientes', { limite: 10 })).json();
  const enMano = halada.find((v) => v.origen_id === p2);
  check(!!enMano, 'la Estacion halo la venta por la puerta');
  const conf = await estacion(llave, 'confirmar', {
    cola_id: enMano.cola_id, numero_documento: 7, numero_control: 7, token: 'tok-fxnc-2',
  });
  check(conf.status === 200, `y confirmo el sellado (HTTP ${conf.status})`);
  await sql(`update public.pedidos set estado='cancelado' where id=${lit(p2)}`);
  const s2 = await uno(`select estado, motivo, pedida_por from fiscal.anulaciones_gustito where origen_id=${lit(p2)}`);
  check(s2?.estado === 'pendiente', 'la solicitud de anulacion nacio sola');
  check(s2?.pedida_por === 'sistema' && /cancelada/i.test(s2?.motivo ?? ''), `pedida por el sistema (${s2?.motivo})`);

  console.log('\n== 3. La Estacion hala la solicitud y responde con la nota ==');
  const lat = await (await estacion(llave, 'latido', { nombre: 'Prueba', equipo: 'FXNC' })).json();
  check(lat.anulaciones_en_nube === 1, `el latido la cuenta como trabajo (${lat.anulaciones_en_nube})`);
  check(lat.volver_en_segundos === 10, 'y pide volver ya mismo');
  const solicitudes = await (await estacion(llave, 'anulaciones', { limite: 10 })).json();
  check(Array.isArray(solicitudes) && solicitudes.length === 1, `hala la solicitud (${solicitudes.length})`);
  check(solicitudes[0]?.origen_id === p2 && !!solicitudes[0]?.motivo, 'con su venta y su motivo');
  const rr = await estacion(llave, 'anulacion_resultado', {
    anulacion_id: solicitudes[0].anulacion_id, ok: true, numero_nota: 3,
  });
  check(rr.status === 200, `y responde el resultado (HTTP ${rr.status})`);
  const hecha = await uno(`select estado, numero_nota from fiscal.anulaciones_gustito where origen_id=${lit(p2)}`);
  check(hecha.estado === 'hecha' && hecha.numero_nota === 3, `la solicitud queda hecha con su nota (${hecha.numero_nota})`);

  console.log('\n== 4. Sin sesion del negocio no se pide nada ==');
  let sinPermiso = false;
  try {
    await sql(`select public.anular_venta_fiscal(${lit(REST)}, 'pedido', ${lit(p2)}, 'intento suelto')`);
  } catch (e) {
    sinPermiso = /sin permiso/i.test(String(e));
  }
  check(sinPermiso, 'anular_venta_fiscal exige ser dueño o encargado del negocio');

  console.log('\n== 5. La solicitud manual exige documento sellado ==');
  const p3 = await pedido('FXNC-3');
  const sinSellar = (await uno(`select fiscal.solicitar_anulacion('pedido', ${lit(p3)}, ${lit(REST)},
    'Cliente devolvio', 'Prueba (dueño)', true) as r`)).r;
  check(sinSellar.ok === false && sinSellar.motivo === 'sin_sellar', `sin sellar responde claro (${sinSellar.motivo})`);
  // Se sella y ahora si.
  const halada3 = await (await estacion(llave, 'pendientes', { limite: 10 })).json();
  const enMano3 = halada3.find((v) => v.origen_id === p3);
  await estacion(llave, 'confirmar', { cola_id: enMano3.cola_id, numero_documento: 8, numero_control: 8, token: 't3' });
  const manual = (await uno(`select fiscal.solicitar_anulacion('pedido', ${lit(p3)}, ${lit(REST)},
    'Cliente devolvio', 'Prueba (dueño)', true) as r`)).r;
  check(manual.ok === true && manual.accion === 'solicitada', 'sellado, la solicitud entra');
  const repetida = (await uno(`select fiscal.solicitar_anulacion('pedido', ${lit(p3)}, ${lit(REST)},
    'Otra vez', 'Prueba (dueño)', true) as r`)).r;
  check(repetida.ok === true && repetida.accion === 'ya_solicitada', 'y pedirla de nuevo no duplica');

  console.log('\n== 6. Una solicitud imposible se rinde a los 5 intentos ==');
  const s3 = await uno(`select id from fiscal.anulaciones_gustito where origen_id=${lit(p3)}`);
  for (let i = 0; i < 5; i++) {
    await estacion(llave, 'anulaciones', { limite: 10 });
    await estacion(llave, 'anulacion_resultado', {
      anulacion_id: s3.id, ok: false, error: 'esa venta no esta en el libro',
    });
  }
  const rendida = await uno(`select estado, intentos, ultimo_error from fiscal.anulaciones_gustito where id=${lit(s3.id)}`);
  check(rendida.estado === 'error' && rendida.intentos === 5, `queda en error tras ${rendida.intentos} intentos`);
  check(/no esta en el libro/.test(rendida.ultimo_error ?? ''), 'con el porque guardado');
} catch (e) {
  console.log('  XX  ' + e.message);
  fallos++;
} finally {
  await limpiar().catch(() => {});
}

console.log(fallos === 0 ? '\nANULACION POR LA COLA: TODO VERDE' : `\n${fallos} FALLO(S)`);
process.exit(fallos === 0 ? 0 : 1);
