-- §11.6 (v1.2 Hierarchies): group-collection registry — shared cross-app data.
--
-- A row marks a collection NAME at a node as group-SHARED: every app in the
-- owning group's subtree may read it (and, with an authenticated editor+,
-- write it) via the explicit `kv::shared_collection("name")` SDK handle. Resolution is
-- nearest-ancestor-group-wins, walking the reading app's chain — that ancestry
-- walk is the isolation boundary (a foreign app's chain never contains the
-- owning group, so the name simply does not resolve). See docs §11.6.
--
-- This table holds only the MARKER (owner, name, kind) — the data itself lives
-- in a per-kind storage table (`group_kv_entries`, 0053, for kind='kv'). It is
-- pure declaration, structurally identical to an `extension_points` marker
-- (0051). A marker is config, not code → ON DELETE CASCADE.
--
-- Ownership is polymorphic (mirrors vars/secrets/scripts/extension_points):
-- exactly one of (app_id, group_id) is set. MVP authoring is restricted to
-- GROUP owners at the manifest layer (an app-declared shared collection is
-- degenerate — only that app would read it); the polymorphic shape keeps the
-- owner-generic reconcile path uniform with the other inheritable kinds.
--
-- `kind` is the storage discriminator. Only 'kv' ships in the MVP; the column
-- + CHECK exist now so docs/files/topics/queue slot in later without a
-- migration, and so a future `docs` collection of the same name is a distinct
-- row (the unique index covers `kind`).

CREATE TABLE group_collections (
    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 group_collections_owner_exactly_one
        CHECK ((group_id IS NULL) <> (app_id IS NULL)),
    name        TEXT NOT NULL,
    kind        TEXT NOT NULL DEFAULT 'kv' CHECK (kind IN ('kv')),
    created_at  TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    updated_at  TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

-- One marker per (owner, name, kind), case-insensitive. Partial because the
-- owner is split across two nullable columns.
CREATE UNIQUE INDEX group_collections_group_uidx
    ON group_collections (group_id, LOWER(name), kind) WHERE group_id IS NOT NULL;
CREATE UNIQUE INDEX group_collections_app_uidx
    ON group_collections (app_id, LOWER(name), kind) WHERE app_id IS NOT NULL;

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