// EL INVENTARIO CUADRA (2026-07-29)
//
// Bloque 4 del plan: el trigger viejo (tg_descontar_stock) solo restaba al
// insertar un renglon, fallaba callado y nada reponia al cancelar ni al quitar
// un renglon. Esta prueba cubre la invariante nueva contra la base viva:
//
//   el stock consumido = renglones de pedidos EXISTENTES con estado <> 'cancelado'
//
//   - Vender resta; el producto sin control de stock no se toca.
//   - Quitar un renglon (lo que hace quitar_item_pedido) repone.
//   - Cancelar el pedido repone todo, y cancelarlo "dos veces" no repone doble.
//   - Un renglon de un pedido ya cancelado no mueve nada (ni al borrarse ni al
//     insertarse).
//   - Purgar fisicamente un pedido entregado NO repone (la comida se despacho).
//   - Sobreventa deja stock negativo (aviso honesto), y al cancelar vuelve exacto.
//   - Cambiar la cantidad de un renglon ajusta la diferencia exacta.
//
// 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();

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, 400));
  return JSON.parse(t);
}
const uno = async (q) => (await sql(q))[0];
const lit = (s) => `'${String(s).replace(/'/g, "''")}'`;

let REST = null;
const limpiar = async () => {
  if (!REST) return;
  await sql(`delete from public.pedidos where restaurante_id=${lit(REST)} and codigo like 'STKTEST%';
             delete from public.productos where restaurante_id=${lit(REST)} and nombre like 'STKTEST%';`);
};

const stockDe = async (id) => (await uno(`select stock from public.productos where id=${lit(id)}`)).stock;
const nuevoPedido = async (codigo) =>
  (await uno(`insert into public.pedidos (restaurante_id, codigo, cliente_nombre, cliente_telefono,
      tipo_entrega, estado, subtotal_usd, total_usd, tasa_bs, metodo_pago)
    values (${lit(REST)}, ${lit(codigo)}, 'Cliente stock', '0', 'pickup', 'recibido', 10, 10, 742.23, 'efectivo')
    returning id`)).id;
const meterItem = async (pedido, producto, cantidad) =>
  (await uno(`insert into public.pedido_items (pedido_id, producto_id, nombre, precio_usd, cantidad)
    values (${lit(pedido)}, ${lit(producto)}, 'STKTEST item', 1, ${cantidad}) returning id`)).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();

  console.log('\n== 0. El trigger viejo murio y el par nuevo esta vivo ==');
  const viejos = await sql(`select tgname from pg_trigger where tgname='trg_descontar_stock'`);
  check(viejos.length === 0, 'trg_descontar_stock ya no existe');
  const nuevos = await sql(`select tgname from pg_trigger where tgname in ('trg_cuadrar_stock_item','trg_cuadrar_stock_pedido')`);
  check(nuevos.length === 2, 'trg_cuadrar_stock_item y trg_cuadrar_stock_pedido montados');

  const prodA = (await uno(`insert into public.productos (restaurante_id, nombre, precio_usd, controla_stock, stock)
    values (${lit(REST)}, 'STKTEST-A con control', 1, true, 10) returning id`)).id;
  const prodB = (await uno(`insert into public.productos (restaurante_id, nombre, precio_usd, controla_stock, stock)
    values (${lit(REST)}, 'STKTEST-B sin control', 1, false, 99) returning id`)).id;

  console.log('\n== 1. Vender resta; sin control no se toca ==');
  const p1 = await nuevoPedido('STKTEST-1');
  const item3A = await meterItem(p1, prodA, 3);
  await meterItem(p1, prodB, 2);
  check((await stockDe(prodA)) === 7, 'A: 10 - 3 = 7');
  check((await stockDe(prodB)) === 99, 'B sin control queda intacto en 99');

  console.log('\n== 2. Quitar un renglon repone ==');
  await sql(`delete from public.pedido_items where id=${lit(item3A)}`);
  check((await stockDe(prodA)) === 10, 'A vuelve a 10 al quitar el renglon');

  console.log('\n== 3. Cancelar repone todo, y no repone doble ==');
  await meterItem(p1, prodA, 4);
  check((await stockDe(prodA)) === 6, 'A: 10 - 4 = 6');
  await sql(`update public.pedidos set estado='cancelado', cancelado_en=now() where id=${lit(p1)}`);
  check((await stockDe(prodA)) === 10, 'A vuelve a 10 al cancelar el pedido');
  await sql(`update public.pedidos set estado='cancelado' where id=${lit(p1)}`);
  check((await stockDe(prodA)) === 10, 'volver a marcar cancelado no repone doble');

  console.log('\n== 4. Un pedido cancelado ya no mueve el stock ==');
  await sql(`delete from public.pedido_items where pedido_id=${lit(p1)} and producto_id=${lit(prodA)}`);
  check((await stockDe(prodA)) === 10, 'borrar un renglon del cancelado no repone');
  await meterItem(p1, prodA, 2);
  check((await stockDe(prodA)) === 10, 'meter un renglon al cancelado no resta');

  console.log('\n== 5. Purgar un pedido entregado NO repone ==');
  const p2 = await nuevoPedido('STKTEST-2');
  await meterItem(p2, prodA, 5);
  check((await stockDe(prodA)) === 5, 'A: 10 - 5 = 5');
  await sql(`update public.pedidos set estado='entregado', entregado_en=now() where id=${lit(p2)}`);
  check((await stockDe(prodA)) === 5, 'marcar entregado no toca el stock');
  await sql(`delete from public.pedidos where id=${lit(p2)}`);
  check((await stockDe(prodA)) === 5, 'purgar el pedido entregado deja el stock consumido');

  console.log('\n== 6. Sobreventa honesta: negativo, y al cancelar vuelve exacto ==');
  await sql(`update public.productos set stock=2 where id=${lit(prodA)}`);
  const p3 = await nuevoPedido('STKTEST-3');
  await meterItem(p3, prodA, 7);
  check((await stockDe(prodA)) === -5, 'A: 2 - 7 = -5 (el conteo estaba malo y se nota)');
  await sql(`update public.pedidos set estado='cancelado', cancelado_en=now() where id=${lit(p3)}`);
  check((await stockDe(prodA)) === 2, 'al cancelar vuelve exacto a 2, sin inventar comida');

  console.log('\n== 7. Cambiar la cantidad ajusta la diferencia ==');
  await sql(`update public.productos set stock=10 where id=${lit(prodA)}`);
  const p4 = await nuevoPedido('STKTEST-4');
  const item2A = await meterItem(p4, prodA, 2);
  check((await stockDe(prodA)) === 8, 'A: 10 - 2 = 8');
  await sql(`update public.pedido_items set cantidad=5 where id=${lit(item2A)}`);
  check((await stockDe(prodA)) === 5, 'subir el renglon de 2 a 5 deja 10 - 5 = 5');
  await sql(`update public.pedidos set estado='cancelado', cancelado_en=now() where id=${lit(p4)}`);
  check((await stockDe(prodA)) === 10, 'cancelar repone la cantidad nueva, no la vieja');

  await limpiar();
  console.log(fallos === 0 ? '\nTODO EN VERDE' : `\n${fallos} FALLOS`);
  process.exit(fallos === 0 ? 0 : 1);
} catch (e) {
  console.error('\nREVENTO:', String(e.message).slice(0, 500));
  try { await limpiar(); } catch { /* ya reventamos, el cleanup es mejor esfuerzo */ }
  process.exit(1);
}
