// Verificacion de BROADCAST DEL PANEL (fn_bcast_panel + canal privado rest-bcast-{id})
// contra la BD viva. Usa el restaurante DEMO, un mesero temporal, y LIMPIA todo.
// Env: SUPABASE_URL, SUPABASE_ANON_KEY, SUPABASE_SERVICE_KEY.
import { createClient } from '@supabase/supabase-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 = (cond, msg) => {
  console.log((cond ? '  OK  ' : '  XX  ') + msg);
  if (!cond) fallos++;
};
const nuevo = () => createClient(URL, ANON, { auth: { persistSession: false } });
const svc = createClient(URL, SERVICE, { auth: { persistSession: false } });
const espera = (ms) => new Promise((r) => setTimeout(r, ms));

const { data: rest } = await svc.from('restaurantes').select('id,abierto').eq('slug', 'demo-gustito').maybeSingle();
if (!rest?.id || !rest.abierto) {
  console.log('Demo no disponible');
  process.exit(1);
}
const REST = rest.id;
const CANAL = `rest-bcast-${REST}`;
const { data: prods } = await svc
  .from('productos')
  .select('id,precio_usd')
  .eq('restaurante_id', REST)
  .eq('disponible', true)
  .limit(1);
const PROD_ID = prods?.[0]?.id;
if (!PROD_ID) {
  console.log('Demo sin productos disponibles');
  process.exit(1);
}

const EMAIL = 'staff.test.broadcast@gustitoxpress.app';
const CLAVE = 'BroadcastTest2026!';
let staffUid;
let pedidoId;

const limpiar = async () => {
  if (pedidoId) await svc.from('pedidos').delete().eq('id', pedidoId);
  if (staffUid) await svc.auth.admin.deleteUser(staffUid).catch(() => {});
};

try {
  await limpiar();
  const { data: u, error: eU } = await svc.auth.admin.createUser({ email: EMAIL, password: CLAVE, email_confirm: true });
  if (eU) throw new Error('createUser: ' + eU.message);
  staffUid = u.user.id;
  await svc.from('perfiles').upsert({ id: staffUid, rol: 'staff', restaurante_id: REST, nombre: 'Staff Broadcast Test' });

  // --- Cliente AUTORIZADO (personal del restaurante): debe recibir el evento. ---
  const staff = nuevo();
  const { data: sesion } = await staff.auth.signInWithPassword({ email: EMAIL, password: CLAVE });
  await staff.realtime.setAuth(sesion.session.access_token);

  const recibidosStaff = [];
  const canalStaff = staff.channel(CANAL, { config: { private: true } });
  canalStaff.on('broadcast', { event: 'cambio' }, ({ payload }) => recibidosStaff.push(payload));

  const estadoSuscripcion = await new Promise((resolve) => {
    canalStaff.subscribe((status) => {
      if (status === 'SUBSCRIBED' || status === 'CHANNEL_ERROR' || status === 'TIMED_OUT') resolve(status);
    });
  });
  check(estadoSuscripcion === 'SUBSCRIBED', 'el personal del restaurante SI puede suscribirse al canal privado');

  // --- Cliente ANONIMO: NO debe poder escuchar el canal de este restaurante. ---
  const anon = nuevo();
  const recibidosAnon = [];
  const canalAnon = anon.channel(CANAL, { config: { private: true } });
  canalAnon.on('broadcast', { event: 'cambio' }, ({ payload }) => recibidosAnon.push(payload));
  const estadoAnon = await new Promise((resolve) => {
    canalAnon.subscribe((status) => {
      if (status === 'SUBSCRIBED' || status === 'CHANNEL_ERROR' || status === 'TIMED_OUT') resolve(status);
    });
  });
  // Con RLS negando el select, supabase-js reporta CHANNEL_ERROR (no autorizado).
  check(estadoAnon !== 'SUBSCRIBED', 'un anonimo NO puede suscribirse al canal del restaurante ajeno (RLS activa)');

  // --- Disparar el trigger: crear un pedido real (camino real de produccion). ---
  const { data: creado, error: eCrear } = await anon.rpc('crear_pedido', {
    p_restaurante_id: REST,
    p_cliente_nombre: 'Cliente Broadcast Test',
    p_cliente_telefono: '04120000000',
    p_tipo_entrega: 'pickup',
    p_metodo_pago: 'efectivo',
    p_items: [{ producto_id: PROD_ID, cantidad: 1 }],
  });
  check(!eCrear && !!creado?.[0]?.pedido_id, 'crear_pedido funciona' + (eCrear ? ' -> ' + eCrear.message : ''));
  pedidoId = creado?.[0]?.pedido_id;

  await espera(3000); // el trigger dispara sincrono; el margen es para el viaje del websocket

  // crear_pedido inserta con totales en $0 y los actualiza despues de tasar los
  // items (ver su cuerpo), asi que UN pedido nuevo dispara dos eventos reales:
  // INSERT (estado inicial) y UPDATE (mismo estado, total ya calculado). Filtrar
  // por accion, no asumir que el primero en llegar por el socket es el INSERT.
  const eventos = recibidosStaff.filter((p) => p.id === pedidoId);
  const evento = eventos.find((p) => p.accion === 'INSERT');
  check(eventos.length > 0, 'el staff recibio el broadcast del pedido nuevo por el canal privado');
  check(evento?.tabla === 'pedidos' && evento?.accion === 'INSERT', 'el payload trae tabla=pedidos y accion=INSERT');
  check(evento?.nuevo?.estado === 'recibido', 'el payload trae el estado nuevo (recibido)');
  check(evento?.nuevo?.tipo_entrega === 'pickup', 'el payload trae tipo_entrega');
  check(recibidosAnon.length === 0, 'el anonimo NO recibio nada del pedido (aislamiento entre restaurantes)');

  // --- Cambio de estado: confirmar y verificar UPDATE con viejo/nuevo estado.
  // Con el cliente STAFF (no service_role): cambiar_estado_pedido valida permiso
  // contra auth.uid() via fn_es_staff_de, que el service_role no tiene. ---
  const antesDeConfirmar = recibidosStaff.length;
  const { error: eConfirmar } = await staff.rpc('cambiar_estado_pedido', { p_pedido_id: pedidoId, p_nuevo_estado: 'confirmado' });
  check(!eConfirmar, 'cambiar_estado_pedido a confirmado' + (eConfirmar ? ' -> ' + eConfirmar.message : ''));
  await espera(2500);
  const eventoUpdate = recibidosStaff.slice(antesDeConfirmar).find((p) => p.id === pedidoId && p.accion === 'UPDATE');
  check(!!eventoUpdate, 'el staff recibio el UPDATE de confirmado');
  check(eventoUpdate?.nuevo?.estado === 'confirmado' && eventoUpdate?.viejo?.estado === 'recibido', 'trae viejo Y nuevo estado');

  await staff.removeChannel(canalStaff);
  await anon.removeChannel(canalAnon);
} catch (e) {
  console.log('  XX  Excepción: ' + e.message);
  fallos++;
} finally {
  await limpiar();
  console.log('\nLimpieza hecha (pedido y usuario de prueba fuera)');
}

console.log(fallos === 0 ? '\nBROADCAST PANEL OK' : `\nBROADCAST PANEL: ${fallos} FALLO(S)`);
process.exit(fallos === 0 ? 0 : 1);
