-- Migration 023: section visibility / feature flags (Fase 12)
--
-- Lets each user hide top-level sections they don't use, and lets admins of a
-- collective hide them for every member. The control surface is intentionally
-- typed-rigid via `public.known_sections()` instead of being a free-form bag
-- of strings — adding a new section requires a migration (which is the same
-- diff that adds the section's routes anyway, so no extra friction).
--
-- ── Precedence ───────────────────────────────────────────────────────────────
-- Effective layers, MOST restrictive wins:
--   1. (Fase 13) server   — server_settings.default_sections jsonb
--   2. collective         — collectives.feature_flags jsonb (admin override)
--   3. user               — users.feature_flags jsonb       (per-user override)
--   4. default            — true (ON)
-- Fase 12 implements layers 2–4. Fase 13 will extend `public.section_enabled()`
-- with a server layer above layer 2. The function body below is structured as
-- a single `coalesce(...)` so that adding the server layer is a one-line diff
-- (prepend the new SELECT to the coalesce) — no callers change shape.
--
-- ── Data shape ───────────────────────────────────────────────────────────────
-- `feature_flags jsonb` (object) where each key is a section name (matching
-- `known_sections()`) and the value is the boolean override. Missing key →
-- "no opinion" → defer to the next layer. Default for both columns is `{}`
-- so existing rows opt in to nothing and behave exactly as before.
--
-- RLS: write-paths on `users.feature_flags` and `collectives.feature_flags`
-- piggyback on the existing `users_update_own` and `collectives_update`
-- policies (migration 003). No new policy is required — the JSONB column is
-- covered by the row-level UPDATE policy automatically.

ALTER TABLE public.users
    ADD COLUMN feature_flags jsonb NOT NULL DEFAULT '{}'::jsonb;

ALTER TABLE public.collectives
    ADD COLUMN feature_flags jsonb NOT NULL DEFAULT '{}'::jsonb;

COMMENT ON COLUMN public.users.feature_flags IS
    'Per-user section visibility overrides. Keys must be in public.known_sections().';
COMMENT ON COLUMN public.collectives.feature_flags IS
    'Per-collective section visibility overrides set by an admin. Outranks the user layer.';

-- ── known_sections() ────────────────────────────────────────────────────────
-- IMMUTABLE: the result is a compile-time literal of the source rows in this
-- migration. Adding/removing a section is a fresh migration, not a runtime
-- change, so it's safe to inline as IMMUTABLE.
CREATE OR REPLACE FUNCTION public.known_sections()
RETURNS text[]
LANGUAGE sql
IMMUTABLE
AS $$
    SELECT ARRAY['lists', 'tasks', 'notes', 'search']::text[]
$$;

COMMENT ON FUNCTION public.known_sections() IS
    'Canonical list of top-level sections that participate in feature_flags. '
    'Each new section requires a migration that updates this function + a '
    'toggle row in /settings + /collective/manage.';

-- ── section_enabled() ───────────────────────────────────────────────────────
-- Resolves the effective ON/OFF state for one section, in one statement, with
-- the layered precedence documented at the top of this file.
--
-- The `coalesce(NULL, NULL, default)` pattern relies on `(jsonb ->> key)`
-- returning NULL when the key is missing — so a layer with "no opinion"
-- transparently falls through. An unknown section key returns NULL from every
-- layer and the trailing `true` wins, matching the documented default-ON
-- behaviour (a new feature is visible by default until someone opts out).
CREATE OR REPLACE FUNCTION public.section_enabled(
    p_section    text,
    p_user       uuid,
    p_collective uuid
)
RETURNS boolean
LANGUAGE sql
STABLE
SECURITY DEFINER
SET search_path = public
AS $$
    SELECT COALESCE(
        -- Fase 13 will prepend a server layer here:
        --   (SELECT (value ->> p_section)::boolean
        --    FROM   public.server_settings
        --    WHERE  key = 'default_sections'),
        (SELECT (feature_flags ->> p_section)::boolean
           FROM   public.collectives
          WHERE   id = p_collective),
        (SELECT (feature_flags ->> p_section)::boolean
           FROM   public.users
          WHERE   id = p_user),
        true
    );
$$;

COMMENT ON FUNCTION public.section_enabled(text, uuid, uuid) IS
    'Effective ON/OFF of a top-level section for (user, collective). '
    'Precedence: collective override > user override > default true. '
    'Fase 13 will prepend a server-settings layer above the collective.';

-- ── Realtime ────────────────────────────────────────────────────────────────
-- The client-side `features.ts` store subscribes to `users` (own row) and
-- `collectives` (active row) so admin/user toggles propagate without a
-- reload. Without the publication membership those subscriptions receive
-- zero events — the realtime test (Playwright SV-02) was the catch.
--
-- REPLICA IDENTITY FULL is required so the OLD row carries all columns on
-- DELETE events; we only consume UPDATE here today, but `users` rows can be
-- deleted by the account-deletion flow (Fase 10.5) and a future Fase 13
-- "admin removes collective" will delete `collectives`. Filtering both
-- subscriptions still works correctly on DELETE this way.
ALTER PUBLICATION supabase_realtime ADD TABLE public.users;
ALTER PUBLICATION supabase_realtime ADD TABLE public.collectives;

ALTER TABLE public.users       REPLICA IDENTITY FULL;
ALTER TABLE public.collectives REPLICA IDENTITY FULL;
