-- 154_scenario_flags_ssot — t-paliad-331 / m/paliad#149 Phase 2 P0
--
-- Single source of truth for per-project scenario state. Per the
-- design (docs/design-deadline-system-revision-2026-05-27.md §2.3
-- and §2.4a), every scenario decision a user makes on a project
-- lives in one jsonb column on paliad.projects:
--
--   { "with_ccr": true, "with_amend": false,
--     "rule:<uuid_of_optional_X>": true,
--     "rule:<uuid_of_recommended_Y>": false }
--
-- Entries are either:
--   * named scenario flags (whitelist via paliad.scenario_flag_catalog), or
--   * per-rule selection deviations of shape "rule:<uuid>".
--
-- The application validates writes against the catalog and the
-- project's active sequencing-rules set; this migration only adds the
-- storage. The three known flags (with_ccr / with_amend / with_cci)
-- are seeded into the catalog so the API layer has something to
-- validate against on day one — extra flags are admin-added later
-- (see §4.2.1 R.109 worked example: with_interpreter_denied /
-- with_translation_granted both land via the editor when m walks the
-- backfill, no fresh migration needed).
--
-- Purely additive: ADD COLUMN with safe DEFAULT, CREATE TABLE, seed
-- inserts. Three existing scenario storage surfaces (project_event_
-- choices, scenarios.spec, DOM-only) are all empty per athena's audit
-- (zero rows in either persistent surface), so there is nothing to
-- migrate.
--
-- No audit trigger fires on paliad.projects today; set_config is
-- defensive so any future audit trigger inherits the reason.

BEGIN;

SELECT set_config(
    'paliad.audit_reason',
    'mig 154: scenario_flags SSoT (t-paliad-331 / m/paliad#149 Phase 2 P0)',
    true
);

-- ----------------------------------------------------------------
-- 1. paliad.projects.scenario_flags — the jsonb SSoT.
-- ----------------------------------------------------------------

ALTER TABLE paliad.projects
    ADD COLUMN scenario_flags jsonb NOT NULL DEFAULT '{}'::jsonb
        CHECK (jsonb_typeof(scenario_flags) = 'object');

COMMENT ON COLUMN paliad.projects.scenario_flags IS
    'Per-project scenario state — single source of truth (m/paliad#149 '
    'Phase 2 P0, design §2.3 + §2.4a). Flat jsonb object whose keys are '
    'either named scenario flags (whitelist via paliad.scenario_flag_catalog) '
    'or per-rule selection deviations of shape "rule:<uuid>". Values are '
    'always JSON booleans; missing keys take the priority-driven default '
    '(mandatory always selected; recommended default-selected; optional '
    'default-unselected). Validated at write time by the '
    'ScenarioFlagsService.Patch handler; this column''s CHECK only '
    'enforces that the top-level shape is an object.';

-- ----------------------------------------------------------------
-- 2. paliad.scenario_flag_catalog — the named-flag whitelist.
--    Per design §4.1: a small admin-editable vocabulary that powers
--    both the write-time validator and the UI's scenario-flag strip.
--    Per-rule entries ("rule:<uuid>") are NOT enumerated here — they
--    match a pattern and are validated by resolving the UUID against
--    the project's active sequencing-rules set.
-- ----------------------------------------------------------------

CREATE TABLE paliad.scenario_flag_catalog (
    flag_key          text        PRIMARY KEY
        CHECK (flag_key ~ '^[a-z][a-z0-9_]*$'
               AND flag_key NOT LIKE 'rule:%'
               AND char_length(flag_key) BETWEEN 1 AND 64),
    label_de          text        NOT NULL CHECK (char_length(label_de) > 0),
    label_en          text        NOT NULL CHECK (char_length(label_en) > 0),
    description       text        NULL,
    -- hidden_unless_set: when true, the flag is only surfaced in the
    -- UI's scenario strip once a rule's condition_expr references it
    -- (or once it's explicitly set on a project). Per design §4.2.1,
    -- with_interpreter_denied + with_translation_granted are good
    -- candidates for this once they're seeded — the flag exists for
    -- write validation but doesn't clutter the default UI.
    hidden_unless_set boolean     NOT NULL DEFAULT false,
    added_at          timestamptz NOT NULL DEFAULT now()
);

COMMENT ON TABLE paliad.scenario_flag_catalog IS
    'Named-flag vocabulary for paliad.projects.scenario_flags '
    '(m/paliad#149 Phase 2 P0, design §4.1). Read by the write-time '
    'validator in ScenarioFlagsService.Patch and by the Verfahrensablauf '
    'scenario-strip UI. Per-rule selection entries ("rule:<uuid>") are '
    'NOT enumerated here — they match a pattern and are validated by '
    'UUID lookup against the project''s active sequencing-rules set.';

COMMENT ON COLUMN paliad.scenario_flag_catalog.hidden_unless_set IS
    'When true, the flag does not appear in the default UI scenario '
    'strip — it is surfaced only when a rule''s condition_expr '
    'references it or when the project already has it set. Lets us '
    'register rare flags (e.g. with_interpreter_denied) without '
    'cluttering the default strip.';

-- ----------------------------------------------------------------
-- 3. Seed the three known flags. These are the flags referenced by
--    the 18 condition_expr rows in paliad.sequencing_rules today
--    (4 composite condition_expr rows are and/or-of these three).
-- ----------------------------------------------------------------

INSERT INTO paliad.scenario_flag_catalog (flag_key, label_de, label_en, description, hidden_unless_set)
VALUES
    ('with_ccr',   'Mit Widerklage auf Nichtigkeit',
                   'With counterclaim for revocation (CCR)',
                   'Active when the defendant has filed a CCR. Gates R.025 + the R.029 reply/rejoinder chain on upc.inf.cfi and the R.030 amendment branch nested under it.',
                   false),
    ('with_amend', 'Mit Antrag auf Patentänderung (R.30)',
                   'With application to amend the patent (R.30)',
                   'Active when the patentee has filed an R.30 application. Gates the R.032 def-to-amend / reply / rejoinder chain on the amendment branch.',
                   false),
    ('with_cci',  'Mit Widerklage auf Verletzung',
                   'With counterclaim for infringement (CCI)',
                   'Active when the defendant on a revocation action has filed an infringement counterclaim. Gates the analogous chain on upc.rev.cfi (the inverse of with_ccr).',
                   false);

-- ----------------------------------------------------------------
-- 4. Sanity check + informational notice.
-- ----------------------------------------------------------------

DO $$
DECLARE
    n int;
BEGIN
    SELECT COUNT(*) INTO n FROM paliad.scenario_flag_catalog;
    IF n <> 3 THEN
        RAISE EXCEPTION '[mig 154] expected 3 seeded flags, found %', n;
    END IF;
    RAISE NOTICE '[mig 154] scenario_flags SSoT ready — % flag(s) in catalog', n;
END $$;

COMMIT;
