-- t-paliad-209 / workstream B — submission-code prefix + rename.
--
-- m's 2026-05-18 call: the `paliad.deadline_rules.code` field is a
-- SUBMISSION identifier (the event/filing within a proceeding), not the
-- legal-citation rule code (which lives in `rule_code` / `legal_source`).
-- Two cleanups land here:
--
--   1. DATA — prefix every existing submission code with its proceeding
--      code so submission codes carry the full hierarchical shape
--      (e.g. `inf.soc` on `upc.inf.cfi` → `upc.inf.cfi.soc`,
--      `de_inf.klage` on `de.inf.lg` → `de.inf.lg.klage`).
--      Algorithm: keep the proceeding-code prefix as-is, strip the
--      old single-segment prefix (everything before the first dot in
--      `dr.code`) and replace it with the proceeding's full `code`.
--
--   2. SCHEMA — rename `paliad.deadline_rules.code` → `submission_code`
--      so future devs don't conflate it with `rule_code` (legal
--      citation) or `proceeding_types.code`. Explicit name encodes the
--      semantic taxonomy ratified in
--      docs/design-proceeding-code-taxonomy-2026-05-18.md §0.1.
--
-- Materialized-view dependency: `paliad.deadline_search` (mig 051) has
-- `dr.code AS rule_local_code` baked into its SELECT list. Postgres
-- rejects RENAME COLUMN when a matview's column list still resolves
-- via the old name — so the matview is dropped before the rename and
-- recreated against `submission_code` afterwards, with every index
-- reproduced. The mig 047 / 051 indexes are reproduced verbatim here.
--
-- IDs and FKs are untouched. `deadline_rules.proceeding_type_id` /
-- `parent_id` / `spawn_proceeding_type_id` reference ids; no
-- code-string FK exists on submission codes (the parent_id chain is on
-- UUID `id`, not the code string), so the data UPDATE doesn't risk
-- breaking joins.
--
-- Idempotent:
--   * The data UPDATE is gated `WHERE dr.code NOT LIKE pt.code || '.%'`
--     — rows already prefixed with their proceeding code (i.e. the
--     migration ran before) are skipped.
--   * The rename is wrapped in a DO block that checks column existence,
--     so a second run is a no-op.
--   * Snapshot table uses CREATE TABLE IF NOT EXISTS.
--   * Matview drop/recreate is DROP IF EXISTS + CREATE.
--
-- audit_reason wrapper required by the mig 079 audit trigger.

SELECT set_config(
    'paliad.audit_reason',
    'mig 098: t-paliad-209 workstream B — prefix every paliad.deadline_rules.code with its proceeding code, then rename code → submission_code; matview deadline_search rebuilt against the new column. See docs/design-proceeding-code-taxonomy-2026-05-18.md and the t-paliad-209 task brief.',
    true);

-- =============================================================================
-- 1. Backup snapshot of paliad.deadline_rules BEFORE the prefix + rename.
--    Captures the rows as they are; serves as the source for the down
--    migration and the permanent audit anchor.
-- =============================================================================

CREATE TABLE IF NOT EXISTS paliad.deadline_rules_pre_098 AS
SELECT *, now() AS snapshotted_at
  FROM paliad.deadline_rules;

COMMENT ON TABLE paliad.deadline_rules_pre_098 IS
    'Snapshot of paliad.deadline_rules taken before mig 098 prefixed '
    'every `code` with its proceeding code and renamed the column to '
    '`submission_code` (t-paliad-209, 2026-05-18). Source-of-truth '
    'for the down migration; persists post-rename as the permanent '
    'audit record.';

-- =============================================================================
-- 2. Drop the deadline_search materialized view. It bakes `dr.code AS
--    rule_local_code` into its SELECT list (mig 051 §4), and Postgres
--    refuses to rename a column that a matview's column list still
--    resolves via the old name. The matview is recreated verbatim in §5
--    against the renamed column.
-- =============================================================================

DROP MATERIALIZED VIEW IF EXISTS paliad.deadline_search;

-- =============================================================================
-- 3. Data UPDATE — prefix every submission code with its proceeding
--    code. Algorithm:
--      * proceeding_code = pt.code
--      * suffix          = portion of dr.code after the first '.'
--      * new code        = proceeding_code || '.' || suffix
--
--    regexp_replace('inf.soc', '^[^.]+\.', '')      = 'soc'
--    regexp_replace('de_inf_bgh.revision', ...)     = 'revision'
--
--    The WHERE clause skips rows that already start with `pt.code || '.'`
--    so re-running the migration is a no-op on already-prefixed rows.
--    Archived rows (proceeding `_archived_litigation`) get the same
--    treatment — they end up as `_archived_litigation.<suffix>`. The
--    shape regex in §6 only inspects active+published rows, so the
--    archived form sits outside the constraint by design.
-- =============================================================================

UPDATE paliad.deadline_rules dr
   SET code = pt.code || '.' || regexp_replace(dr.code, '^[^.]+\.', '')
  FROM paliad.proceeding_types pt
 WHERE pt.id = dr.proceeding_type_id
   AND dr.code IS NOT NULL
   AND position('.' in dr.code) > 0
   AND dr.code NOT LIKE pt.code || '.%';

-- =============================================================================
-- 4. Rename the column. Guarded in a DO block so a second run (e.g. a
--    fresh DB built up to mig 098 from an empty schema, or a manual
--    re-apply) is a no-op rather than a hard error.
-- =============================================================================

DO $$
BEGIN
    IF EXISTS (
        SELECT 1
          FROM information_schema.columns
         WHERE table_schema = 'paliad'
           AND table_name   = 'deadline_rules'
           AND column_name  = 'code'
    ) THEN
        ALTER TABLE paliad.deadline_rules
            RENAME COLUMN code TO submission_code;
    END IF;
END $$;

-- =============================================================================
-- 5. Recreate the deadline_search matview against the renamed column.
--    Column list reproduced verbatim from mig 051 §4 with the single
--    edit: `dr.code AS rule_local_code` → `dr.submission_code AS
--    rule_local_code`. All indexes from mig 051 are reproduced too.
-- =============================================================================

CREATE MATERIALIZED VIEW paliad.deadline_search AS
SELECT
    'rule'::text                         AS kind,
    'r:' || dr.id::text                  AS row_key,
    dc.id                                AS concept_id,
    dc.slug                              AS concept_slug,
    dc.name_de                           AS concept_name_de,
    dc.name_en                           AS concept_name_en,
    dc.description                       AS concept_description,
    dc.aliases                           AS concept_aliases,
    dc.party                             AS concept_party,
    dc.category                          AS concept_category,
    dc.sort_order                        AS concept_sort_order,
    dr.id                                AS rule_id,
    NULL::bigint                         AS trigger_event_id,
    pt.code                              AS proceeding_code,
    pt.name                              AS proceeding_name_de,
    pt.name_en                           AS proceeding_name_en,
    pt.jurisdiction                      AS jurisdiction,
    pt.display_order                     AS proceeding_display_order,
    dr.submission_code                   AS rule_local_code,
    dr.name                              AS rule_name_de,
    dr.name_en                           AS rule_name_en,
    dr.legal_source                      AS legal_source,
    dr.rule_code                         AS rule_code,
    dr.duration_value,
    dr.duration_unit,
    dr.timing,
    COALESCE(dr.primary_party, dc.party) AS effective_party
  FROM paliad.deadline_rules dr
  JOIN paliad.proceeding_types pt  ON pt.id = dr.proceeding_type_id
  JOIN paliad.deadline_concepts dc ON dc.id = dr.concept_id
 WHERE dr.is_active
   AND pt.is_active
   AND pt.category = 'fristenrechner'

UNION ALL

SELECT
    'trigger'::text,
    't:' || te.id::text,
    dc.id,
    dc.slug,
    dc.name_de,
    dc.name_en,
    dc.description,
    dc.aliases,
    dc.party,
    dc.category,
    dc.sort_order,
    NULL::uuid,
    te.id,
    NULL::text,
    NULL::text,
    NULL::text,
    'cross-cutting'::text,
    9999::int                            AS proceeding_display_order,
    te.code,
    te.name_de,
    te.name,
    NULL::text,
    NULL::text,
    NULL::int,
    NULL::text,
    NULL::text,
    dc.party
  FROM paliad.trigger_events te
  JOIN paliad.deadline_concepts dc ON dc.slug = te.concept_id
 WHERE te.is_active;

CREATE UNIQUE INDEX deadline_search_row_key       ON paliad.deadline_search (row_key);
CREATE INDEX deadline_search_concept_id           ON paliad.deadline_search (concept_id);
CREATE INDEX deadline_search_proc_code            ON paliad.deadline_search (proceeding_code);
CREATE INDEX deadline_search_legal_source         ON paliad.deadline_search (legal_source);
CREATE INDEX deadline_search_effective_party      ON paliad.deadline_search (effective_party);
CREATE INDEX deadline_search_legal_source_trgm    ON paliad.deadline_search USING gin (legal_source gin_trgm_ops);
CREATE INDEX deadline_search_concept_de_trgm      ON paliad.deadline_search USING gin (concept_name_de gin_trgm_ops);
CREATE INDEX deadline_search_concept_en_trgm      ON paliad.deadline_search USING gin (concept_name_en gin_trgm_ops);
CREATE INDEX deadline_search_rule_de_trgm         ON paliad.deadline_search USING gin (rule_name_de gin_trgm_ops);
CREATE INDEX deadline_search_rule_en_trgm         ON paliad.deadline_search USING gin (rule_name_en gin_trgm_ops);
CREATE INDEX deadline_search_rule_code_trgm       ON paliad.deadline_search USING gin (rule_code gin_trgm_ops);

-- =============================================================================
-- 6. Hard assertions. Half-applied migrations would leave the rule
--    corpus inconsistent; gate on the shape of every active+published
--    row and on column existence so this fails loudly rather than
--    leaving the schema in a half-renamed state.
-- =============================================================================

DO $$
DECLARE
    v_bad_shape    integer;
    v_null_codes   integer;
    v_col_exists   boolean;
BEGIN
    -- 6.1 Every active+published row has the proceeding-code-prefixed
    --     4+-segment shape. Archived rows (`_archived_litigation` ones)
    --     keep their shorter shape by design — they're carved out.
    -- Suffix segments may include digits (existing data — e.g. EPA rule
    -- codes like `epa.opp.boa.r106` / `epa.grant.exa.r71_3` carry the
    -- statutory rule number in the suffix). Allow [a-z_0-9] per segment.
    SELECT count(*) INTO v_bad_shape
      FROM paliad.deadline_rules
     WHERE is_active = true
       AND lifecycle_state = 'published'
       AND submission_code !~ '^[a-z_0-9]+\.[a-z_0-9]+\.[a-z_0-9]+\.[a-z_0-9]+(\..*)?$';
    IF v_bad_shape <> 0 THEN
        RAISE EXCEPTION
            'mig 098: expected every active+published deadline_rules row to match the 4+-segment submission_code shape, got % violators',
            v_bad_shape;
    END IF;

    -- 6.2 No NULL submission_code on active+published rows that BELONG
    --     to a proceeding. Orphan rows (`proceeding_type_id IS NULL`)
    --     are cross-cutting rules without a fixed proceeding home
    --     (Wiedereinsetzung, Schriftsatznachreichung, etc.) — they
    --     legitimately carry NULL submission_code because there's no
    --     proceeding to prefix with. Exempt them.
    SELECT count(*) INTO v_null_codes
      FROM paliad.deadline_rules
     WHERE is_active = true
       AND lifecycle_state = 'published'
       AND proceeding_type_id IS NOT NULL
       AND submission_code IS NULL;
    IF v_null_codes <> 0 THEN
        RAISE EXCEPTION
            'mig 098: expected 0 NULL submission_code on active+published rows, got %',
            v_null_codes;
    END IF;

    -- 6.3 Column was actually renamed. Catches the case where the DO
    --     guard in §4 short-circuited because the schema hadn't yet
    --     been migrated.
    SELECT EXISTS (
        SELECT 1
          FROM information_schema.columns
         WHERE table_schema = 'paliad'
           AND table_name   = 'deadline_rules'
           AND column_name  = 'submission_code'
    ) INTO v_col_exists;
    IF NOT v_col_exists THEN
        RAISE EXCEPTION
            'mig 098: column paliad.deadline_rules.submission_code missing after rename — half-applied migration';
    END IF;
END $$;
