-- §9.4 Service Interceptors (v1.2) — before-op allow/deny hooks.
--
-- A marker `(owner, service, op) -> interceptor_script` declares that a script
-- runs BEFORE a data-plane operation and may deny it. MVP scope: `service='kv'`,
-- `op IN ('set','delete')`, allow/deny only (no data transform, no chaining,
-- no after-hooks). The interceptor is itself a script owned by the same node
-- (or an ancestor group); it is resolved + run through the existing `invoke()`
-- re-entry path, so this table holds only the MARKER — pure declaration,
-- structurally like an `extension_points` row (0051): config, not code, so
-- ON DELETE CASCADE.
--
-- Ownership is polymorphic (mirrors extension_points/vars/secrets/scripts):
-- exactly one of (app_id, group_id) is set. Resolution walks the calling app's
-- chain (app, then nearest ancestor group) and picks the nearest declaration
-- for a (service, op) — nearest-owner-wins, so an app overrides a group's
-- interceptor, the deliberate inverse of a sealed import.

CREATE TABLE interceptors (
    id                 UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    group_id           UUID REFERENCES groups(id) ON DELETE CASCADE,
    app_id             UUID REFERENCES apps(id) ON DELETE CASCADE,
    CONSTRAINT interceptors_owner_exactly_one
        CHECK ((group_id IS NULL) <> (app_id IS NULL)),
    -- The intercepted operation. MVP: service='kv', op IN ('set','delete').
    service            TEXT NOT NULL,
    op                 TEXT NOT NULL,
    -- Name of the interceptor script (resolved on the owner's chain at run time).
    interceptor_script TEXT NOT NULL,
    created_at         TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    updated_at         TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

-- One marker per (owner, service, op). Partial because the owner is split
-- across two nullable columns.
CREATE UNIQUE INDEX interceptors_group_uidx
    ON interceptors (group_id, service, op) WHERE group_id IS NOT NULL;
CREATE UNIQUE INDEX interceptors_app_uidx
    ON interceptors (app_id, service, op) WHERE app_id IS NOT NULL;

-- Lookup indexes for the resolver's chain join + list-by-owner.
CREATE INDEX interceptors_group_id_idx ON interceptors (group_id) WHERE group_id IS NOT NULL;
CREATE INDEX interceptors_app_id_idx ON interceptors (app_id) WHERE app_id IS NOT NULL;
