-- =============================================================================
-- Migration 025: mcp_calls + ai_call_budgets
-- Operation Ollama-First v5.0 — Phase 1 (Phase 10 MCP / Phase 9 預算告警 預備)
-- 日期: 2026-05-03 台北
-- =============================================================================
-- 說明:
--   mcp_calls   — Phase 10 引入 5 個 MCP server 後的遙測表，Schema 先到位。
--                 與 ai_calls 分表，因 MCP 沒有 token 概念、計費邏輯不同。
--   ai_call_budgets — Phase 9 預算告警表；種子資料即立即可用。
--
-- 設計決策:
--   1. mcp_calls.insight_id 不加 FK：避免 cascade（Phase 11 ai_insights 會頻繁 archive）
--      改用「軟連結」+ 應用層 join，保留可被 NULL 化的彈性。
--   2. ai_call_budgets.provider NULL = 全供應商總額（UNIQUE constraint 用 (period, provider)
--      若 NULL 行為不一致需保護，由應用層強制單例）
--      註: PostgreSQL 預設 NULL != NULL，所以同 period 多筆 provider=NULL 會通過 UNIQUE，
--          需應用層自律或改用部分索引（見下方）。
--
-- 回滾腳本:
--   DROP INDEX IF EXISTS idx_mcp_calls_called_at;
--   DROP INDEX IF EXISTS idx_mcp_calls_caller_called_at;
--   DROP INDEX IF EXISTS idx_mcp_calls_server_tool;
--   DROP INDEX IF EXISTS idx_mcp_calls_status_called_at;
--   DROP INDEX IF EXISTS uq_ai_call_budgets_period_null_provider;
--   DROP TABLE IF EXISTS mcp_calls;
--   DROP TABLE IF EXISTS ai_call_budgets;
-- =============================================================================

-- ─────────────────────────────────────────────────────────────────────────────
-- mcp_calls — MCP Server 呼叫遙測（Phase 10 預備）
-- ─────────────────────────────────────────────────────────────────────────────
CREATE TABLE IF NOT EXISTS mcp_calls (
    id              BIGSERIAL    PRIMARY KEY,
    called_at       TIMESTAMPTZ  NOT NULL DEFAULT NOW(),

    -- 與 ai_calls.caller 同一張白名單，便於跨表 trace
    caller          VARCHAR(64)  NOT NULL,

    -- omnisearch / firecrawl / postgres / playwright / filesystem / git
    server          VARCHAR(64)  NOT NULL,

    -- search / scrape / query / read_file / git_log / ...
    tool            VARCHAR(128) NOT NULL,

    input_args      JSONB,
    output_size     INTEGER,                          -- bytes，異常巨大可警示
    duration_ms     INTEGER,
    -- M1: NOT NULL 對齊 ai_calls
    status          VARCHAR(16)   NOT NULL,
    error           TEXT,
    cost_usd        NUMERIC(10,6) NOT NULL DEFAULT 0,
    cache_hit       BOOLEAN       NOT NULL DEFAULT FALSE,

    -- M6: 跨 ai_calls/mcp_calls 串鏈用（Phase 10 後 LLM→MCP→LLM 鏈不可斷）
    request_id      VARCHAR(64),

    -- 軟連結：若 MCP 結果被 embed 寫入 ai_insights，記錄 insight_id 但不加 FK
    insight_id      BIGINT,

    -- ─────── critic-A11 修補：白名單 + PII/膨脹護欄 ───────
    -- M1: status 白名單
    CONSTRAINT chk_mcp_calls_status CHECK (
        status IN ('ok','error','timeout','rate_limited','cache_only')
    ),
    -- L3: duration 範圍
    CONSTRAINT chk_mcp_calls_duration_range CHECK (
        duration_ms IS NULL OR (duration_ms >= 0 AND duration_ms <= 600000)
    ),
    -- H2: input_args / error 大小護欄（postgres-mcp 可能含 SQL，含 PII 風險）
    CONSTRAINT chk_mcp_calls_args_size CHECK (
        input_args IS NULL OR octet_length(input_args::text) <= 16384
    ),
    CONSTRAINT chk_mcp_calls_error_size CHECK (
        error IS NULL OR octet_length(error) <= 4096
    )
);

CREATE INDEX IF NOT EXISTS idx_mcp_calls_called_at
    ON mcp_calls (called_at DESC);

CREATE INDEX IF NOT EXISTS idx_mcp_calls_caller_called_at
    ON mcp_calls (caller, called_at DESC);

CREATE INDEX IF NOT EXISTS idx_mcp_calls_server_tool
    ON mcp_calls (server, tool, called_at DESC);

-- M5: 異常監控 partial 精確列舉
CREATE INDEX IF NOT EXISTS idx_mcp_calls_status_called_at
    ON mcp_calls (status, called_at DESC)
    WHERE status IN ('error','timeout','rate_limited');

-- M6: request_id 串鏈（部分索引，sparse 不全建）
CREATE INDEX IF NOT EXISTS idx_mcp_calls_request_id
    ON mcp_calls (request_id)
    WHERE request_id IS NOT NULL;

GRANT ALL PRIVILEGES ON mcp_calls TO momo;
GRANT USAGE, SELECT ON SEQUENCE mcp_calls_id_seq TO momo;

-- ─────────────────────────────────────────────────────────────────────────────
-- ai_call_budgets — 預算與告警閾值（Phase 9 預算守門）
-- ─────────────────────────────────────────────────────────────────────────────
CREATE TABLE IF NOT EXISTS ai_call_budgets (
    id          SERIAL       PRIMARY KEY,
    period      VARCHAR(16)  NOT NULL,               -- daily / weekly / monthly
    provider    VARCHAR(32),                         -- NULL = 全供應商總額
    budget_usd  NUMERIC(10,2) NOT NULL,
    alert_pct   INTEGER      NOT NULL DEFAULT 80,    -- 達此百分比觸發 Telegram 告警
    updated_at  TIMESTAMPTZ  DEFAULT NOW(),

    CONSTRAINT chk_ai_budget_period
        CHECK (period IN ('daily', 'weekly', 'monthly')),
    CONSTRAINT chk_ai_budget_alert_pct
        CHECK (alert_pct BETWEEN 1 AND 100),
    CONSTRAINT chk_ai_budget_amount
        CHECK (budget_usd > 0)
);

-- 部分唯一索引：分別處理 provider IS NULL 與 NOT NULL，避免 NULL != NULL 漏洞
CREATE UNIQUE INDEX IF NOT EXISTS uq_ai_call_budgets_period_provider
    ON ai_call_budgets (period, provider)
    WHERE provider IS NOT NULL;

CREATE UNIQUE INDEX IF NOT EXISTS uq_ai_call_budgets_period_null_provider
    ON ai_call_budgets (period)
    WHERE provider IS NULL;

GRANT ALL PRIVILEGES ON ai_call_budgets TO momo;
GRANT USAGE, SELECT ON SEQUENCE ai_call_budgets_id_seq TO momo;

-- ─────────────────────────────────────────────────────────────────────────────
-- 種子資料（戰役 v5.0 規格 + critic-A11 H3 補 nim/nim_via_elephant/ollama）
-- M2: ON CONFLICT 配 partial unique index 會炸；改用 WHERE NOT EXISTS 確保冪等
-- ─────────────────────────────────────────────────────────────────────────────

-- 全供應商總額（period, provider=NULL）
INSERT INTO ai_call_budgets (period, provider, budget_usd, alert_pct)
SELECT 'daily', NULL, 1.00, 80
WHERE NOT EXISTS (
    SELECT 1 FROM ai_call_budgets WHERE period='daily' AND provider IS NULL
);

INSERT INTO ai_call_budgets (period, provider, budget_usd, alert_pct)
SELECT 'weekly', NULL, 5.00, 80
WHERE NOT EXISTS (
    SELECT 1 FROM ai_call_budgets WHERE period='weekly' AND provider IS NULL
);

INSERT INTO ai_call_budgets (period, provider, budget_usd, alert_pct)
SELECT 'monthly', NULL, 20.00, 80
WHERE NOT EXISTS (
    SELECT 1 FROM ai_call_budgets WHERE period='monthly' AND provider IS NULL
);

-- 個別供應商（含 H3 修補：補 nim / nim_via_elephant / ollama 雙線）
INSERT INTO ai_call_budgets (period, provider, budget_usd, alert_pct)
SELECT 'monthly', 'claude', 10.00, 80
WHERE NOT EXISTS (
    SELECT 1 FROM ai_call_budgets WHERE period='monthly' AND provider='claude'
);

INSERT INTO ai_call_budgets (period, provider, budget_usd, alert_pct)
SELECT 'monthly', 'gemini', 8.00, 80
WHERE NOT EXISTS (
    SELECT 1 FROM ai_call_budgets WHERE period='monthly' AND provider='gemini'
);

-- H3: NIM 兩條獨立計費鏈（NemoTron 配額 + ElephantAlpha 49B），各設預算
INSERT INTO ai_call_budgets (period, provider, budget_usd, alert_pct)
SELECT 'monthly', 'nim', 5.00, 80
WHERE NOT EXISTS (
    SELECT 1 FROM ai_call_budgets WHERE period='monthly' AND provider='nim'
);

INSERT INTO ai_call_budgets (period, provider, budget_usd, alert_pct)
SELECT 'monthly', 'nim_via_elephant', 5.00, 80
WHERE NOT EXISTS (
    SELECT 1 FROM ai_call_budgets WHERE period='monthly' AND provider='nim_via_elephant'
);

-- H3: OpenRouter（PPT deepseek-v3.2）
INSERT INTO ai_call_budgets (period, provider, budget_usd, alert_pct)
SELECT 'monthly', 'openrouter', 3.00, 80
WHERE NOT EXISTS (
    SELECT 1 FROM ai_call_budgets WHERE period='monthly' AND provider='openrouter'
);

-- Ollama 雙線（免費，但設極低預算 + alert=100% 統一告警邏輯，異常激增可警示）
INSERT INTO ai_call_budgets (period, provider, budget_usd, alert_pct)
SELECT 'monthly', 'gcp_ollama', 0.01, 100
WHERE NOT EXISTS (
    SELECT 1 FROM ai_call_budgets WHERE period='monthly' AND provider='gcp_ollama'
);

INSERT INTO ai_call_budgets (period, provider, budget_usd, alert_pct)
SELECT 'monthly', 'ollama_111', 0.01, 100
WHERE NOT EXISTS (
    SELECT 1 FROM ai_call_budgets WHERE period='monthly' AND provider='ollama_111'
);

DO $$
BEGIN
    RAISE NOTICE 'Migration 025 done: mcp_calls + ai_call_budgets + 10 seed budgets (Operation Ollama-First v5.0 P1, critic-A11 fixes B2/H1/H2/H3/M1/M2/M5/M6/L3 applied)';
END $$;
