-- =====================================================
-- Atomic Course Enrollment
--
-- Problem: Enrolling a participant in a course requires
-- multiple queries (check capacity, count enrolled, insert).
-- Race conditions can over-enroll a course.
--
-- Fix: Single transactional PG function that locks the
-- course row, validates capacity, and inserts with the
-- correct status (enrolled vs waitlisted).
-- =====================================================

CREATE OR REPLACE FUNCTION public.enroll_course_participant(
  p_course_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
)
RETURNS jsonb
LANGUAGE plpgsql
SECURITY DEFINER
SET search_path = ''
AS $$
DECLARE
  v_course record;
  v_enrolled_count bigint;
  v_status public.enrollment_status;
  v_waitlist_position bigint;
  v_participant_id uuid;
BEGIN
  -- 1. Lock the course row to prevent concurrent enrollment races
  SELECT * INTO v_course
  FROM public.courses
  WHERE id = p_course_id
  FOR UPDATE;

  IF v_course IS NULL THEN
    RAISE EXCEPTION 'Course % not found', p_course_id
      USING ERRCODE = 'P0002';
  END IF;

  -- 2. Validate course status is open for enrollment
  IF v_course.status != 'open' THEN
    RAISE EXCEPTION 'Course is not open for enrollment (current status: %)', v_course.status
      USING ERRCODE = 'P0001';
  END IF;

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

  -- 4. Count currently enrolled participants
  SELECT count(*) INTO v_enrolled_count
  FROM public.course_participants
  WHERE course_id = p_course_id
    AND status = 'enrolled';

  -- 5. Determine status based on capacity
  IF v_enrolled_count >= v_course.capacity THEN
    v_status := 'waitlisted';
  ELSE
    v_status := 'enrolled';
  END IF;

  -- 6. Insert the participant
  INSERT INTO public.course_participants (
    course_id,
    member_id,
    first_name,
    last_name,
    email,
    phone,
    status,
    enrolled_at
  ) VALUES (
    p_course_id,
    p_member_id,
    p_first_name,
    p_last_name,
    p_email,
    p_phone,
    v_status,
    now()
  )
  RETURNING id INTO v_participant_id;

  -- 7. Calculate waitlist position if waitlisted
  IF v_status = 'waitlisted' THEN
    SELECT count(*) INTO v_waitlist_position
    FROM public.course_participants
    WHERE course_id = p_course_id
      AND status = 'waitlisted';
  END IF;

  -- 8. Return result
  RETURN jsonb_build_object(
    'participant_id', v_participant_id,
    'status', v_status::text,
    'waitlist_position', CASE WHEN v_status = 'waitlisted' THEN v_waitlist_position ELSE NULL END
  );
END;
$$;

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