-- =============================================================================
-- BROADCAST DEL PANEL (fase 1): los eventos que el panel escuchaba con
-- postgres_changes ahora se emiten desde triggers a un canal privado por
-- restaurante: rest-bcast-{restaurante_id}.
--
-- Por que: postgres_changes evalua cada cambio contra CADA suscriptor (RLS por
-- evento por dispositivo) y con muchos locales conectados a la vez mete lag en
-- el pico. Broadcast entrega el mensaje al canal y ya. Es la misma leccion del
-- chat de ZafraClic (2026-07-20), aqui resuelta a nivel BD con realtime.send().
--
-- Diseno:
--   * UN canal por restaurante (no por tabla): el dispositivo del mesero abre
--     UNA suscripcion en vez de 7.
--   * Payload minimo {tabla, accion, id, nuevo{estado,tipo_entrega,mesa,
--     verificado}, viejo{estado}}: lo justo para que cada vista decida si le
--     toca refrescar (y traer la fila puntual si la necesita).
--   * Canal PRIVADO + policy en realtime.messages: solo el personal del
--     restaurante (perfiles.restaurante_id) puede escuchar su canal. Sin esto,
--     cualquiera escucharia los pedidos de cualquier local.
--   * El envio va envuelto en un bloque de excepcion: el aviso en vivo JAMAS
--     puede tumbar la operacion real (crear un pedido, cobrar una mesa). Si el
--     broadcast falla, el poll de respaldo del panel (5-15s) lo recoge.
--
-- Fase 2 (migracion aparte, cuando el frontend nuevo este desplegado y
-- verificado): sacar pedidos/cuentas_mesa/llamadas_mesa/pagos_mesa de la
-- publicacion supabase_realtime para apagar el costo de WAL por suscriptor.
-- =============================================================================

-- Generica multi-tabla: SIEMPRE to_jsonb(NEW/OLD), nunca NEW.columna directo
-- (las 4 tablas tienen columnas distintas; ver leccion de triggers genericos).
create or replace function public.fn_bcast_panel()
returns trigger
language plpgsql
security definer
set search_path = ''
as $$
declare
  v_new jsonb := case when tg_op = 'DELETE' then null else to_jsonb(new) end;
  v_old jsonb := case when tg_op = 'INSERT' then null else to_jsonb(old) end;
  v_rest text := coalesce(v_new->>'restaurante_id', v_old->>'restaurante_id');
begin
  -- Sin restaurante no hay canal (no deberia pasar en estas 4 tablas).
  if v_rest is null then
    return null;
  end if;

  begin
    perform realtime.send(
      jsonb_strip_nulls(jsonb_build_object(
        'tabla', tg_table_name,
        'accion', tg_op,
        'id', coalesce(v_new->>'id', v_old->>'id'),
        'nuevo', case when v_new is null then null else jsonb_strip_nulls(jsonb_build_object(
          'estado',       v_new->'estado',
          'tipo_entrega', v_new->'tipo_entrega',
          'mesa',         v_new->'mesa',
          'verificado',   v_new->'verificado'
        )) end,
        'viejo', case when v_old is null then null else jsonb_strip_nulls(jsonb_build_object(
          'estado', v_old->'estado'
        )) end
      )),
      'cambio',                  -- evento
      'rest-bcast-' || v_rest,   -- canal del restaurante
      true                       -- privado
    );
  exception when others then
    -- El tiempo real es un extra, nunca la operacion. Silencio y a seguir.
    null;
  end;

  return null;
end;
$$;

comment on function public.fn_bcast_panel() is
  'Emite cambios de pedidos/cuentas_mesa/llamadas_mesa/pagos_mesa al canal privado rest-bcast-{restaurante_id} (broadcast, reemplaza postgres_changes del panel).';

-- Triggers sobre las 4 tablas que el panel escucha.
drop trigger if exists trg_bcast_panel on public.pedidos;
create trigger trg_bcast_panel
  after insert or update or delete on public.pedidos
  for each row execute function public.fn_bcast_panel();

drop trigger if exists trg_bcast_panel on public.cuentas_mesa;
create trigger trg_bcast_panel
  after insert or update or delete on public.cuentas_mesa
  for each row execute function public.fn_bcast_panel();

drop trigger if exists trg_bcast_panel on public.llamadas_mesa;
create trigger trg_bcast_panel
  after insert or update or delete on public.llamadas_mesa
  for each row execute function public.fn_bcast_panel();

drop trigger if exists trg_bcast_panel on public.pagos_mesa;
create trigger trg_bcast_panel
  after insert or update or delete on public.pagos_mesa
  for each row execute function public.fn_bcast_panel();

-- Autorizacion del canal privado: solo el personal del restaurante escucha el
-- canal de SU restaurante (dueno, staff o repartidor: todos tienen
-- perfiles.restaurante_id y todos ya ven estos datos por RLS de tablas).
drop policy if exists panel_escucha_su_restaurante on realtime.messages;
create policy panel_escucha_su_restaurante
on realtime.messages
for select
to authenticated
using (
  extension = 'broadcast'
  and exists (
    select 1
      from public.perfiles p
     where p.id = (select auth.uid())
       and p.restaurante_id is not null
       and 'rest-bcast-' || p.restaurante_id::text = realtime.topic()
  )
);
