-- TODO PAGO ELECTRONICO LLEVA SU REFERENCIA.
--
-- Decision de Gilberto (2026-07-30): "no puede ser opcional, porque es parte
-- del registro fiscal". Un cobro electronico sin referencia no se puede
-- rastrear: no se cuadra contra el estado de cuenta del banco, no se atiende
-- un reclamo de doble cobro, y en el libro queda un hueco.
--
-- Se descubrio revisando el PUNTO DE VENTA (el datafono nunca pidio su numero
-- de lote), pero el hueco era mas grande: en produccion habia 19 pagos moviles
-- de 67 guardados sin referencia. La regla se aplica pareja.
--
-- ⚠ La regla vive en la BASE, no en la pantalla: una validacion que solo esta
-- en el navegador se la salta cualquiera llamando la RPC directo, y esto es
-- registro fiscal.
--
-- Lo viejo NO se toca (nada de CHECK sobre las filas que ya existen): lo que
-- ya paso, paso. Esto aplica de aqui en adelante.

begin;

-- Un solo sitio que define que pago necesita referencia. Efectivo no (no hay
-- nada que rastrear) y 'perdida' tampoco (es una marca interna de la mesa).
create or replace function public.fn_pago_necesita_referencia(p_metodo text)
returns boolean
language sql immutable
as $$
  select coalesce(p_metodo, '') in ('pago_movil', 'transferencia', 'zelle', 'punto_venta');
$$;

/**
 * Revisa la referencia de un cobro y explica en cristiano que falta.
 * El minimo de 4 caracteres es el mismo que ya exigia la pantalla de mesas:
 * frena el dedazo y el "1" de relleno, sin pelear con los formatos de cada
 * banco (unos dan 6 digitos, otros 12, el datafono da lote y referencia).
 */
create or replace function public.fn_exigir_referencia(p_metodo text, p_referencia text)
returns text
language plpgsql immutable
as $$
declare v_ref text := nullif(trim(coalesce(p_referencia, '')), '');
begin
  if not public.fn_pago_necesita_referencia(p_metodo) then
    return null; -- efectivo y demas: sin referencia y sin problema
  end if;
  if v_ref is null then
    raise exception 'Falta el numero de referencia del pago (%). Es parte del registro fiscal.',
      case p_metodo when 'punto_venta' then 'el lote o referencia del datafono' else 'el que dio el banco' end;
  end if;
  if length(v_ref) < 4 then
    raise exception 'Esa referencia es muy corta: copia el numero completo del comprobante.';
  end if;
  return v_ref;
end $$;

-- ===== Las tres puertas por donde entra un cobro de mesa =====

create or replace function public.cobrar_y_cerrar_mesa(p_cuenta_id uuid, p_metodo text, p_referencia text default null::text, p_propina numeric default 0, p_comprobante_url text default null::text)
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_propina       numeric(10,2) := round(greatest(coalesce(p_propina, 0), 0), 2);
  v_pago          uuid := null;
  v_mesero_id     uuid;
  v_mesero_nombre text;
  v_verif         boolean;
  v_cerrar        boolean;
  v_ref           text;
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;
  -- La referencia se exige ANTES de tocar nada: si falta, no se cobra a medias.
  v_ref := public.fn_exigir_referencia(p_metodo, p_referencia);
  -- El punto de venta lo aprobo el banco en el momento: no va a la cola de
  -- "pagos por revisar" como el pago movil, que si hay que ir a verificar.
  v_verif := (p_metodo in ('efectivo', 'punto_venta')) 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();

  -- Inserta el pago si hay comida por cobrar O si dejaron propina (propina sola).
  if v_monto > 0 or v_propina > 0 then
    insert into public.pagos_mesa (cuenta_id, restaurante_id, monto_usd, propina_usd, metodo, referencia, comprobante_url, mesero_id, mesero_nombre, verificado)
      values (p_cuenta_id, v_cta.restaurante_id, v_monto, v_propina, p_metodo, v_ref, nullif(trim(coalesce(p_comprobante_url, '')), ''), 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;

  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$;

commit;
