-- 135_primary_party_check — Slice B3, m/paliad#124 §18.3
--
-- Tightens paliad.deadline_rules.primary_party from free-text to a
-- CHECK constraint over the canonical four-value vocabulary
-- (claimant / defendant / court / both). NULL stays valid for the
-- 78 cross-cutting orphan concept seeds (Wiedereinsetzung,
-- Versäumnisurteil-Einspruch, Schriftsatznachreichung,
-- Weiterbehandlung) — they have no proceeding_type_id binding so
-- they're outside the calculator's path; loosening the CHECK to
-- "IS NULL OR IN (…)" keeps them valid without backfill gymnastics.
--
-- Audit-first: the DO block RAISEs NOTICE for every non-conforming
-- row before adding the CHECK, and RAISEs EXCEPTION if any dirty
-- rows are found so the operator can decide a manual cleanup path.
-- Live audit (Supabase, 2026-05-26 §18.0) confirmed zero dirty rows
-- on the current corpus: 26 claimant + 26 defendant + 38 court +
-- 63 both + 78 NULL = 231 total, all in the canonical vocab. The
-- audit pass stays in the migration for safety against future drift
-- (e.g. a rule editor write that bypassed the application-layer
-- validation hook this slice also adds).

DO $$
DECLARE
    rec record;
    dirty_count int := 0;
BEGIN
    RAISE NOTICE '[mig 135] primary_party audit pass — non-conforming rows:';
    FOR rec IN
        SELECT dr.id, dr.name, dr.primary_party,
               pt.code AS proceeding_code
          FROM paliad.deadline_rules dr
          LEFT JOIN paliad.proceeding_types pt ON pt.id = dr.proceeding_type_id
         WHERE dr.is_active = true
           AND dr.primary_party IS NOT NULL
           AND dr.primary_party NOT IN ('claimant', 'defendant', 'court', 'both')
         ORDER BY pt.code NULLS LAST, dr.name
    LOOP
        RAISE NOTICE '[mig 135]   %  %  primary_party=%  (rule=%)',
            COALESCE(rec.proceeding_code, '<orphan>'),
            rec.name,
            rec.primary_party,
            rec.id;
        dirty_count := dirty_count + 1;
    END LOOP;
    IF dirty_count > 0 THEN
        RAISE EXCEPTION '[mig 135] FAILED — % rule(s) carry non-canonical primary_party values. '
            'Manual cleanup required: update each row to one of '
            '''claimant'', ''defendant'', ''court'', ''both'', or NULL. '
            'See the NOTICE lines above for the offending rows.', dirty_count;
    END IF;
    RAISE NOTICE '[mig 135] audit clean — proceeding with CHECK constraint';
END $$;

-- ---------------------------------------------------------------
-- Add the CHECK constraint. NULL stays valid; the four canonical
-- values are the only allowed non-NULL forms.
-- ---------------------------------------------------------------

ALTER TABLE paliad.deadline_rules
    ADD CONSTRAINT deadline_rules_primary_party_chk
        CHECK (
            primary_party IS NULL
            OR primary_party IN ('claimant', 'defendant', 'court', 'both')
        );

COMMENT ON CONSTRAINT deadline_rules_primary_party_chk
    ON paliad.deadline_rules IS
    'Slice B3 (mig 135, m/paliad#124 §18.3) — canonical four-value '
    'vocab for primary_party (claimant / defendant / court / both). '
    'NULL allowed for cross-cutting orphan concept seeds (78 rows in '
    'live corpus as of mig 135). See pkg/litigationplanner.PrimaryParties '
    'for the in-code vocabulary.';

-- ---------------------------------------------------------------
-- Post-migration distribution check — informational NOTICE only.
-- ---------------------------------------------------------------

DO $$
DECLARE
    rec record;
BEGIN
    RAISE NOTICE '[mig 135] post: primary_party distribution after constraint add:';
    FOR rec IN
        SELECT COALESCE(primary_party, '<NULL>') AS party, COUNT(*) AS n
          FROM paliad.deadline_rules
         WHERE is_active = true
         GROUP BY primary_party
         ORDER BY party
    LOOP
        RAISE NOTICE '[mig 135]   %  count=%', rec.party, rec.n;
    END LOOP;
END $$;
