-- Crea tabla exclusiva de H.C.E. para abonos vinculados a recibos.
-- No modifica public.pagos, el modulo Recibos ni las politicas RLS de D&G.
BEGIN;

CREATE TABLE IF NOT EXISTS public.hce_abonos (
  id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
  recibo_id bigint NOT NULL,
  recibo_numero_snapshot text,
  recibo_numero_guia_snapshot text,
  recibo_cliente_snapshot text,
  recibo_monto_snapshot numeric,
  cliente text NOT NULL DEFAULT '',
  monto numeric NOT NULL CHECK (monto > 0),
  moneda text NOT NULL DEFAULT 'USD',
  metodo_pago text NOT NULL CHECK (
    metodo_pago IN (
      'Efectivo',
      'Zelle',
      'Transferencia bancaria',
      'Tarjeta',
      'PayPal',
      'Otro'
    )
  ),
  referencia text,
  fecha date NOT NULL,
  notas text,
  created_at timestamptz NOT NULL DEFAULT now(),
  creado_por text
);

CREATE INDEX IF NOT EXISTS hce_abonos_recibo_id_fecha_idx
  ON public.hce_abonos (recibo_id, fecha DESC);

-- RLS habilitado. Sin politicas permissivas para anon/authenticated,
-- el acceso directo desde cliente queda bloqueado. El servidor HCE usa
-- service_role, que bypass RLS.
ALTER TABLE public.hce_abonos ENABLE ROW LEVEL SECURITY;

-- Permisos de tabla: solo service_role puede leer/insertar. Los demas
-- roles quedan bloqueados tanto por RLS como por la falta de grants.
REVOKE ALL ON public.hce_abonos FROM PUBLIC, anon, authenticated;
GRANT SELECT, INSERT ON public.hce_abonos TO service_role;

-- La secuencia de identidad generada por la columna id tambien debe ser
-- usable por service_role para poder insertar filas.
GRANT USAGE, SELECT ON SEQUENCE public.hce_abonos_id_seq TO service_role;

-- Trigger: evita sobrepagos concurrentes (FOR UPDATE sobre hce_recibos),
-- guarda snapshots del recibo y hace inmutables los abonos.
CREATE OR REPLACE FUNCTION public.hce_guardar_abono()
RETURNS trigger
LANGUAGE plpgsql
VOLATILE
SECURITY DEFINER
SET search_path = pg_catalog
SET row_security = off
AS $function$
DECLARE
  recibo_actual public.hce_recibos%ROWTYPE;
  total_abonado numeric;
BEGIN
  IF TG_OP = 'DELETE' THEN
    RAISE EXCEPTION 'Los abonos no se pueden eliminar'
      USING ERRCODE = '23514';
  END IF;

  IF TG_OP = 'UPDATE' THEN
    RAISE EXCEPTION 'Los abonos no se pueden modificar'
      USING ERRCODE = '23514';
  END IF;

  -- INSERT
  IF NEW.recibo_id IS NULL THEN
    RAISE EXCEPTION 'El abono debe estar vinculado a un recibo'
      USING ERRCODE = '23514';
  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 r.*
    INTO recibo_actual
    FROM public.hce_recibos AS r
   WHERE r.id = NEW.recibo_id
   FOR UPDATE;

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

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

  SELECT COALESCE(pg_catalog.sum(a.monto), 0)
    INTO total_abonado
    FROM public.hce_abonos AS a
   WHERE a.recibo_id = NEW.recibo_id;

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

  NEW.recibo_numero_snapshot := recibo_actual.numero;
  NEW.recibo_numero_guia_snapshot := recibo_actual.numero_guia;
  NEW.recibo_cliente_snapshot := recibo_actual.cliente;
  NEW.recibo_monto_snapshot := recibo_actual.monto;

  RETURN NEW;
END;
$function$;

ALTER FUNCTION public.hce_guardar_abono() OWNER TO postgres;

DROP TRIGGER IF EXISTS hce_abonos_guardar_abono ON public.hce_abonos;

CREATE TRIGGER hce_abonos_guardar_abono
BEFORE INSERT OR UPDATE OR DELETE
ON public.hce_abonos
FOR EACH ROW
EXECUTE FUNCTION public.hce_guardar_abono();

REVOKE ALL ON FUNCTION public.hce_guardar_abono() FROM PUBLIC;
GRANT EXECUTE ON FUNCTION public.hce_guardar_abono()
  TO service_role;

COMMIT;
