-- =====================================================
-- Atomic Event Registration
--
-- Problem: Registering for an event requires multiple
-- queries (check capacity, validate age, count registrations,
-- insert). Race conditions can over-register an event.
--
-- Fix:
--  A) Ensure member_id FK column exists on event_registrations
--     (idempotent — may already exist from 20260416000006).
--  B) Single transactional PG function that locks the event
--     row, validates capacity/age, and inserts with the
--     correct status (confirmed vs waitlisted).
-- =====================================================

-- A) Add member_id column if not already present
ALTER TABLE public.event_registrations
  ADD COLUMN IF NOT EXISTS member_id uuid
  REFERENCES public.members(id) ON DELETE SET NULL;

-- Ensure index exists (idempotent)
CREATE INDEX IF NOT EXISTS ix_event_registrations_member
  ON public.event_registrations(member_id)
  WHERE member_id IS NOT NULL;

-- The status CHECK constraint already includes 'waitlisted' in the
-- original schema: check (status in ('pending','confirmed','waitlisted','cancelled'))
-- No constraint modification needed.

-- B) Atomic registration function
CREATE OR REPLACE FUNCTION public.register_for_event(
  p_event_id uuid,
  p_member_id uuid DEFAULT NULL,
  p_first_name text DEFAULT NULL,
  p_last_name text DEFAULT NULL,
  p_email text DEFAULT NULL,
  p_phone text DEFAULT NULL,
  p_date_of_birth date DEFAULT NULL,
  p_parent_name text DEFAULT NULL,
  p_parent_phone text DEFAULT NULL
)
RETURNS jsonb
LANGUAGE plpgsql
SECURITY DEFINER
SET search_path = ''
AS $$
DECLARE
  v_event record;
  v_reg_count bigint;
  v_status text;
  v_age integer;
  v_registration_id uuid;
BEGIN
  -- 1. Lock the event row to prevent concurrent registration races
  SELECT * INTO v_event
  FROM public.events
  WHERE id = p_event_id
  FOR UPDATE;

  IF v_event IS NULL THEN
    RAISE EXCEPTION 'Event % not found', p_event_id
      USING ERRCODE = 'P0002';
  END IF;

  -- 2. Validate event status is open for registration
  IF v_event.status != 'open' THEN
    RAISE EXCEPTION 'Event is not open for registration (current status: %)', v_event.status
      USING ERRCODE = 'P0001';
  END IF;

  -- 3. Check registration deadline hasn't passed
  IF v_event.registration_deadline IS NOT NULL AND v_event.registration_deadline < current_date THEN
    RAISE EXCEPTION 'Registration deadline (%) has passed', v_event.registration_deadline
      USING ERRCODE = 'P0001';
  END IF;

  -- 4. Age validation: calculate age at event_date if date_of_birth provided
  IF p_date_of_birth IS NOT NULL THEN
    v_age := extract(year FROM age(v_event.event_date, p_date_of_birth))::integer;

    IF v_event.min_age IS NOT NULL AND v_age < v_event.min_age THEN
      RAISE EXCEPTION 'Participant age (%) is below the minimum age (%) for this event', v_age, v_event.min_age
        USING ERRCODE = 'P0001';
    END IF;

    IF v_event.max_age IS NOT NULL AND v_age > v_event.max_age THEN
      RAISE EXCEPTION 'Participant age (%) exceeds the maximum age (%) for this event', v_age, v_event.max_age
        USING ERRCODE = 'P0001';
    END IF;
  END IF;

  -- 5. Count confirmed + pending registrations
  SELECT count(*) INTO v_reg_count
  FROM public.event_registrations
  WHERE event_id = p_event_id
    AND status IN ('confirmed', 'pending');

  -- 6. Determine status based on capacity
  IF v_event.capacity IS NOT NULL AND v_reg_count >= v_event.capacity THEN
    v_status := 'waitlisted';
  ELSE
    v_status := 'confirmed';
  END IF;

  -- 7. Insert the registration
  INSERT INTO public.event_registrations (
    event_id,
    member_id,
    first_name,
    last_name,
    email,
    phone,
    date_of_birth,
    parent_name,
    parent_phone,
    status
  ) VALUES (
    p_event_id,
    p_member_id,
    p_first_name,
    p_last_name,
    p_email,
    p_phone,
    p_date_of_birth,
    p_parent_name,
    p_parent_phone,
    v_status
  )
  RETURNING id INTO v_registration_id;

  -- 8. Return result
  RETURN jsonb_build_object(
    'registration_id', v_registration_id,
    'status', v_status
  );
END;
$$;

GRANT EXECUTE ON FUNCTION public.register_for_event(uuid, uuid, text, text, text, text, date, text, text) TO authenticated;
GRANT EXECUTE ON FUNCTION public.register_for_event(uuid, uuid, text, text, text, text, date, text, text) TO service_role;
