-- t-paliad-174 — SmartTimeline Slice 3.
-- Two structural additions for the counterclaim sub-project shape
-- (§4 of docs/design-smart-timeline-2026-05-08.md):
--
--   1. paliad.projects.counterclaim_of  — nullable FK referencing
--      paliad.projects(id) ON DELETE SET NULL. When non-NULL the row
--      represents the CCR (counterclaim) sub-project filed against the
--      target row. Standard parent_id keeps governing the project tree;
--      counterclaim_of is the *additional* relation describing the CCR
--      link. parent_id of the CCR child is set to the target's parent
--      (sibling-under-patent placement, §4.4) — that placement is owned
--      by ProjectService.CreateCounterclaim, not the schema.
--
--   2. Two-level-CCR rejection trigger — UPC practice does NOT have
--      counterclaim-of-a-counterclaim chains. Reject the malformed shape
--      at the schema level so the application can never write it. CHECK
--      can't reference other rows; trigger function raises explicitly.
--
-- Idempotent: re-applying is a no-op. Tracker advances 76 → 77.

-- 1. paliad.projects.counterclaim_of ---------------------------------------

ALTER TABLE paliad.projects
    ADD COLUMN IF NOT EXISTS counterclaim_of uuid NULL
        REFERENCES paliad.projects(id) ON DELETE SET NULL;

COMMENT ON COLUMN paliad.projects.counterclaim_of IS
    'When non-NULL this project is the CCR (counterclaim) filed against '
    'the referenced parent project. parent_id continues to govern the '
    'project tree (CCR is placed as a sibling under the same patent — '
    'see ProjectService.CreateCounterclaim). ON DELETE SET NULL keeps '
    'the CCR row alive when the parent is hard-deleted (rare; default '
    'is archival) so the audit trail survives.';

CREATE INDEX IF NOT EXISTS projects_counterclaim_of_idx
    ON paliad.projects (counterclaim_of)
    WHERE counterclaim_of IS NOT NULL;

-- 2. Two-level-CCR rejection trigger ---------------------------------------

CREATE OR REPLACE FUNCTION paliad.projects_no_two_level_ccr() RETURNS trigger
    LANGUAGE plpgsql AS $$
BEGIN
    -- A project that is itself a CCR may NOT be the target of another CCR.
    -- Two cases to reject:
    --
    --   (a) NEW row points at a parent that is itself a CCR:
    --       NEW.counterclaim_of -> some row with counterclaim_of NOT NULL.
    --
    --   (b) NEW row claims to be a CCR (NEW.counterclaim_of IS NOT NULL)
    --       but already has another CCR pointing AT it (NEW.id is the
    --       target of some other row's counterclaim_of). The cleaner
    --       phrasing: "no row may simultaneously have a CCR child AND
    --       a CCR parent".
    IF NEW.counterclaim_of IS NOT NULL THEN
        IF EXISTS (
            SELECT 1 FROM paliad.projects p
             WHERE p.id = NEW.counterclaim_of
               AND p.counterclaim_of IS NOT NULL
        ) THEN
            RAISE EXCEPTION
                'two-level counterclaim chains are not allowed: parent project % is itself a counterclaim',
                NEW.counterclaim_of;
        END IF;

        IF EXISTS (
            SELECT 1 FROM paliad.projects p
             WHERE p.counterclaim_of = NEW.id
        ) THEN
            RAISE EXCEPTION
                'project % already has a counterclaim child and cannot itself be a counterclaim',
                NEW.id;
        END IF;
    END IF;

    RETURN NEW;
END;
$$;

COMMENT ON FUNCTION paliad.projects_no_two_level_ccr() IS
    'Rejects two-level counterclaim chains. UPC practice does not have '
    'CCR-of-a-CCR; reject the malformed shape at write time so the app '
    'layer never has to defend against it. See migration 077.';

DROP TRIGGER IF EXISTS projects_no_two_level_ccr ON paliad.projects;
CREATE TRIGGER projects_no_two_level_ccr
    BEFORE INSERT OR UPDATE OF counterclaim_of ON paliad.projects
    FOR EACH ROW
    EXECUTE FUNCTION paliad.projects_no_two_level_ccr();
