-- =====================================================
-- Module Statistics RPCs
--
-- A) Course statistics — counts per status, participants,
--    average occupancy, total revenue
-- B) Event statistics — counts, upcoming/past, registrations,
--    average occupancy
-- C) Booking statistics — counts, revenue, avg stay,
--    occupancy rate for a date range
-- D) Event registration counts — batch lookup replacing
--    N+1 JS iteration
-- =====================================================

-- -------------------------------------------------------
-- A) Course statistics
-- -------------------------------------------------------

CREATE OR REPLACE FUNCTION public.get_course_statistics(p_account_id uuid)
RETURNS TABLE (
  total_courses bigint,
  open_courses bigint,
  running_courses bigint,
  completed_courses bigint,
  cancelled_courses bigint,
  total_participants bigint,
  total_waitlisted bigint,
  avg_occupancy_rate numeric,
  total_revenue numeric
)
LANGUAGE plpgsql
STABLE
SECURITY DEFINER
SET search_path = ''
AS $$
BEGIN
  -- Access check
  IF NOT public.has_role_on_account(p_account_id) THEN
    RAISE EXCEPTION 'Access denied'
      USING ERRCODE = '42501';
  END IF;

  RETURN QUERY
  WITH course_stats AS (
    SELECT
      count(*)::bigint AS total_courses,
      count(*) FILTER (WHERE c.status = 'open')::bigint AS open_courses,
      count(*) FILTER (WHERE c.status = 'running')::bigint AS running_courses,
      count(*) FILTER (WHERE c.status = 'completed')::bigint AS completed_courses,
      count(*) FILTER (WHERE c.status = 'cancelled')::bigint AS cancelled_courses
    FROM public.courses c
    WHERE c.account_id = p_account_id
  ),
  participant_stats AS (
    SELECT
      count(*) FILTER (WHERE cp.status = 'enrolled')::bigint AS total_participants,
      count(*) FILTER (WHERE cp.status = 'waitlisted')::bigint AS total_waitlisted
    FROM public.course_participants cp
    JOIN public.courses c ON c.id = cp.course_id
    WHERE c.account_id = p_account_id
  ),
  occupancy_stats AS (
    SELECT
      ROUND(
        AVG(
          CASE WHEN c.capacity > 0 THEN
            enrolled_ct::numeric / c.capacity * 100
          ELSE 0 END
        ),
        1
      ) AS avg_occupancy_rate
    FROM public.courses c
    LEFT JOIN LATERAL (
      SELECT count(*)::numeric AS enrolled_ct
      FROM public.course_participants cp
      WHERE cp.course_id = c.id AND cp.status = 'enrolled'
    ) ec ON true
    WHERE c.account_id = p_account_id
      AND c.status != 'cancelled'
  ),
  revenue_stats AS (
    SELECT
      COALESCE(SUM(c.fee * enrolled_ct), 0)::numeric AS total_revenue
    FROM public.courses c
    LEFT JOIN LATERAL (
      SELECT count(*)::numeric AS enrolled_ct
      FROM public.course_participants cp
      WHERE cp.course_id = c.id AND cp.status IN ('enrolled', 'completed')
    ) ec ON true
    WHERE c.account_id = p_account_id
      AND c.status != 'cancelled'
  )
  SELECT
    cs.total_courses,
    cs.open_courses,
    cs.running_courses,
    cs.completed_courses,
    cs.cancelled_courses,
    ps.total_participants,
    ps.total_waitlisted,
    os.avg_occupancy_rate,
    rs.total_revenue
  FROM course_stats cs
  CROSS JOIN participant_stats ps
  CROSS JOIN occupancy_stats os
  CROSS JOIN revenue_stats rs;
END;
$$;

GRANT EXECUTE ON FUNCTION public.get_course_statistics(uuid) TO authenticated;
GRANT EXECUTE ON FUNCTION public.get_course_statistics(uuid) TO service_role;

-- -------------------------------------------------------
-- B) Event statistics
-- -------------------------------------------------------

CREATE OR REPLACE FUNCTION public.get_event_statistics(p_account_id uuid)
RETURNS TABLE (
  total_events bigint,
  upcoming_events bigint,
  past_events bigint,
  total_registrations bigint,
  avg_occupancy_rate numeric
)
LANGUAGE plpgsql
STABLE
SECURITY DEFINER
SET search_path = ''
AS $$
BEGIN
  -- Access check
  IF NOT public.has_role_on_account(p_account_id) THEN
    RAISE EXCEPTION 'Access denied'
      USING ERRCODE = '42501';
  END IF;

  RETURN QUERY
  WITH event_counts AS (
    SELECT
      count(*)::bigint AS total_events,
      count(*) FILTER (
        WHERE e.event_date >= current_date
          AND e.status NOT IN ('cancelled', 'completed')
      )::bigint AS upcoming_events,
      count(*) FILTER (
        WHERE e.event_date < current_date
          OR e.status IN ('completed')
      )::bigint AS past_events
    FROM public.events e
    WHERE e.account_id = p_account_id
  ),
  reg_counts AS (
    SELECT count(*)::bigint AS total_registrations
    FROM public.event_registrations er
    JOIN public.events e ON e.id = er.event_id
    WHERE e.account_id = p_account_id
      AND er.status IN ('confirmed', 'pending')
  ),
  occupancy AS (
    SELECT
      ROUND(
        AVG(
          CASE WHEN e.capacity IS NOT NULL AND e.capacity > 0 THEN
            reg_ct::numeric / e.capacity * 100
          ELSE NULL END
        ),
        1
      ) AS avg_occupancy_rate
    FROM public.events e
    LEFT JOIN LATERAL (
      SELECT count(*)::numeric AS reg_ct
      FROM public.event_registrations er
      WHERE er.event_id = e.id AND er.status IN ('confirmed', 'pending')
    ) rc ON true
    WHERE e.account_id = p_account_id
      AND e.status != 'cancelled'
  )
  SELECT
    ec.total_events,
    ec.upcoming_events,
    ec.past_events,
    rc.total_registrations,
    COALESCE(occ.avg_occupancy_rate, 0)::numeric
  FROM event_counts ec
  CROSS JOIN reg_counts rc
  CROSS JOIN occupancy occ;
END;
$$;

GRANT EXECUTE ON FUNCTION public.get_event_statistics(uuid) TO authenticated;
GRANT EXECUTE ON FUNCTION public.get_event_statistics(uuid) TO service_role;

-- -------------------------------------------------------
-- C) Booking statistics
-- -------------------------------------------------------

CREATE OR REPLACE FUNCTION public.get_booking_statistics(
  p_account_id uuid,
  p_from date DEFAULT NULL,
  p_to date DEFAULT NULL
)
RETURNS TABLE (
  total_bookings bigint,
  active_bookings bigint,
  checked_in_count bigint,
  total_revenue numeric,
  avg_stay_nights numeric,
  occupancy_rate numeric
)
LANGUAGE plpgsql
STABLE
SECURITY DEFINER
SET search_path = ''
AS $$
DECLARE
  v_from date;
  v_to date;
  v_total_rooms bigint;
  v_total_room_nights numeric;
  v_booked_room_nights numeric;
BEGIN
  -- Access check
  IF NOT public.has_role_on_account(p_account_id) THEN
    RAISE EXCEPTION 'Access denied'
      USING ERRCODE = '42501';
  END IF;

  -- Default date range: current month
  v_from := COALESCE(p_from, date_trunc('month', current_date)::date);
  v_to   := COALESCE(p_to, (date_trunc('month', current_date) + interval '1 month' - interval '1 day')::date);

  -- Calculate total available room-nights
  SELECT count(*)::bigint INTO v_total_rooms
  FROM public.rooms
  WHERE account_id = p_account_id
    AND is_active = true;

  v_total_room_nights := v_total_rooms::numeric * (v_to - v_from + 1);

  -- Calculate booked room-nights in range (non-cancelled)
  SELECT COALESCE(SUM(
    LEAST(b.check_out, v_to + 1) - GREATEST(b.check_in, v_from)
  ), 0)::numeric
  INTO v_booked_room_nights
  FROM public.bookings b
  WHERE b.account_id = p_account_id
    AND b.status NOT IN ('cancelled', 'no_show')
    AND b.check_in <= v_to
    AND b.check_out > v_from;

  RETURN QUERY
  SELECT
    count(*)::bigint AS total_bookings,
    count(*) FILTER (WHERE b.status IN ('confirmed', 'checked_in'))::bigint AS active_bookings,
    count(*) FILTER (WHERE b.status = 'checked_in')::bigint AS checked_in_count,
    COALESCE(SUM(b.total_price) FILTER (WHERE b.status != 'cancelled'), 0)::numeric AS total_revenue,
    ROUND(
      COALESCE(AVG((b.check_out - b.check_in)::numeric) FILTER (WHERE b.status != 'cancelled'), 0),
      1
    ) AS avg_stay_nights,
    CASE WHEN v_total_room_nights > 0 THEN
      ROUND(v_booked_room_nights / v_total_room_nights * 100, 1)
    ELSE 0 END AS occupancy_rate
  FROM public.bookings b
  WHERE b.account_id = p_account_id
    AND b.check_in <= v_to
    AND b.check_out > v_from;
END;
$$;

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

-- -------------------------------------------------------
-- D) Event registration counts (batch lookup)
-- -------------------------------------------------------

CREATE OR REPLACE FUNCTION public.get_event_registration_counts(p_event_ids uuid[])
RETURNS TABLE (event_id uuid, registration_count bigint)
LANGUAGE sql
STABLE
SECURITY DEFINER
SET search_path = ''
AS $$
  SELECT
    er.event_id,
    count(*)::bigint AS registration_count
  FROM public.event_registrations er
  WHERE er.event_id = ANY(p_event_ids)
    AND er.status IN ('confirmed', 'pending')
  GROUP BY er.event_id;
$$;

GRANT EXECUTE ON FUNCTION public.get_event_registration_counts(uuid[]) TO authenticated;
GRANT EXECUTE ON FUNCTION public.get_event_registration_counts(uuid[]) TO service_role;
