-- CORREO DE LA FACTURA (2026-07-29): outbox + trigger + RPCs de drenaje.
--
-- Cuando la Estacion confirma el sellado (cola_gustito pasa a 'sellado' con su
-- token), si el pedido tiene correo del cliente se encola un correo con el
-- enlace del sello. El envio real lo hace la Pages Function de la imprenta
-- (Mailtrap API); aca solo vive la cola, para que ningun correo se pierda y
-- ningun correo tumbe jamas una confirmacion fiscal.

create table if not exists fiscal.correos_factura (
  id uuid primary key default gen_random_uuid(),
  cola_id uuid not null unique references fiscal.cola_gustito(id) on delete cascade,
  emisor_id uuid not null,
  token text not null,
  para text not null,
  estado text not null default 'pendiente' check (estado in ('pendiente','enviado','fallado')),
  intentos int not null default 0,
  ultimo_error text,
  creado_en timestamptz not null default now(),
  enviado_en timestamptz
);
create index if not exists correos_factura_pendientes
  on fiscal.correos_factura (creado_en) where estado = 'pendiente';

create or replace function fiscal.tg_encolar_correo_factura() returns trigger
language plpgsql security definer set search_path to 'fiscal','public'
as $$
declare v_correo text;
begin
  -- Solo el momento exacto en que la venta queda sellada y con token.
  if new.estado <> 'sellado' or new.token is null or old.estado = 'sellado' then
    return new;
  end if;
  -- v1: solo pedidos (la cuenta de mesa no captura correo del cliente).
  if new.tipo <> 'pedido' then return new; end if;

  select nullif(trim(cliente_correo), '') into v_correo
    from public.pedidos where id = new.origen_id::uuid;
  if v_correo is null then return new; end if;

  insert into fiscal.correos_factura (cola_id, emisor_id, token, para)
  values (new.id, new.emisor_id, new.token, v_correo)
  on conflict (cola_id) do nothing;
  return new;
exception when others then
  -- El correo es cortesia; el sellado es sagrado. Si esto falla (un origen_id
  -- que no es uuid, un permiso), la confirmacion fiscal sigue intacta.
  return new;
end $$;

drop trigger if exists tg_correo_factura on fiscal.cola_gustito;
create trigger tg_correo_factura
  after update on fiscal.cola_gustito
  for each row execute function fiscal.tg_encolar_correo_factura();

-- Lo que la imprenta necesita para redactar y mandar cada correo pendiente.
create or replace function public.fn_correos_factura_pendientes(p_limite int default 5)
returns jsonb
language sql security definer set search_path to 'fiscal','public'
as $$
  select coalesce(jsonb_agg(fila), '[]'::jsonb) from (
    select jsonb_build_object(
      'id', c.id,
      'para', c.para,
      'token', c.token,
      'negocio', e.razon_social,
      'numero', d.numero_documento,
      'serie', d.serie,
      'control', d.numero_control,
      'fecha', d.fecha_emision,
      'total_bs', d.total_bs,
      'intentos', c.intentos
    ) as fila
    from fiscal.correos_factura c
    join fiscal.emisores e on e.id = c.emisor_id
    left join fiscal.documentos d on d.token = c.token
    where c.estado = 'pendiente' and c.intentos < 5
    order by c.creado_en
    limit greatest(1, least(coalesce(p_limite, 5), 20))
  ) t;
$$;

create or replace function public.fn_correo_factura_resultado(p_id uuid, p_ok boolean, p_error text default null)
returns void
language sql security definer set search_path to 'fiscal','public'
as $$
  update fiscal.correos_factura set
    intentos = intentos + 1,
    ultimo_error = p_error,
    estado = case
      when p_ok then 'enviado'
      when intentos + 1 >= 5 then 'fallado'
      else 'pendiente'
    end,
    enviado_en = case when p_ok then now() else enviado_en end
  where id = p_id;
$$;

-- Solo la imprenta (service_role) drena la cola de correos.
revoke execute on function public.fn_correos_factura_pendientes(int) from public, anon, authenticated;
revoke execute on function public.fn_correo_factura_resultado(uuid, boolean, text) from public, anon, authenticated;
grant execute on function public.fn_correos_factura_pendientes(int) to service_role;
grant execute on function public.fn_correo_factura_resultado(uuid, boolean, text) to service_role;
