-- ============================================================
-- Gustito Xpress — MODELO de cobro correcto (Gilberto):
--
-- El mesero SI puede registrar cualquier metodo, incluido pago electronico
-- (pago movil, zelle, transferencia): coloca la REFERENCIA. Pero:
--   - EFECTIVO (mesero): queda verificado y la mesa se cierra de una.
--   - ELECTRONICO por el mesero: queda POR REVISAR y la mesa NO se cierra;
--     el PANEL (dueño) verifica la referencia y ahi se libera la mesa.
--   - ELECTRONICO por el dueño: el dueño es el panel, queda verificado.
--
-- La mesa se cierra automaticamente cuando TODO esta pagado y verificado.
-- ============================================================

-- ---- helper: la mesa esta toda pagada y verificada? ----
create or replace function public.fn_mesa_toda_pagada(p_cuenta_id uuid)
returns boolean language sql stable security definer set search_path to 'public' as $$
  select not exists (
    select 1 from public.pedidos p
    left join public.pagos_mesa pm on pm.id = p.pago_mesa_id
    where p.cuenta_id = p_cuenta_id and p.estado <> 'cancelado'
      and (p.pago_mesa_id is null or pm.verificado = false)
  );
$$;

-- ---- cobrar_parte_mesa: efectivo verificado; electronico del mesero por revisar ----
create or replace function public.cobrar_parte_mesa(p_cuenta_id uuid, p_pedido_ids uuid[], p_metodo text, p_referencia text default null, p_nota text default null)
returns table(pago_id uuid, monto_usd numeric, saldo_usd numeric)
language plpgsql security definer set search_path to 'public'
as $function$
declare
  v_cta            public.cuentas_mesa%rowtype;
  v_esperados      int;
  v_n              int;
  v_monto          numeric(10,2);
  v_pago           uuid;
  v_mesero_id      uuid := null;
  v_mesero_nombre  text := null;
  v_total          numeric(10,2);
  v_pagado         numeric(10,2);
  v_verif          boolean;
begin
  select * into v_cta from public.cuentas_mesa where id = p_cuenta_id for update;
  if not found or v_cta.estado = 'cerrada' then
    raise exception 'La cuenta no esta disponible';
  end if;
  if not (fn_es_staff_de(v_cta.restaurante_id) or fn_es_admin()) then
    raise exception 'Sin permiso';
  end if;
  if p_metodo not in ('pago_movil','efectivo','transferencia','zelle','punto_venta') then
    raise exception 'Metodo de pago invalido';
  end if;
  -- Efectivo (o el dueño) = verificado; electronico del mesero = por revisar.
  v_verif := (p_metodo = 'efectivo') or fn_es_dueno_de(v_cta.restaurante_id) or fn_es_admin();

  select count(distinct u) into v_esperados from unnest(coalesce(p_pedido_ids, '{}'::uuid[])) u;
  if v_esperados = 0 then raise exception 'Elige al menos un pedido para cobrar'; end if;

  select pf.id, coalesce(nullif(trim(pf.nombre), ''), 'Mesero')
    into v_mesero_id, v_mesero_nombre from public.perfiles pf where pf.id = auth.uid();

  select count(*), coalesce(sum(s.total_usd), 0) into v_n, v_monto
    from (
      select p.total_usd from public.pedidos p
       where p.id = any(p_pedido_ids) and p.cuenta_id = p_cuenta_id
         and p.pago_mesa_id is null and p.estado <> 'cancelado'
       for update
    ) s;
  if v_n <> v_esperados then
    raise exception 'Algun pedido ya fue cobrado, esta cancelado o no es de esta cuenta';
  end if;
  if v_monto <= 0 then raise exception 'Nada que cobrar'; end if;

  insert into public.pagos_mesa (cuenta_id, restaurante_id, monto_usd, metodo, referencia, nota, mesero_id, mesero_nombre, verificado)
    values (p_cuenta_id, v_cta.restaurante_id, v_monto, p_metodo, nullif(trim(coalesce(p_referencia,'')), ''), nullif(trim(coalesce(p_nota,'')), ''), v_mesero_id, v_mesero_nombre, v_verif)
    returning id into v_pago;
  update public.pedidos set pago_mesa_id = v_pago where id = any(p_pedido_ids);

  select coalesce(sum(p.total_usd), 0) into v_total
    from public.pedidos p where p.cuenta_id = p_cuenta_id and p.estado <> 'cancelado';
  select coalesce(sum(pm.monto_usd), 0) into v_pagado
    from public.pagos_mesa pm where pm.cuenta_id = p_cuenta_id and pm.verificado;

  return query select v_pago, v_monto, greatest(v_total - v_pagado, 0::numeric);
end;
$function$;

-- ---- cobrar_y_cerrar_mesa: registra el resto; cierra solo si TODO quedo verificado ----
drop function if exists public.cobrar_y_cerrar_mesa(uuid, text, text);
create function public.cobrar_y_cerrar_mesa(p_cuenta_id uuid, p_metodo text, p_referencia text default null)
returns table(pago_id uuid, monto_usd numeric, cerrada boolean)
language plpgsql security definer set search_path to 'public'
as $function$
declare
  v_cta           public.cuentas_mesa%rowtype;
  v_monto         numeric(10,2);
  v_pago          uuid := null;
  v_mesero_id     uuid;
  v_mesero_nombre text;
  v_verif         boolean;
  v_cerrar        boolean;
begin
  select * into v_cta from public.cuentas_mesa where id = p_cuenta_id for update;
  if not found or v_cta.estado = 'cerrada' then raise exception 'La cuenta no esta disponible'; end if;
  if not (fn_es_staff_de(v_cta.restaurante_id) or fn_es_admin()) then raise exception 'Sin permiso'; end if;
  if p_metodo not in ('pago_movil','efectivo','transferencia','zelle','punto_venta') then raise exception 'Metodo de pago invalido'; end if;
  v_verif := (p_metodo = 'efectivo') or fn_es_dueno_de(v_cta.restaurante_id) or fn_es_admin();

  select coalesce(sum(s.total_usd), 0) into v_monto
    from (
      select p.total_usd from public.pedidos p
       where p.cuenta_id = p_cuenta_id and p.pago_mesa_id is null and p.estado <> 'cancelado'
       for update
    ) s;

  select pf.id, coalesce(nullif(trim(pf.nombre), ''), 'Mesero')
    into v_mesero_id, v_mesero_nombre from public.perfiles pf where pf.id = auth.uid();

  if v_monto > 0 then
    insert into public.pagos_mesa (cuenta_id, restaurante_id, monto_usd, metodo, referencia, mesero_id, mesero_nombre, verificado)
      values (p_cuenta_id, v_cta.restaurante_id, v_monto, p_metodo, nullif(trim(coalesce(p_referencia, '')), ''), v_mesero_id, v_mesero_nombre, v_verif)
      returning id into v_pago;
    update public.pedidos set pago_mesa_id = v_pago
      where cuenta_id = p_cuenta_id and pago_mesa_id is null and estado <> 'cancelado';
  end if;

  -- Cierra solo si todo quedo pagado Y verificado (el electronico del mesero no).
  v_cerrar := fn_mesa_toda_pagada(p_cuenta_id);
  if v_cerrar then
    update public.cuentas_mesa set estado = 'cerrada', cerrada_en = now() where id = p_cuenta_id;
  end if;

  return query select v_pago, v_monto, v_cerrar;
end;
$function$;
grant execute on function public.cobrar_y_cerrar_mesa(uuid, text, text) to authenticated;

-- ---- verificar_pago_cliente: SOLO dueño; al confirmar, si todo quedo pagado, libera ----
create or replace function public.verificar_pago_cliente(p_pago_id uuid, p_ok boolean)
returns void
language plpgsql security definer set search_path to 'public'
as $function$
declare v_rest uuid; v_verif boolean; v_cuenta uuid;
begin
  select restaurante_id, verificado, cuenta_id into v_rest, v_verif, v_cuenta from public.pagos_mesa where id = p_pago_id;
  if v_rest is null then raise exception 'Pago no existe'; end if;
  if not (fn_es_dueno_de(v_rest) or fn_es_admin()) then
    raise exception 'Solo el panel (dueño) verifica los pagos';
  end if;
  if v_verif then raise exception 'Ese pago ya estaba verificado'; end if;
  if p_ok then
    update public.pagos_mesa set verificado = true where id = p_pago_id;
    -- Si con esto la mesa quedo toda pagada y verificada, se libera.
    if fn_mesa_toda_pagada(v_cuenta) then
      update public.cuentas_mesa set estado = 'cerrada', cerrada_en = now() where id = v_cuenta and estado <> 'cerrada';
    end if;
  else
    update public.pedidos set pago_mesa_id = null where pago_mesa_id = p_pago_id;
    delete from public.pagos_mesa where id = p_pago_id;
  end if;
end;
$function$;
