-- 145_scenarios — Slice D, m/paliad#124 §5 (revised)
--
-- Creates paliad.scenarios + paliad.projects.active_scenario_id FK.
-- A scenario is a named composition of existing proceedings + flags
-- + per-card choices + anchor dates the user can switch between for
-- a project (project_id NOT NULL) OR save as an abstract template on
-- /tools/verfahrensablauf (project_id IS NULL).
--
-- m's 2026-05-26 picks (AskUserQuestion round, doc commit 6e58595):
--   Q1: composition shape  → primary+spawned (v1); multi-proceeding
--                            peer compose is the v2 goal. spec.jsonb
--                            architected for N entries from day 1.
--   Q2: scope              → per-project + abstract.
--   Q3: trigger dates      → per-anchor overrides over one base date.
--   Q4: storage            → NEW paliad.scenarios table with jsonb
--                            spec (NOT a project_event_choices column
--                            extension).
--
-- "users should not add their own rules" (m, t-paliad-301) — scenarios
-- compose existing rules, never author new ones. spec.proceedings[*].code
-- must resolve to an existing active paliad.proceeding_types row;
-- spec.proceedings[*].anchor_overrides keys must resolve to existing
-- submission_codes. Validation happens at the application layer
-- (ScenarioService.validateSpec) — not in DB CHECK constraints (too
-- expensive to express in pure SQL).
--
-- Migration number: 145. Coordination check 2026-05-26 17:38: curie's
-- B.2-B.6 migrations land in the 139-143 range. 144 reserved as buffer.
-- 145 is the next safe claim.
--
-- ADDITIVE ONLY: CREATE TABLE, ALTER ADD COLUMN, indexes, RLS policies.
-- Down drops everything. No backfill (zero existing scenarios on day 1).
--
-- See docs/design-litigation-planner-2026-05-26.md §5 + §18.4 for the
-- design.

-- ---------------------------------------------------------------
-- 1. The scenarios table
-- ---------------------------------------------------------------

CREATE TABLE paliad.scenarios (
    id          uuid PRIMARY KEY DEFAULT gen_random_uuid(),
    -- project_id NULL = abstract scenario (saved Verfahrensablauf
    -- template, no Akte). project_id NOT NULL = scenario attached to
    -- a real Akte.
    project_id  uuid NULL REFERENCES paliad.projects(id) ON DELETE CASCADE,
    name        text NOT NULL,
    description text NULL,
    -- spec carries the full composition. Shape documented in the
    -- design doc §5; the application validates structure before write.
    spec        jsonb NOT NULL,
    created_by  uuid NULL REFERENCES paliad.users(id) ON DELETE SET NULL,
    created_at  timestamptz NOT NULL DEFAULT now(),
    updated_at  timestamptz NOT NULL DEFAULT now(),

    -- Within a single project, scenario names are unique. Abstract
    -- scenarios are unique per (created_by, name) so two users can
    -- each keep a "with_ccr" template without colliding. NULLS NOT
    -- DISTINCT means a single user can have one "name" per
    -- (project_id, created_by) tuple, where NULL project_id +
    -- NULL created_by is a single global namespace (used only by
    -- seed / system scenarios — none today).
    CONSTRAINT scenarios_unique_per_scope
        UNIQUE NULLS NOT DISTINCT (project_id, created_by, name),

    -- Non-empty name.
    CONSTRAINT scenarios_name_nonempty CHECK (char_length(name) > 0),

    -- Non-empty spec — at least an object. The application checks
    -- structure (version, proceedings[], base_trigger_date format).
    CONSTRAINT scenarios_spec_object CHECK (jsonb_typeof(spec) = 'object')
);

CREATE INDEX scenarios_project_id_idx
    ON paliad.scenarios(project_id) WHERE project_id IS NOT NULL;

CREATE INDEX scenarios_abstract_user_idx
    ON paliad.scenarios(created_by) WHERE project_id IS NULL;

COMMENT ON TABLE paliad.scenarios IS
    'Named compositions of existing proceedings + flags + per-card '
    'choices + anchor dates. project_id NULL = abstract template; '
    'project_id NOT NULL = attached to an Akte. Design: '
    'docs/design-litigation-planner-2026-05-26.md §5. (Slice D)';

COMMENT ON COLUMN paliad.scenarios.spec IS
    'jsonb composition spec. Shape: {version: int, base_trigger_date: '
    'ISO date, proceedings: [{code, role, flags[], per_card_choices, '
    'anchor_overrides, skip_rules[]}, ...]}. Validated at write-time '
    'by ScenarioService.validateSpec.';

-- ---------------------------------------------------------------
-- 2. paliad.projects.active_scenario_id FK
--
-- NULL = use today's ad-hoc per-card choice state from
-- paliad.project_event_choices (pre-scenario behaviour preserved).
-- Non-NULL = the project's current SmartTimeline / Akte-Fristenrechner
-- render reads from this scenario's spec instead.
-- ---------------------------------------------------------------

ALTER TABLE paliad.projects
    ADD COLUMN active_scenario_id uuid NULL
        REFERENCES paliad.scenarios(id) ON DELETE SET NULL;

COMMENT ON COLUMN paliad.projects.active_scenario_id IS
    'FK to paliad.scenarios. NULL = read choices from '
    'paliad.project_event_choices (legacy). Non-NULL = read from the '
    'pointed scenario.spec.';

-- ---------------------------------------------------------------
-- 3. RLS — mirror paliad.project_event_choices's pattern (mig 129).
--
-- Project-scoped scenarios (project_id NOT NULL) inherit team visibility
-- via paliad.can_see_project. Abstract scenarios (project_id IS NULL)
-- are private to created_by — only the author can read / write them.
-- ---------------------------------------------------------------

ALTER TABLE paliad.scenarios ENABLE ROW LEVEL SECURITY;

-- Project-scoped: team visibility.
DROP POLICY IF EXISTS scenarios_project_select ON paliad.scenarios;
CREATE POLICY scenarios_project_select ON paliad.scenarios
    FOR SELECT
    USING (project_id IS NOT NULL AND paliad.can_see_project(project_id));

DROP POLICY IF EXISTS scenarios_project_mutate ON paliad.scenarios;
CREATE POLICY scenarios_project_mutate ON paliad.scenarios
    FOR ALL
    USING (project_id IS NOT NULL AND paliad.can_see_project(project_id))
    WITH CHECK (project_id IS NOT NULL AND paliad.can_see_project(project_id));

-- Abstract: owner-only.
DROP POLICY IF EXISTS scenarios_abstract_select ON paliad.scenarios;
CREATE POLICY scenarios_abstract_select ON paliad.scenarios
    FOR SELECT
    USING (project_id IS NULL AND created_by = auth.uid());

DROP POLICY IF EXISTS scenarios_abstract_mutate ON paliad.scenarios;
CREATE POLICY scenarios_abstract_mutate ON paliad.scenarios
    FOR ALL
    USING (project_id IS NULL AND created_by = auth.uid())
    WITH CHECK (project_id IS NULL AND created_by = auth.uid());

-- ---------------------------------------------------------------
-- 4. updated_at trigger (mirrors other paliad tables that carry
-- updated_at — keep it in lockstep with row mutations).
-- ---------------------------------------------------------------

CREATE OR REPLACE FUNCTION paliad.scenarios_touch_updated_at()
    RETURNS trigger AS $$
BEGIN
    NEW.updated_at = now();
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER scenarios_touch_updated_at_trg
    BEFORE UPDATE ON paliad.scenarios
    FOR EACH ROW
    EXECUTE FUNCTION paliad.scenarios_touch_updated_at();

-- ---------------------------------------------------------------
-- 5. Informational NOTICE — schema-only migration, zero rows added.
-- ---------------------------------------------------------------

DO $$
BEGIN
    RAISE NOTICE '[mig 145] paliad.scenarios created (0 rows; awaits API usage)';
    RAISE NOTICE '[mig 145] paliad.projects.active_scenario_id added (all rows NULL initially)';
END $$;
