-- ============================================================
-- Migration 051: RLS Performance Fixes
-- Addresses all Supabase linter warnings:
--   1. auth_rls_initplan: wrap auth.uid() in (select auth.uid())
--   2. multiple_permissive_policies: consolidate overlapping policies
-- ============================================================

-- ============================================================
-- PART 1: Fix auth_rls_initplan
-- Replace auth.uid() with (select auth.uid()) in ALL RLS policies
-- using a dynamic DO block that reads pg_policies catalog
-- ============================================================

DO $$
DECLARE
  r record;
  qual_new text;
  check_new text;
  cmd_parts text[];
  sql_cmd text;
BEGIN
  FOR r IN
    SELECT
      schemaname,
      tablename,
      policyname,
      permissive,
      roles,
      cmd AS policy_cmd,
      qual,
      with_check
    FROM pg_policies
    WHERE schemaname = 'public'
      AND (
        qual ~ 'auth\.uid\(\)' AND qual !~ '\(\s*select\s+auth\.uid\(\)\s*\)'
        OR
        with_check ~ 'auth\.uid\(\)' AND with_check !~ '\(\s*select\s+auth\.uid\(\)\s*\)'
      )
  LOOP
    -- Replace auth.uid() with (select auth.uid()) in USING clause
    qual_new := r.qual;
    IF qual_new IS NOT NULL THEN
      -- Avoid double-wrapping: only replace bare auth.uid() not already in (select ...)
      qual_new := regexp_replace(qual_new, '(?<!\(select )auth\.uid\(\)', '(select auth.uid())', 'gi');
    END IF;

    -- Replace auth.uid() with (select auth.uid()) in WITH CHECK clause
    check_new := r.with_check;
    IF check_new IS NOT NULL THEN
      check_new := regexp_replace(check_new, '(?<!\(select )auth\.uid\(\)', '(select auth.uid())', 'gi');
    END IF;

    -- Drop old policy
    EXECUTE format('DROP POLICY IF EXISTS %I ON %I.%I',
      r.policyname, r.schemaname, r.tablename);

    -- Build CREATE POLICY command
    sql_cmd := format('CREATE POLICY %I ON %I.%I', r.policyname, r.schemaname, r.tablename);

    -- AS PERMISSIVE/RESTRICTIVE
    IF r.permissive = 'false' THEN
      sql_cmd := sql_cmd || ' AS RESTRICTIVE';
    END IF;

    -- FOR command
    sql_cmd := sql_cmd || ' FOR ' || r.policy_cmd;

    -- TO roles
    sql_cmd := sql_cmd || ' TO ' || array_to_string(r.roles, ', ');

    -- USING clause
    IF qual_new IS NOT NULL THEN
      sql_cmd := sql_cmd || ' USING (' || qual_new || ')';
    END IF;

    -- WITH CHECK clause
    IF check_new IS NOT NULL THEN
      sql_cmd := sql_cmd || ' WITH CHECK (' || check_new || ')';
    END IF;

    EXECUTE sql_cmd;

    RAISE NOTICE 'Fixed initplan: %.% policy "%"', r.schemaname, r.tablename, r.policyname;
  END LOOP;
END;
$$;


-- ============================================================
-- PART 2: Fix multiple_permissive_policies
-- Pattern A: Tables with "Org members can view X" (SELECT) +
--            "Editors can manage X" (FOR ALL) → drop the SELECT policy
--            since FOR ALL already covers SELECT.
-- Pattern B: Tables with "Platform admins full access" (FOR ALL) +
--            "Editors can manage X" (FOR ALL) → merge platform admin
--            check into the editors policy, drop the platform admin policy.
-- Pattern C: Tables with "Org members can view X" (SELECT) +
--            "Members can manage X" (FOR ALL) → same as Pattern A.
-- ============================================================

-- --------------------------------------------------------
-- Pattern A: Drop redundant SELECT-only policies
-- These tables have both "view" (SELECT) and "manage" (FOR ALL)
-- The FOR ALL already covers SELECT, making the view policy redundant
-- --------------------------------------------------------

-- sponsors
DROP POLICY IF EXISTS "Org members can view sponsors" ON public.sponsors;

-- sponsor_deliverables
DROP POLICY IF EXISTS "Org members can view sponsor deliverables" ON public.sponsor_deliverables;

-- sponsor_tiers
DROP POLICY IF EXISTS "Org members can view sponsor tiers" ON public.sponsor_tiers;

-- documents
DROP POLICY IF EXISTS "Org members can view documents" ON public.documents;

-- kanban_boards
DROP POLICY IF EXISTS "Org members can view boards" ON public.kanban_boards;

-- kanban_columns
DROP POLICY IF EXISTS "Org members can view columns" ON public.kanban_columns;

-- kanban_cards
DROP POLICY IF EXISTS "Org members can view cards" ON public.kanban_cards;

-- calendar_events
DROP POLICY IF EXISTS "Org members can view events" ON public.calendar_events;

-- events
DROP POLICY IF EXISTS "Org members can view events" ON public.events;

-- event_members
DROP POLICY IF EXISTS "Org members can view event members" ON public.event_members;

-- event_roles
DROP POLICY IF EXISTS "Org members can view event roles" ON public.event_roles;

-- event_departments
DROP POLICY IF EXISTS "Org members can view event departments" ON public.event_departments;

-- event_member_departments
DROP POLICY IF EXISTS "Org members can view member departments" ON public.event_member_departments;

-- event_task_columns
DROP POLICY IF EXISTS "Org members can view event task columns" ON public.event_task_columns;

-- event_tasks
DROP POLICY IF EXISTS "Org members can view event tasks" ON public.event_tasks;

-- department_dashboards
DROP POLICY IF EXISTS "Org members can view department dashboards" ON public.department_dashboards;

-- dashboard_panels
DROP POLICY IF EXISTS "Org members can view dashboard panels" ON public.dashboard_panels;

-- department_checklists
DROP POLICY IF EXISTS "Org members can view department checklists" ON public.department_checklists;

-- department_checklist_items
DROP POLICY IF EXISTS "Org members can view dept checklist items" ON public.department_checklist_items;

-- department_notes
DROP POLICY IF EXISTS "Org members can view department notes" ON public.department_notes;

-- checklist_items
DROP POLICY IF EXISTS "Org members can view checklist items" ON public.checklist_items;

-- budget_categories
DROP POLICY IF EXISTS "Org members can view budget categories" ON public.budget_categories;

-- budget_items
DROP POLICY IF EXISTS "Org members can view budget items" ON public.budget_items;

-- card_tags: "Users can view card tags" + "Members can manage card tags" (FOR ALL)
DROP POLICY IF EXISTS "Users can view card tags in their orgs" ON public.card_tags;

-- document_locks: "Anyone can delete expired locks" + "Users can delete their own locks"
-- Merge into single DELETE policy
DROP POLICY IF EXISTS "Anyone can delete expired locks" ON public.document_locks;
DROP POLICY IF EXISTS "Users can delete their own locks" ON public.document_locks;
CREATE POLICY "Users can delete locks" ON public.document_locks
  FOR DELETE
  USING (
    user_id = (select auth.uid())
    OR last_heartbeat < (now() - interval '2 minutes')
  );


-- --------------------------------------------------------
-- Pattern B: Merge "Platform admins full access" into existing policies
-- For tables that have both "Editors can manage X" and
-- "Platform admins full access to X", we merge the admin check
-- into the editors policy using OR is_platform_admin(), then
-- drop the separate platform admin policy.
-- --------------------------------------------------------

-- Note: The is_platform_admin() function was already fixed in migration 039
-- to use (select auth.uid()). We just need to merge the policies.

-- For tables with platform admin policies, we need to update the
-- "Editors can manage" policy to include OR (select public.is_platform_admin())
-- then drop the platform admin policy.

-- calendar_events
DROP POLICY IF EXISTS "Platform admins full access to calendar_events" ON public.calendar_events;
DROP POLICY IF EXISTS "Editors can manage events" ON public.calendar_events;
CREATE POLICY "Editors can manage events" ON public.calendar_events
  FOR ALL
  USING (
    (select public.is_platform_admin())
    OR EXISTS (
      SELECT 1 FROM public.org_members om
      WHERE om.org_id = calendar_events.org_id
        AND om.user_id = (select auth.uid())
    )
  )
  WITH CHECK (
    (select public.is_platform_admin())
    OR EXISTS (
      SELECT 1 FROM public.org_members om
      WHERE om.org_id = calendar_events.org_id
        AND om.user_id = (select auth.uid())
    )
  );

-- documents
DROP POLICY IF EXISTS "Platform admins full access to documents" ON public.documents;
DROP POLICY IF EXISTS "Editors can manage documents" ON public.documents;
CREATE POLICY "Editors can manage documents" ON public.documents
  FOR ALL
  USING (
    (select public.is_platform_admin())
    OR EXISTS (
      SELECT 1 FROM public.org_members om
      WHERE om.org_id = documents.org_id
        AND om.user_id = (select auth.uid())
    )
  )
  WITH CHECK (
    (select public.is_platform_admin())
    OR EXISTS (
      SELECT 1 FROM public.org_members om
      WHERE om.org_id = documents.org_id
        AND om.user_id = (select auth.uid())
    )
  );

-- events
DROP POLICY IF EXISTS "Platform admins full access to events" ON public.events;
DROP POLICY IF EXISTS "Editors can manage events" ON public.events;
CREATE POLICY "Editors can manage events" ON public.events
  FOR ALL
  USING (
    (select public.is_platform_admin())
    OR EXISTS (
      SELECT 1 FROM public.org_members om
      WHERE om.org_id = events.org_id
        AND om.user_id = (select auth.uid())
    )
  )
  WITH CHECK (
    (select public.is_platform_admin())
    OR EXISTS (
      SELECT 1 FROM public.org_members om
      WHERE om.org_id = events.org_id
        AND om.user_id = (select auth.uid())
    )
  );

-- event_members
DROP POLICY IF EXISTS "Platform admins full access to event_members" ON public.event_members;
DROP POLICY IF EXISTS "Editors can manage event members" ON public.event_members;
CREATE POLICY "Editors can manage event members" ON public.event_members
  FOR ALL
  USING (
    (select public.is_platform_admin())
    OR EXISTS (
      SELECT 1 FROM public.events e
      JOIN public.org_members om ON om.org_id = e.org_id
      WHERE e.id = event_members.event_id
        AND om.user_id = (select auth.uid())
    )
  )
  WITH CHECK (
    (select public.is_platform_admin())
    OR EXISTS (
      SELECT 1 FROM public.events e
      JOIN public.org_members om ON om.org_id = e.org_id
      WHERE e.id = event_members.event_id
        AND om.user_id = (select auth.uid())
    )
  );

-- event_departments
DROP POLICY IF EXISTS "Platform admins full access to event_departments" ON public.event_departments;
DROP POLICY IF EXISTS "Editors can manage event departments" ON public.event_departments;
CREATE POLICY "Editors can manage event departments" ON public.event_departments
  FOR ALL
  USING (
    (select public.is_platform_admin())
    OR EXISTS (
      SELECT 1 FROM public.events e
      JOIN public.org_members om ON om.org_id = e.org_id
      WHERE e.id = event_departments.event_id
        AND om.user_id = (select auth.uid())
    )
  )
  WITH CHECK (
    (select public.is_platform_admin())
    OR EXISTS (
      SELECT 1 FROM public.events e
      JOIN public.org_members om ON om.org_id = e.org_id
      WHERE e.id = event_departments.event_id
        AND om.user_id = (select auth.uid())
    )
  );

-- kanban_boards
DROP POLICY IF EXISTS "Platform admins full access to kanban_boards" ON public.kanban_boards;
DROP POLICY IF EXISTS "Editors can manage boards" ON public.kanban_boards;
CREATE POLICY "Editors can manage boards" ON public.kanban_boards
  FOR ALL
  USING (
    (select public.is_platform_admin())
    OR EXISTS (
      SELECT 1 FROM public.org_members om
      WHERE om.org_id = kanban_boards.org_id
        AND om.user_id = (select auth.uid())
    )
  )
  WITH CHECK (
    (select public.is_platform_admin())
    OR EXISTS (
      SELECT 1 FROM public.org_members om
      WHERE om.org_id = kanban_boards.org_id
        AND om.user_id = (select auth.uid())
    )
  );

-- org_roles
DROP POLICY IF EXISTS "Platform admins full access to org_roles" ON public.org_roles;
-- org_roles has "Org members can view roles" + "Admins can manage roles"
DROP POLICY IF EXISTS "Org members can view roles" ON public.org_roles;
DROP POLICY IF EXISTS "Admins can manage roles" ON public.org_roles;
CREATE POLICY "Admins can manage roles" ON public.org_roles
  FOR ALL
  USING (
    (select public.is_platform_admin())
    OR EXISTS (
      SELECT 1 FROM public.org_members om
      WHERE om.org_id = org_roles.org_id
        AND om.user_id = (select auth.uid())
        AND om.role IN ('owner', 'admin')
    )
  )
  WITH CHECK (
    (select public.is_platform_admin())
    OR EXISTS (
      SELECT 1 FROM public.org_members om
      WHERE om.org_id = org_roles.org_id
        AND om.user_id = (select auth.uid())
        AND om.role IN ('owner', 'admin')
    )
  );
-- Org members still need to SELECT roles (for UI dropdowns etc)
CREATE POLICY "Org members can view roles" ON public.org_roles
  FOR SELECT
  USING (
    EXISTS (
      SELECT 1 FROM public.org_members om
      WHERE om.org_id = org_roles.org_id
        AND om.user_id = (select auth.uid())
    )
  );

-- org_invites
DROP POLICY IF EXISTS "Platform admins full access to org_invites" ON public.org_invites;
-- org_invites has "Org members can view invites" + "Admins can manage invites"
DROP POLICY IF EXISTS "Org members can view invites" ON public.org_invites;
DROP POLICY IF EXISTS "Admins can manage invites" ON public.org_invites;
CREATE POLICY "Admins can manage invites" ON public.org_invites
  FOR ALL
  USING (
    (select public.is_platform_admin())
    OR EXISTS (
      SELECT 1 FROM public.org_members om
      WHERE om.org_id = org_invites.org_id
        AND om.user_id = (select auth.uid())
        AND om.role IN ('owner', 'admin')
    )
  )
  WITH CHECK (
    (select public.is_platform_admin())
    OR EXISTS (
      SELECT 1 FROM public.org_members om
      WHERE om.org_id = org_invites.org_id
        AND om.user_id = (select auth.uid())
        AND om.role IN ('owner', 'admin')
    )
  );
-- Org members still need to SELECT invites
CREATE POLICY "Org members can view invites" ON public.org_invites
  FOR SELECT
  USING (
    EXISTS (
      SELECT 1 FROM public.org_members om
      WHERE om.org_id = org_invites.org_id
        AND om.user_id = (select auth.uid())
    )
  );

-- organizations
DROP POLICY IF EXISTS "Platform admins full access to organizations" ON public.organizations;
-- organizations already has separate view/update/delete policies, platform admin just adds another FOR ALL
-- Merge into existing policies by adding OR is_platform_admin()
DROP POLICY IF EXISTS "Members can view their organizations" ON public.organizations;
CREATE POLICY "Members can view their organizations" ON public.organizations
  FOR SELECT
  USING (
    (select public.is_platform_admin())
    OR EXISTS (
      SELECT 1 FROM public.org_members om
      WHERE om.org_id = organizations.id
        AND om.user_id = (select auth.uid())
    )
  );

DROP POLICY IF EXISTS "Owners and admins can update organizations" ON public.organizations;
CREATE POLICY "Owners and admins can update organizations" ON public.organizations
  FOR UPDATE
  USING (
    (select public.is_platform_admin())
    OR EXISTS (
      SELECT 1 FROM public.org_members om
      WHERE om.org_id = organizations.id
        AND om.user_id = (select auth.uid())
        AND om.role IN ('owner', 'admin')
    )
  )
  WITH CHECK (
    (select public.is_platform_admin())
    OR EXISTS (
      SELECT 1 FROM public.org_members om
      WHERE om.org_id = organizations.id
        AND om.user_id = (select auth.uid())
        AND om.role IN ('owner', 'admin')
    )
  );

DROP POLICY IF EXISTS "Owners can delete organizations" ON public.organizations;
CREATE POLICY "Owners can delete organizations" ON public.organizations
  FOR DELETE
  USING (
    (select public.is_platform_admin())
    OR EXISTS (
      SELECT 1 FROM public.org_members om
      WHERE om.org_id = organizations.id
        AND om.user_id = (select auth.uid())
        AND om.role = 'owner'
    )
  );

-- org_members
DROP POLICY IF EXISTS "Platform admins full access to org_members" ON public.org_members;
-- org_members has separate view/manage/delete policies + Allow member inserts
-- Just add platform admin to the view policy
DROP POLICY IF EXISTS "Members can view org members" ON public.org_members;
CREATE POLICY "Members can view org members" ON public.org_members
  FOR SELECT
  USING (
    (select public.is_platform_admin())
    OR EXISTS (
      SELECT 1 FROM public.org_members om2
      WHERE om2.org_id = org_members.org_id
        AND om2.user_id = (select auth.uid())
    )
  );

DROP POLICY IF EXISTS "Owners and admins can manage members" ON public.org_members;
CREATE POLICY "Owners and admins can manage members" ON public.org_members
  FOR UPDATE
  USING (
    (select public.is_platform_admin())
    OR EXISTS (
      SELECT 1 FROM public.org_members om2
      WHERE om2.org_id = org_members.org_id
        AND om2.user_id = (select auth.uid())
        AND om2.role IN ('owner', 'admin')
    )
  )
  WITH CHECK (
    (select public.is_platform_admin())
    OR EXISTS (
      SELECT 1 FROM public.org_members om2
      WHERE om2.org_id = org_members.org_id
        AND om2.user_id = (select auth.uid())
        AND om2.role IN ('owner', 'admin')
    )
  );

DROP POLICY IF EXISTS "Owners and admins can delete members" ON public.org_members;
CREATE POLICY "Owners and admins can delete members" ON public.org_members
  FOR DELETE
  USING (
    (select public.is_platform_admin())
    OR EXISTS (
      SELECT 1 FROM public.org_members om2
      WHERE om2.org_id = org_members.org_id
        AND om2.user_id = (select auth.uid())
        AND om2.role IN ('owner', 'admin')
    )
  );


-- ============================================================
-- PART 3: Run the initplan fixer AGAIN after Part 2
-- Part 2 created new policies that may still use bare auth.uid()
-- in the USING/WITH CHECK from the original policy definitions.
-- This second pass catches any remaining bare auth.uid() calls.
-- ============================================================

DO $$
DECLARE
  r record;
  qual_new text;
  check_new text;
  sql_cmd text;
BEGIN
  FOR r IN
    SELECT
      schemaname,
      tablename,
      policyname,
      permissive,
      roles,
      cmd AS policy_cmd,
      qual,
      with_check
    FROM pg_policies
    WHERE schemaname = 'public'
      AND (
        qual ~ 'auth\.uid\(\)' AND qual !~ '\(\s*select\s+auth\.uid\(\)\s*\)'
        OR
        with_check ~ 'auth\.uid\(\)' AND with_check !~ '\(\s*select\s+auth\.uid\(\)\s*\)'
      )
  LOOP
    qual_new := r.qual;
    IF qual_new IS NOT NULL THEN
      qual_new := regexp_replace(qual_new, '(?<!\(select )auth\.uid\(\)', '(select auth.uid())', 'gi');
    END IF;

    check_new := r.with_check;
    IF check_new IS NOT NULL THEN
      check_new := regexp_replace(check_new, '(?<!\(select )auth\.uid\(\)', '(select auth.uid())', 'gi');
    END IF;

    EXECUTE format('DROP POLICY IF EXISTS %I ON %I.%I',
      r.policyname, r.schemaname, r.tablename);

    sql_cmd := format('CREATE POLICY %I ON %I.%I', r.policyname, r.schemaname, r.tablename);

    IF r.permissive = 'false' THEN
      sql_cmd := sql_cmd || ' AS RESTRICTIVE';
    END IF;

    sql_cmd := sql_cmd || ' FOR ' || r.policy_cmd;
    sql_cmd := sql_cmd || ' TO ' || array_to_string(r.roles, ', ');

    IF qual_new IS NOT NULL THEN
      sql_cmd := sql_cmd || ' USING (' || qual_new || ')';
    END IF;

    IF check_new IS NOT NULL THEN
      sql_cmd := sql_cmd || ' WITH CHECK (' || check_new || ')';
    END IF;

    EXECUTE sql_cmd;

    RAISE NOTICE 'Fixed initplan (pass 2): %.% policy "%"', r.schemaname, r.tablename, r.policyname;
  END LOOP;
END;
$$;
