-- =============================================================================
-- AwoooP Phase 5: MCP Gateway 四表
-- ADR-116（五閘門 enforcement）+ ADR-118（credential isolation）
-- 2026-05-04 ogt + Claude Sonnet 4.6
-- =============================================================================
-- 執行順序：
--   1. awooop_mcp_tool_registry  — Tool 白名單
--   2. awooop_mcp_grants         — Agent × Tool 授權記錄
--   3. awooop_mcp_credential_refs — k8s Secret 參照（不儲存明文）
--   4. awooop_mcp_gateway_audit  — 每次 gateway call 稽核
-- =============================================================================

BEGIN;

-- ---------------------------------------------------------------------------
-- 1. awooop_mcp_tool_registry — Tool 白名單（Gate 3: Tool）
-- ---------------------------------------------------------------------------
CREATE TABLE IF NOT EXISTS awooop_mcp_tool_registry (
    tool_id          UUID        PRIMARY KEY DEFAULT gen_random_uuid(),
    project_id       VARCHAR(64) NOT NULL
        REFERENCES awooop_projects(project_id) ON DELETE CASCADE,
    tool_name        VARCHAR(128) NOT NULL,
    tool_type        VARCHAR(32)  NOT NULL,   -- 'builtin' | 'mcp_server' | 'custom'
    description      TEXT,
    allowed_scopes   JSONB        NOT NULL DEFAULT '[]'::jsonb,  -- ["read","write","admin"]
    environment_tags JSONB        NOT NULL DEFAULT '{}'::jsonb,  -- {"env": "prod"} gate 4 用
    is_active        BOOLEAN      NOT NULL DEFAULT TRUE,
    created_at       TIMESTAMPTZ  NOT NULL DEFAULT NOW(),
    updated_at       TIMESTAMPTZ  NOT NULL DEFAULT NOW(),

    CONSTRAINT chk_tool_type
        CHECK (tool_type IN ('builtin','mcp_server','custom')),
    CONSTRAINT chk_allowed_scopes_array
        CHECK (jsonb_typeof(allowed_scopes) = 'array'),
    CONSTRAINT uix_tool_registry_project_name
        UNIQUE (project_id, tool_name)
);

CREATE INDEX IF NOT EXISTS idx_mcp_tool_registry_project
    ON awooop_mcp_tool_registry (project_id, is_active);

-- ---------------------------------------------------------------------------
-- 2. awooop_mcp_grants — Agent × Tool 授權（Gate 2: Agent + Gate 3: Tool）
-- ---------------------------------------------------------------------------
CREATE TABLE IF NOT EXISTS awooop_mcp_grants (
    grant_id    UUID        PRIMARY KEY DEFAULT gen_random_uuid(),
    project_id  VARCHAR(64) NOT NULL
        REFERENCES awooop_projects(project_id) ON DELETE CASCADE,
    agent_id    VARCHAR(128) NOT NULL,   -- awooop_agents.agent_id
    tool_id     UUID         NOT NULL
        REFERENCES awooop_mcp_tool_registry(tool_id) ON DELETE CASCADE,
    granted_by  VARCHAR(128) NOT NULL,   -- principal（human user / system）
    granted_scopes JSONB     NOT NULL DEFAULT '[]'::jsonb,  -- subset of tool.allowed_scopes
    expires_at  TIMESTAMPTZ,             -- NULL = 永不過期
    is_revoked  BOOLEAN      NOT NULL DEFAULT FALSE,
    revoked_at  TIMESTAMPTZ,
    revoked_by  VARCHAR(128),
    created_at  TIMESTAMPTZ  NOT NULL DEFAULT NOW(),

    CONSTRAINT chk_grant_scopes_array
        CHECK (jsonb_typeof(granted_scopes) = 'array'),
    CONSTRAINT chk_revoke_consistency
        CHECK (
            (is_revoked = FALSE AND revoked_at IS NULL AND revoked_by IS NULL)
            OR
            (is_revoked = TRUE AND revoked_at IS NOT NULL)
        ),
    CONSTRAINT uix_mcp_grant_agent_tool
        UNIQUE (project_id, agent_id, tool_id)
);

CREATE INDEX IF NOT EXISTS idx_mcp_grants_lookup
    ON awooop_mcp_grants (project_id, agent_id, tool_id)
    WHERE is_revoked = FALSE;

CREATE INDEX IF NOT EXISTS idx_mcp_grants_expiry
    ON awooop_mcp_grants (expires_at)
    WHERE is_revoked = FALSE AND expires_at IS NOT NULL;

-- ---------------------------------------------------------------------------
-- 3. awooop_mcp_credential_refs — k8s Secret 參照（ADR-118 credential isolation）
-- 只儲存 ref 路徑 + sha256 指紋；明文絕不入庫
-- ---------------------------------------------------------------------------
CREATE TABLE IF NOT EXISTS awooop_mcp_credential_refs (
    ref_id          UUID         PRIMARY KEY DEFAULT gen_random_uuid(),
    tool_id         UUID         NOT NULL
        REFERENCES awooop_mcp_tool_registry(tool_id) ON DELETE CASCADE,
    project_id      VARCHAR(64)  NOT NULL
        REFERENCES awooop_projects(project_id) ON DELETE CASCADE,
    -- k8s secret ref：格式 "namespace/secret-name#key"
    k8s_secret_ref  VARCHAR(256) NOT NULL,
    -- sha256(actual_secret_value) — 用於 audit；不可還原原值
    value_sha256    VARCHAR(64),
    description     TEXT,
    is_active       BOOLEAN      NOT NULL DEFAULT TRUE,
    created_at      TIMESTAMPTZ  NOT NULL DEFAULT NOW(),
    rotated_at      TIMESTAMPTZ,

    CONSTRAINT chk_k8s_ref_format
        CHECK (k8s_secret_ref ~ '^[a-z0-9-]+/[a-z0-9-]+#[a-zA-Z0-9_-]+$'),
    CONSTRAINT chk_value_sha256_hex
        CHECK (value_sha256 IS NULL OR value_sha256 ~ '^[0-9a-f]{64}$'),
    CONSTRAINT uix_credential_ref_tool
        UNIQUE (tool_id, k8s_secret_ref)
);

CREATE INDEX IF NOT EXISTS idx_mcp_cred_refs_tool
    ON awooop_mcp_credential_refs (tool_id)
    WHERE is_active = TRUE;

-- ---------------------------------------------------------------------------
-- 4. awooop_mcp_gateway_audit — Gateway call 稽核日誌（ADR-116 P1-09）
-- 不儲存 raw input/output；只儲存 hash + 結果狀態
-- ---------------------------------------------------------------------------
CREATE TABLE IF NOT EXISTS awooop_mcp_gateway_audit (
    call_id         UUID         PRIMARY KEY DEFAULT gen_random_uuid(),
    project_id      VARCHAR(64)  NOT NULL,
    run_id          UUID,        -- FK soft（run 可能不存在）
    trace_id        VARCHAR(128),
    agent_id        VARCHAR(128),
    tool_id         UUID         NOT NULL
        REFERENCES awooop_mcp_tool_registry(tool_id),
    tool_name       VARCHAR(128) NOT NULL,
    credential_ref  VARCHAR(256),   -- k8s_secret_ref 路徑（不含 key value）
    input_hash      VARCHAR(64),    -- sha256(canonical input JSON)
    output_hash     VARCHAR(64),    -- sha256(canonical output JSON)
    gate_result     JSONB        NOT NULL DEFAULT '{}'::jsonb,
        -- {"gate1_project": true, "gate2_agent": true, "gate3_tool": true,
        --  "gate4_env": true, "gate5_approval": true}
    result_status   VARCHAR(16)  NOT NULL,   -- 'success' | 'blocked' | 'failed' | 'timeout'
    block_gate      SMALLINT,    -- 哪個 gate 攔截（1-5，NULL=未攔截）
    block_reason    VARCHAR(256),
    latency_ms      INTEGER,
    created_at      TIMESTAMPTZ  NOT NULL DEFAULT NOW(),

    CONSTRAINT chk_gateway_result_status
        CHECK (result_status IN ('success','blocked','failed','timeout')),
    CONSTRAINT chk_block_gate_range
        CHECK (block_gate IS NULL OR (block_gate >= 1 AND block_gate <= 5)),
    CONSTRAINT chk_input_hash_hex
        CHECK (input_hash IS NULL OR input_hash ~ '^[0-9a-f]{64}$'),
    CONSTRAINT chk_output_hash_hex
        CHECK (output_hash IS NULL OR output_hash ~ '^[0-9a-f]{64}$')
);

-- 查詢熱路徑：by project + run
CREATE INDEX IF NOT EXISTS idx_mcp_audit_run
    ON awooop_mcp_gateway_audit (project_id, run_id, created_at DESC);

-- 查詢熱路徑：blocked calls 分析
CREATE INDEX IF NOT EXISTS idx_mcp_audit_blocked
    ON awooop_mcp_gateway_audit (project_id, block_gate, created_at DESC)
    WHERE result_status = 'blocked';

-- 時序熱路徑（recent calls）
CREATE INDEX IF NOT EXISTS idx_mcp_audit_recent
    ON awooop_mcp_gateway_audit (project_id, created_at DESC);

-- =============================================================================
-- Row Level Security
-- =============================================================================

ALTER TABLE awooop_mcp_tool_registry  ENABLE ROW LEVEL SECURITY;
ALTER TABLE awooop_mcp_grants         ENABLE ROW LEVEL SECURITY;
ALTER TABLE awooop_mcp_credential_refs ENABLE ROW LEVEL SECURITY;
ALTER TABLE awooop_mcp_gateway_audit  ENABLE ROW LEVEL SECURITY;

ALTER TABLE awooop_mcp_tool_registry  FORCE ROW LEVEL SECURITY;
ALTER TABLE awooop_mcp_grants         FORCE ROW LEVEL SECURITY;
ALTER TABLE awooop_mcp_credential_refs FORCE ROW LEVEL SECURITY;
ALTER TABLE awooop_mcp_gateway_audit  FORCE ROW LEVEL SECURITY;

-- awooop_app role：只能看自己 project 的資料
CREATE POLICY mcp_tool_registry_tenant_isolation ON awooop_mcp_tool_registry
    USING (
        project_id = current_setting('app.project_id', TRUE)
        OR current_setting('app.project_id', TRUE) IS NULL
    );

CREATE POLICY mcp_grants_tenant_isolation ON awooop_mcp_grants
    USING (
        project_id = current_setting('app.project_id', TRUE)
        OR current_setting('app.project_id', TRUE) IS NULL
    );

CREATE POLICY mcp_credential_refs_tenant_isolation ON awooop_mcp_credential_refs
    USING (
        project_id = current_setting('app.project_id', TRUE)
        OR current_setting('app.project_id', TRUE) IS NULL
    );

CREATE POLICY mcp_gateway_audit_tenant_isolation ON awooop_mcp_gateway_audit
    USING (
        project_id = current_setting('app.project_id', TRUE)
        OR current_setting('app.project_id', TRUE) IS NULL
    );

COMMIT;
