-- Phase 1 of the Skills+Revisions+Proposals work. Purely additive — no
-- existing rows are touched, no tables renamed, no columns dropped. PR-2
-- will follow up with the PromptRequest → ResourceProposal cutover (rename
-- + backfill + service rewire) once the new tables have settled.
--
-- New objects:
--   1. ResourceRevision — append-only audit + diff log keyed by
--      (resourceType, resourceId). Both Prompt and Skill produce revisions.
--      Hot reads stay on the resource row's inline content; revisions are
--      only consulted by history/diff/restore endpoints.
--   2. ResourceProposal — generic propose/approve/reject queue, drop-in
--      replacement for the prompt-only PromptRequest. Created empty here;
--      backfill from PromptRequest happens in PR-2.
--   3. Skill — new resource type that mirrors Prompt for everything CRUD-
--      shaped. Adds `files` Json (multi-file bundles, materialised onto
--      disk by `mcpctl skills sync`) and `metadata` Json (typed at the
--      app layer in PR-3: hooks, mcpServers, postInstall, …).
--   4. semver + currentRevisionId columns on Prompt.

-- ── 1. ResourceRevision ──
CREATE TABLE "ResourceRevision" (
    "id"              TEXT NOT NULL,
    -- Discriminator: 'prompt' | 'skill'. TEXT, not enum, to make adding a
    -- third resource type later a non-migration change.
    "resourceType"    TEXT NOT NULL,
    -- Soft FK — no relation declared. Survives resource deletion so the
    -- audit trail isn't lost when a prompt or skill is removed.
    "resourceId"      TEXT NOT NULL,
    "semver"          TEXT NOT NULL DEFAULT '0.1.0',
    -- sha256 of the canonicalised body — stable diff key. Two revisions
    -- with the same hash are byte-identical (skills sync uses this to
    -- skip work even when semver hasn't bumped).
    "contentHash"     TEXT NOT NULL,
    -- Snapshot of the resource at this revision: { content, metadata?, ... }
    "body"            JSONB NOT NULL,
    "authorUserId"    TEXT,
    "authorSessionId" TEXT,
    "note"            TEXT NOT NULL DEFAULT '',
    "createdAt"       TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,

    CONSTRAINT "ResourceRevision_pkey" PRIMARY KEY ("id")
);

-- History viewer: latest-first within a resource.
CREATE INDEX "ResourceRevision_resourceType_resourceId_createdAt_idx"
    ON "ResourceRevision"("resourceType", "resourceId", "createdAt" DESC);
-- Direct lookup by semver: GET /api/v1/revisions?resourceType=&resourceId=&semver=
CREATE INDEX "ResourceRevision_resourceType_resourceId_semver_idx"
    ON "ResourceRevision"("resourceType", "resourceId", "semver");
-- Cross-resource sync diff: "do I already have a revision with this hash?"
CREATE INDEX "ResourceRevision_contentHash_idx"
    ON "ResourceRevision"("contentHash");
-- Author drilldown for audit views.
CREATE INDEX "ResourceRevision_authorUserId_idx"
    ON "ResourceRevision"("authorUserId");

-- ── 2. ResourceProposal ──
CREATE TABLE "ResourceProposal" (
    "id"                 TEXT NOT NULL,
    "resourceType"       TEXT NOT NULL,
    "name"               TEXT NOT NULL,
    -- Proposed body — { content, metadata? } shaped per resourceType.
    "body"               JSONB NOT NULL,
    "projectId"          TEXT,
    "agentId"            TEXT,
    "createdBySession"   TEXT,
    "createdByUserId"    TEXT,
    -- Status lifecycle: pending → (approved|rejected). Reviewer note set
    -- on either terminal state.
    "status"             TEXT NOT NULL DEFAULT 'pending',
    "reviewerNote"       TEXT NOT NULL DEFAULT '',
    -- Set when status='approved': the ResourceRevision the approval created.
    "approvedRevisionId" TEXT,
    -- Optimistic-concurrency counter for concurrent reviewer actions.
    "version"            INTEGER NOT NULL DEFAULT 1,
    "createdAt"          TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
    "updatedAt"          TIMESTAMP(3) NOT NULL,

    CONSTRAINT "ResourceProposal_pkey" PRIMARY KEY ("id")
);

-- Cascade matches Prompt's behaviour: deleting a project drops its proposals.
ALTER TABLE "ResourceProposal"
    ADD CONSTRAINT "ResourceProposal_projectId_fkey"
        FOREIGN KEY ("projectId") REFERENCES "Project"("id")
        ON DELETE CASCADE ON UPDATE CASCADE;
ALTER TABLE "ResourceProposal"
    ADD CONSTRAINT "ResourceProposal_agentId_fkey"
        FOREIGN KEY ("agentId") REFERENCES "Agent"("id")
        ON DELETE CASCADE ON UPDATE CASCADE;

-- Mirrors Prompt's two-unique pattern: a (type, name) pair can have one
-- proposal per project XOR one per agent. NULL semantics inherit Postgres
-- defaults; the app layer reuses the `?? ''` workaround for direct
-- compound-key lookups, same as Prompt.
CREATE UNIQUE INDEX "ResourceProposal_resourceType_name_projectId_key"
    ON "ResourceProposal"("resourceType", "name", "projectId");
CREATE UNIQUE INDEX "ResourceProposal_resourceType_name_agentId_key"
    ON "ResourceProposal"("resourceType", "name", "agentId");
-- Reviewer queue: SELECT … WHERE resourceType=? AND status='pending'.
CREATE INDEX "ResourceProposal_resourceType_status_idx"
    ON "ResourceProposal"("resourceType", "status");
CREATE INDEX "ResourceProposal_projectId_idx"
    ON "ResourceProposal"("projectId");
CREATE INDEX "ResourceProposal_createdBySession_idx"
    ON "ResourceProposal"("createdBySession");

-- ── 3. Skill ──
-- Mirrors Prompt for scoping (project XOR agent XOR neither = global) and
-- carries the inline SKILL.md body in `content`. Multi-file bundles live
-- in `files` (path → content map). Typed skill metadata (hooks,
-- mcpServers, postInstall, …) lives opaquely in `metadata` here and is
-- validated app-layer (PR-3).
CREATE TABLE "Skill" (
    "id"                TEXT NOT NULL,
    "name"              TEXT NOT NULL,
    "description"       TEXT NOT NULL DEFAULT '',
    "content"           TEXT NOT NULL,
    "files"             JSONB NOT NULL DEFAULT '{}',
    "metadata"          JSONB NOT NULL DEFAULT '{}',
    "projectId"         TEXT,
    "agentId"           TEXT,
    "priority"          INTEGER NOT NULL DEFAULT 5,
    "summary"           TEXT,
    "chapters"          JSONB,
    "semver"            TEXT NOT NULL DEFAULT '0.1.0',
    -- Soft pointer to the latest ResourceRevision row for this skill.
    -- NULL before the first revision is recorded.
    "currentRevisionId" TEXT,
    -- Optimistic-concurrency counter. NOT semver — that's `semver` above.
    "version"           INTEGER NOT NULL DEFAULT 1,
    "createdAt"         TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
    "updatedAt"         TIMESTAMP(3) NOT NULL,

    CONSTRAINT "Skill_pkey" PRIMARY KEY ("id")
);

ALTER TABLE "Skill"
    ADD CONSTRAINT "Skill_projectId_fkey"
        FOREIGN KEY ("projectId") REFERENCES "Project"("id")
        ON DELETE CASCADE ON UPDATE CASCADE;
ALTER TABLE "Skill"
    ADD CONSTRAINT "Skill_agentId_fkey"
        FOREIGN KEY ("agentId") REFERENCES "Agent"("id")
        ON DELETE CASCADE ON UPDATE CASCADE;

CREATE UNIQUE INDEX "Skill_name_projectId_key" ON "Skill"("name", "projectId");
CREATE UNIQUE INDEX "Skill_name_agentId_key"   ON "Skill"("name", "agentId");
CREATE INDEX "Skill_projectId_idx" ON "Skill"("projectId");
CREATE INDEX "Skill_agentId_idx"   ON "Skill"("agentId");
CREATE INDEX "Skill_name_idx"      ON "Skill"("name");

-- ── 4. semver + currentRevisionId on Prompt ──
-- ADD COLUMN with a default is instant on Postgres ≥11 (no table rewrite).
ALTER TABLE "Prompt"
    ADD COLUMN "semver"            TEXT NOT NULL DEFAULT '0.1.0',
    ADD COLUMN "currentRevisionId" TEXT;
