-- pgTAP: section visibility (feature_flags) — Fase 12.1
-- Run with: psql -U postgres -d postgres -f supabase/tests/015_section_visibility.sql
--
-- Verifies:
--   * `users.feature_flags` and `collectives.feature_flags` JSONB columns exist
--     with NOT NULL + default '{}'.
--   * `public.known_sections()` is IMMUTABLE and returns the canonical 4 sections.
--   * `public.section_enabled(section, user, collective)` is STABLE, returns
--     boolean, and applies the documented precedence:
--         collective override > user override > default ON.
--     The forward-compat server layer (Fase 13) adds another layer ABOVE the
--     collective one; nothing here should make that diff awkward.
--
-- These pgTAP assertions run as postgres which bypasses RLS — the RLS write
-- path for `users.feature_flags` / `collectives.feature_flags` is covered by
-- the Vitest integration suite (tests/section-visibility.test.ts).

CREATE EXTENSION IF NOT EXISTS pgtap;

BEGIN;
SELECT plan(20);

-- ── Schema invariants ───────────────────────────────────────────────────────

SELECT has_column(
    'public', 'users', 'feature_flags',
    'SV-T01: public.users has feature_flags column'
);

SELECT col_type_is(
    'public', 'users', 'feature_flags', 'jsonb',
    'SV-T02: public.users.feature_flags is jsonb'
);

SELECT col_not_null(
    'public', 'users', 'feature_flags',
    'SV-T03: public.users.feature_flags is NOT NULL'
);

SELECT has_column(
    'public', 'collectives', 'feature_flags',
    'SV-T04: public.collectives has feature_flags column'
);

SELECT col_type_is(
    'public', 'collectives', 'feature_flags', 'jsonb',
    'SV-T05: public.collectives.feature_flags is jsonb'
);

SELECT col_not_null(
    'public', 'collectives', 'feature_flags',
    'SV-T06: public.collectives.feature_flags is NOT NULL'
);

-- ── known_sections() ────────────────────────────────────────────────────────

SELECT has_function(
    'public', 'known_sections',
    'SV-T07: public.known_sections() exists'
);

SELECT volatility_is(
    'public', 'known_sections', ARRAY[]::text[], 'immutable',
    'SV-T08: known_sections() is IMMUTABLE'
);

SELECT results_eq(
    $$ SELECT public.known_sections() $$,
    $$ VALUES (ARRAY['lists','tasks','notes','search']) $$,
    'SV-T09: known_sections() returns the canonical 4 sections in order'
);

-- ── section_enabled() ───────────────────────────────────────────────────────

SELECT has_function(
    'public', 'section_enabled', ARRAY['text', 'uuid', 'uuid'],
    'SV-T10: public.section_enabled(text, uuid, uuid) exists'
);

SELECT function_returns(
    'public', 'section_enabled', ARRAY['text', 'uuid', 'uuid'], 'boolean',
    'SV-T11: section_enabled() returns boolean'
);

SELECT volatility_is(
    'public', 'section_enabled', ARRAY['text', 'uuid', 'uuid'], 'stable',
    'SV-T12: section_enabled() is STABLE (planner-cacheable in a statement)'
);

-- ── Precedence semantics ────────────────────────────────────────────────────
-- Uses Ana (seed admin) + the seed collective. Reset both flag columns to {}
-- at the top of each assertion so the previous one doesn't leak.

UPDATE public.users
   SET feature_flags = '{}'::jsonb
 WHERE id = '11111111-1111-1111-1111-111111111111';
UPDATE public.collectives
   SET feature_flags = '{}'::jsonb
 WHERE id = 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa';

-- SV-T13: default ON — empty flags on both rows → tasks enabled.
SELECT is(
    public.section_enabled(
        'tasks',
        '11111111-1111-1111-1111-111111111111',
        'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa'
    ),
    true,
    'SV-T13: with empty flag JSON on both rows the section defaults to ON'
);

-- SV-T14: user override OFF wins when collective has no opinion.
UPDATE public.users
   SET feature_flags = '{"tasks": false}'::jsonb
 WHERE id = '11111111-1111-1111-1111-111111111111';
SELECT is(
    public.section_enabled(
        'tasks',
        '11111111-1111-1111-1111-111111111111',
        'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa'
    ),
    false,
    'SV-T14: user OFF + collective absent → OFF'
);

-- SV-T15: collective override OFF wins even when user explicitly enables.
UPDATE public.users
   SET feature_flags = '{"tasks": true}'::jsonb
 WHERE id = '11111111-1111-1111-1111-111111111111';
UPDATE public.collectives
   SET feature_flags = '{"tasks": false}'::jsonb
 WHERE id = 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa';
SELECT is(
    public.section_enabled(
        'tasks',
        '11111111-1111-1111-1111-111111111111',
        'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa'
    ),
    false,
    'SV-T15: collective OFF beats user ON (collective is the higher layer)'
);

-- SV-T16: unknown sections default ON (forward-compat — new code can call
-- section_enabled() before a migration adds the key to known_sections()).
UPDATE public.users
   SET feature_flags = '{}'::jsonb
 WHERE id = '11111111-1111-1111-1111-111111111111';
UPDATE public.collectives
   SET feature_flags = '{}'::jsonb
 WHERE id = 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa';
SELECT is(
    public.section_enabled(
        'foo-unknown-section',
        '11111111-1111-1111-1111-111111111111',
        'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa'
    ),
    true,
    'SV-T16: an unknown section key returns true (default ON, no migration race)'
);

-- ── Realtime publication membership ─────────────────────────────────────────
-- features.ts subscribes to UPDATE events on users (own row) + collectives
-- (active row). Without the publication membership those subscriptions
-- receive zero events. REPLICA IDENTITY FULL keeps OLD-row DELETE payloads
-- intact for the same callers down the line.

SELECT ok(
    EXISTS (
        SELECT 1 FROM pg_publication_tables
        WHERE pubname = 'supabase_realtime'
        AND   schemaname = 'public'
        AND   tablename  = 'users'
    ),
    'SV-T17: public.users is part of the supabase_realtime publication'
);

SELECT ok(
    EXISTS (
        SELECT 1 FROM pg_publication_tables
        WHERE pubname = 'supabase_realtime'
        AND   schemaname = 'public'
        AND   tablename  = 'collectives'
    ),
    'SV-T18: public.collectives is part of the supabase_realtime publication'
);

SELECT results_eq(
    $$ SELECT relreplident::text FROM pg_class c
       JOIN   pg_namespace n ON n.oid = c.relnamespace
       WHERE  n.nspname = 'public' AND c.relname = 'users' $$,
    $$ VALUES ('f') $$,
    'SV-T19: public.users has REPLICA IDENTITY FULL'
);

SELECT results_eq(
    $$ SELECT relreplident::text FROM pg_class c
       JOIN   pg_namespace n ON n.oid = c.relnamespace
       WHERE  n.nspname = 'public' AND c.relname = 'collectives' $$,
    $$ VALUES ('f') $$,
    'SV-T20: public.collectives has REPLICA IDENTITY FULL'
);

SELECT * FROM finish();
ROLLBACK;
