-- HCE Pagos por recibo — migración corregida para revisión
-- No reasigna pagos históricos y NO cambia políticas RLS existentes.
-- Revisar en un entorno de prueba antes de ejecutar en producción.

BEGIN;

ALTER TABLE public.pagos
  ADD COLUMN IF NOT EXISTS recibo_id bigint,
  ADD COLUMN IF NOT EXISTS recibo_numero_snapshot text,
  ADD COLUMN IF NOT EXISTS recibo_numero_guia_snapshot text,
  ADD COLUMN IF NOT EXISTS recibo_cliente_snapshot text,
  ADD COLUMN IF NOT EXISTS recibo_monto_snapshot numeric;

CREATE INDEX IF NOT EXISTS pagos_recibo_id_fecha_idx
  ON public.pagos (recibo_id, fecha DESC);

-- Trigger: valida cada abono, serializa por recibo y conserva snapshots.
CREATE OR REPLACE FUNCTION public.hce_guardar_abono_por_recibo()
RETURNS trigger
LANGUAGE plpgsql
VOLATILE
SECURITY DEFINER
SET search_path = pg_catalog
SET row_security = off
AS $function$
DECLARE
  r record;
  total_abonado numeric;
BEGIN
  IF TG_OP = 'DELETE' THEN
    IF OLD.recibo_id IS NOT NULL THEN
      RAISE EXCEPTION 'Los abonos vinculados a recibos no se pueden eliminar'
        USING ERRCODE = '23514';
    END IF;
    RETURN OLD;
  END IF;

  IF TG_OP = 'UPDATE' THEN
    IF OLD.recibo_id IS NOT NULL OR NEW.recibo_id IS NOT NULL THEN
      RAISE EXCEPTION 'Los abonos vinculados a recibos no se pueden modificar'
        USING ERRCODE = '23514';
    END IF;
    RETURN NEW;
  END IF;

  IF NEW.recibo_id IS NULL THEN
    RETURN NEW;
  END IF;

  IF NEW.monto IS NULL OR NEW.monto <= 0 THEN
    RAISE EXCEPTION 'El importe del abono debe ser mayor que cero'
      USING ERRCODE = '23514';
  END IF;

  SELECT id, numero, numero_guia, cliente, monto
    INTO r
    FROM public.hce_recibos
   WHERE id = NEW.recibo_id
   FOR UPDATE;

  IF NOT FOUND THEN
    RAISE EXCEPTION 'No existe el recibo indicado'
      USING ERRCODE = '23503';
  END IF;

  IF r.monto IS NULL OR r.monto <= 0 THEN
    RAISE EXCEPTION 'El recibo no tiene un monto válido para recibir abonos'
      USING ERRCODE = '23514';
  END IF;

  SELECT COALESCE(sum(monto), 0)
    INTO total_abonado
    FROM public.pagos
   WHERE recibo_id = NEW.recibo_id;

  IF total_abonado + NEW.monto > r.monto THEN
    RAISE EXCEPTION 'El abono supera el saldo pendiente del recibo'
      USING ERRCODE = '23514';
  END IF;

  NEW.recibo_numero_snapshot := r.numero;
  NEW.recibo_numero_guia_snapshot := r.numero_guia;
  NEW.recibo_cliente_snapshot := r.cliente;
  NEW.recibo_monto_snapshot := r.monto;
  RETURN NEW;
END;
$function$;

ALTER FUNCTION public.hce_guardar_abono_por_recibo() OWNER TO postgres;

DROP TRIGGER IF EXISTS pagos_guardar_abono_por_recibo ON public.pagos;
CREATE TRIGGER pagos_guardar_abono_por_recibo
BEFORE INSERT OR UPDATE OR DELETE ON public.pagos
FOR EACH ROW EXECUTE FUNCTION public.hce_guardar_abono_por_recibo();

REVOKE ALL ON FUNCTION public.hce_guardar_abono_por_recibo() FROM PUBLIC;
GRANT EXECUTE ON FUNCTION public.hce_guardar_abono_por_recibo()
  TO anon, authenticated, service_role;

-- Evita reducir el monto de un recibo por debajo de lo ya abonado.
-- No cambia el flujo de Recibos cuando el monto sigue siendo válido.
CREATE OR REPLACE FUNCTION public.hce_validar_monto_recibo_con_abonos()
RETURNS trigger
LANGUAGE plpgsql
VOLATILE
SECURITY DEFINER
SET search_path = pg_catalog
SET row_security = off
AS $function$
DECLARE
  total_abonado numeric;
BEGIN
  IF NEW.monto IS NOT DISTINCT FROM OLD.monto THEN
    RETURN NEW;
  END IF;

  SELECT COALESCE(sum(p.monto), 0)
    INTO total_abonado
    FROM public.pagos p
   WHERE p.recibo_id = OLD.id;

  IF NEW.monto IS NULL OR NEW.monto < total_abonado THEN
    RAISE EXCEPTION 'El monto del recibo no puede ser menor que los abonos registrados (%).', total_abonado
      USING ERRCODE = '23514';
  END IF;

  RETURN NEW;
END;
$function$;

ALTER FUNCTION public.hce_validar_monto_recibo_con_abonos() OWNER TO postgres;
DROP TRIGGER IF EXISTS hce_validar_monto_recibo_con_abonos ON public.hce_recibos;
CREATE TRIGGER hce_validar_monto_recibo_con_abonos
BEFORE UPDATE OF monto ON public.hce_recibos
FOR EACH ROW EXECUTE FUNCTION public.hce_validar_monto_recibo_con_abonos();

REVOKE ALL ON FUNCTION public.hce_validar_monto_recibo_con_abonos() FROM PUBLIC;
GRANT EXECUTE ON FUNCTION public.hce_validar_monto_recibo_con_abonos()
  TO anon, authenticated, service_role;

COMMIT;

-- IMPORTANTE:
-- Esta migración conserva las políticas RLS actuales deliberadamente.
-- Antes de producción hay que revisar si esas políticas permiten INSERT/SELECT
-- de pagos vinculados a recibos desde clientes públicos y limitar el endpoint
-- del ERP a usuarios autorizados. No se crean políticas amplias nuevas aquí.
