-- =====================================================
-- Atomic Application Workflow
-- Replaces multi-query approve/reject in api.ts with
-- single transactional PG functions.
-- =====================================================

-- approve_application: atomically creates a member from an application
CREATE OR REPLACE FUNCTION public.approve_application(
  p_application_id uuid,
  p_user_id uuid
)
RETURNS uuid
LANGUAGE plpgsql
SECURITY DEFINER
SET search_path = ''
AS $$
DECLARE
  v_app record;
  v_member_id uuid;
  v_member_number text;
BEGIN
  -- 1. Fetch and lock the application
  SELECT * INTO v_app
  FROM public.membership_applications
  WHERE id = p_application_id
  FOR UPDATE;

  IF v_app IS NULL THEN
    RAISE EXCEPTION 'Application % not found', p_application_id
      USING ERRCODE = 'P0002';
  END IF;

  -- Authorization: caller must have write permission on this account
  IF NOT public.has_permission(auth.uid(), v_app.account_id, 'members.write'::public.app_permissions) THEN
    RAISE EXCEPTION 'Access denied to account %', v_app.account_id
      USING ERRCODE = '42501';
  END IF;

  IF v_app.status NOT IN ('submitted', 'review') THEN
    RAISE EXCEPTION 'Application is not in a reviewable state (current: %)', v_app.status
      USING ERRCODE = 'P0001';
  END IF;

  -- 2. Generate next member number
  SELECT LPAD(
    (COALESCE(
      MAX(CASE WHEN member_number ~ '^\d+$' THEN member_number::integer ELSE 0 END),
      0
    ) + 1)::text,
    4, '0'
  ) INTO v_member_number
  FROM public.members
  WHERE account_id = v_app.account_id;

  -- 3. Create the member
  INSERT INTO public.members (
    account_id,
    member_number,
    first_name,
    last_name,
    email,
    phone,
    street,
    postal_code,
    city,
    date_of_birth,
    status,
    entry_date,
    created_by,
    updated_by
  ) VALUES (
    v_app.account_id,
    v_member_number,
    v_app.first_name,
    v_app.last_name,
    v_app.email,
    v_app.phone,
    v_app.street,
    v_app.postal_code,
    v_app.city,
    v_app.date_of_birth,
    'active'::public.membership_status,
    current_date,
    auth.uid(),
    auth.uid()
  )
  RETURNING id INTO v_member_id;

  -- 4. Mark application as approved
  UPDATE public.membership_applications
  SET
    status = 'approved'::public.application_status,
    reviewed_by = auth.uid(),
    reviewed_at = now(),
    member_id = v_member_id,
    updated_at = now()
  WHERE id = p_application_id;

  RETURN v_member_id;
END;
$$;

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

-- reject_application: atomically rejects an application with notes
CREATE OR REPLACE FUNCTION public.reject_application(
  p_application_id uuid,
  p_user_id uuid,
  p_review_notes text DEFAULT NULL
)
RETURNS void
LANGUAGE plpgsql
SECURITY DEFINER
SET search_path = ''
AS $$
DECLARE
  v_app record;
BEGIN
  -- Fetch and lock the application
  SELECT * INTO v_app
  FROM public.membership_applications
  WHERE id = p_application_id
  FOR UPDATE;

  IF v_app IS NULL THEN
    RAISE EXCEPTION 'Application % not found', p_application_id
      USING ERRCODE = 'P0002';
  END IF;

  -- Authorization: caller must have write permission on this account
  IF NOT public.has_permission(auth.uid(), v_app.account_id, 'members.write'::public.app_permissions) THEN
    RAISE EXCEPTION 'Access denied to account %', v_app.account_id
      USING ERRCODE = '42501';
  END IF;

  IF v_app.status NOT IN ('submitted', 'review') THEN
    RAISE EXCEPTION 'Application is not in a reviewable state (current: %)', v_app.status
      USING ERRCODE = 'P0001';
  END IF;

  UPDATE public.membership_applications
  SET
    status = 'rejected'::public.application_status,
    reviewed_by = auth.uid(),
    reviewed_at = now(),
    review_notes = p_review_notes,
    updated_at = now()
  WHERE id = p_application_id;
END;
$$;

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