-- Sprint 1: 数据层补全
-- 将数据库从 3 张表升级到 8 张完整表 + audit_log
-- 日期: 2026-05-10
-- 负责人: 宰相

-- ============================================================
-- 一、新表创建
-- ============================================================

-- 1.1 model_provider: 模型厂商表
CREATE TABLE IF NOT EXISTS model_provider (
    id              BIGSERIAL PRIMARY KEY,
    name            TEXT NOT NULL UNIQUE,           -- "OpenAI", "百度", "DeepSeek"
    name_cn         TEXT,                            -- 中文名
    country         TEXT NOT NULL DEFAULT 'unknown', -- "US" / "CN" / "EU"
    website         TEXT,
    founded_year    INTEGER,
    description     TEXT,
    logo_url        TEXT,
    status          TEXT NOT NULL DEFAULT 'active', -- active / deprecated
    created_at      TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
    updated_at      TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
    created_by      TEXT DEFAULT 'system',
    updated_by      TEXT DEFAULT 'system',
    deleted_at      TIMESTAMP
);

CREATE INDEX IF NOT EXISTS idx_provider_country ON model_provider(country);
CREATE INDEX IF NOT EXISTS idx_provider_status ON model_provider(status);
CREATE INDEX IF NOT EXISTS idx_provider_deleted ON model_provider(deleted_at) WHERE deleted_at IS NOT NULL;

COMMENT ON TABLE model_provider IS '模型厂商/开发商信息';

-- 1.2 operator: 运营商/云平台表
CREATE TABLE IF NOT EXISTS operator (
    id              BIGSERIAL PRIMARY KEY,
    name            TEXT NOT NULL UNIQUE,           -- "阿里云", "AWS", "OpenRouter"
    name_cn         TEXT,
    country         TEXT NOT NULL DEFAULT 'unknown',
    website         TEXT,
    description     TEXT,
    status          TEXT NOT NULL DEFAULT 'active',
    created_at      TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
    updated_at      TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
    created_by      TEXT DEFAULT 'system',
    updated_by      TEXT DEFAULT 'system',
    deleted_at      TIMESTAMP
);

CREATE INDEX IF NOT EXISTS idx_operator_country ON operator(country);
CREATE INDEX IF NOT EXISTS idx_operator_status ON operator(status);

COMMENT ON TABLE operator IS '模型运营平台/云服务商';

-- 1.3 region_pricing: 区域定价表（替代 model_prices）
CREATE TABLE IF NOT EXISTS region_pricing (
    id                    BIGSERIAL PRIMARY KEY,
    model_id              BIGINT NOT NULL REFERENCES models(id) ON DELETE CASCADE,
    operator_id           BIGINT REFERENCES operator(id) ON DELETE SET NULL,
    region                TEXT NOT NULL DEFAULT 'global', -- global / cn / us / eu
    currency              TEXT NOT NULL DEFAULT 'USD',
    input_price_per_mtok  REAL NOT NULL DEFAULT 0,
    output_price_per_mtok REAL NOT NULL DEFAULT 0,
    request_price         REAL,                        -- 按请求计费
    effective_date        DATE NOT NULL DEFAULT CURRENT_DATE,
    is_free               BOOLEAN NOT NULL DEFAULT FALSE,
    source_url            TEXT,
    notes                 TEXT,
    created_at            TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
    updated_at            TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
    created_by            TEXT DEFAULT 'system',
    updated_by            TEXT DEFAULT 'system',
    UNIQUE(model_id, operator_id, region, currency, effective_date),
    -- CHECK 约束
    CONSTRAINT chk_price_non_negative CHECK (input_price_per_mtok >= 0 AND output_price_per_mtok >= 0),
    CONSTRAINT chk_currency_valid CHECK (currency IN ('CNY', 'USD', 'EUR'))
);

CREATE INDEX IF NOT EXISTS idx_region_pricing_model_id ON region_pricing(model_id);
CREATE INDEX IF NOT EXISTS idx_region_pricing_region ON region_pricing(region);
CREATE INDEX IF NOT EXISTS idx_region_pricing_currency ON region_pricing(currency);
CREATE INDEX IF NOT EXISTS idx_region_pricing_is_free ON region_pricing(is_free);
CREATE INDEX IF NOT EXISTS idx_region_pricing_effective ON region_pricing(effective_date);

COMMENT ON TABLE region_pricing IS '模型区域定价信息（含CNY/USD/EUR）';

-- 1.4 pricing_history: 价格变动历史
CREATE TABLE IF NOT EXISTS pricing_history (
    id              BIGSERIAL PRIMARY KEY,
    model_id        BIGINT NOT NULL REFERENCES models(id) ON DELETE CASCADE,
    region          TEXT NOT NULL DEFAULT 'global',
    currency        TEXT NOT NULL DEFAULT 'USD',
    old_input_price REAL,
    new_input_price REAL,
    old_output_price REAL,
    new_output_price REAL,
    change_percent  REAL,                            -- 变动百分比
    changed_at      TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
    reason          TEXT,                            -- 变动原因（如官方公告）
    created_at      TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);

CREATE INDEX IF NOT EXISTS idx_pricing_history_model ON pricing_history(model_id);
CREATE INDEX IF NOT EXISTS idx_pricing_history_changed ON pricing_history(changed_at);

COMMENT ON TABLE pricing_history IS '模型价格变动历史追踪';

-- 1.5 free_tier: 免费政策库
CREATE TABLE IF NOT EXISTS free_tier (
    id              BIGSERIAL PRIMARY KEY,
    model_id        BIGINT NOT NULL REFERENCES models(id) ON DELETE CASCADE,
    operator_id     BIGINT REFERENCES operator(id) ON DELETE SET NULL,
    free_type       TEXT NOT NULL DEFAULT 'limited', -- unlimited / limited / trial
    max_requests    INTEGER,                         -- 免费请求次数上限
    max_tokens      BIGINT,                          -- 免费Token上限
    expiration_date DATE,                            -- 免费政策到期日
    description     TEXT,
    source_url      TEXT,
    created_at      TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
    updated_at      TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
    UNIQUE(model_id, operator_id)
);

CREATE INDEX IF NOT EXISTS idx_free_tier_model ON free_tier(model_id);
CREATE INDEX IF NOT EXISTS idx_free_tier_type ON free_tier(free_type);

COMMENT ON TABLE free_tier IS '模型免费政策/额度信息';

-- 1.6 daily_report: 日报存储（替代 report_runs）
CREATE TABLE IF NOT EXISTS daily_report (
    id              BIGSERIAL PRIMARY KEY,
    report_date     DATE NOT NULL UNIQUE,
    status          TEXT NOT NULL DEFAULT 'pending', -- pending / generated / failed
    model_count     INTEGER,                         -- 当日模型总数
    new_models      INTEGER DEFAULT 0,              -- 新上线模型数
    price_changes   INTEGER DEFAULT 0,              -- 价格变动数
    free_models     INTEGER DEFAULT 0,              -- 免费模型数
    summary_md      TEXT,                            -- Markdown摘要
    output_path     TEXT,
    error_message   TEXT,
    created_at      TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
    updated_at      TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);

CREATE INDEX IF NOT EXISTS idx_daily_report_date ON daily_report(report_date);
CREATE INDEX IF NOT EXISTS idx_daily_report_status ON daily_report(status);

COMMENT ON TABLE daily_report IS '每日自动报告运行记录';

-- 1.7 user_subscription: 用户订阅（Phase 2 基础）
CREATE TABLE IF NOT EXISTS user_subscription (
    id              BIGSERIAL PRIMARY KEY,
    user_id         TEXT NOT NULL,
    subscribe_type  TEXT NOT NULL DEFAULT 'daily',   -- daily / price_alert / new_model
    model_id        BIGINT REFERENCES models(id) ON DELETE SET NULL,
    provider_id     BIGINT REFERENCES model_provider(id) ON DELETE SET NULL,
    threshold       REAL,                            -- 价格变动阈值(%)
    is_active       BOOLEAN NOT NULL DEFAULT TRUE,
    created_at      TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
    updated_at      TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
    UNIQUE(user_id, subscribe_type, model_id, provider_id)
);

CREATE INDEX IF NOT EXISTS idx_subscription_user ON user_subscription(user_id);
CREATE INDEX IF NOT EXISTS idx_subscription_active ON user_subscription(is_active);

COMMENT ON TABLE user_subscription IS '用户订阅配置（Phase 2）';

-- 1.8 audit_log: 审计日志【新增】
CREATE TABLE IF NOT EXISTS audit_log (
    id              BIGSERIAL PRIMARY KEY,
    table_name      TEXT NOT NULL,
    record_id       BIGINT NOT NULL,
    field_name      TEXT,
    old_value       TEXT,
    new_value       TEXT,
    operation       TEXT NOT NULL,                   -- INSERT / UPDATE / DELETE
    operator        TEXT DEFAULT 'system',            -- 操作人/采集器标识
    batch_id        TEXT,                            -- 采集批次号
    source_url      TEXT,
    created_at      TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);

CREATE INDEX IF NOT EXISTS idx_audit_table ON audit_log(table_name);
CREATE INDEX IF NOT EXISTS idx_audit_record ON audit_log(record_id);
CREATE INDEX IF NOT EXISTS idx_audit_batch ON audit_log(batch_id);
CREATE INDEX IF NOT EXISTS idx_audit_created ON audit_log(created_at);

COMMENT ON TABLE audit_log IS '数据变更审计日志（血缘追踪）';

-- ============================================================
-- 二、现有表字段扩充
-- ============================================================

-- 2.1 修改 models 表，添加 TECHNICAL_DESIGN 中定义的字段
DO $$
BEGIN
    -- 添加 provider_id 外键字段
    IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name='models' AND column_name='provider_id') THEN
        ALTER TABLE models ADD COLUMN provider_id BIGINT REFERENCES model_provider(id) ON DELETE SET NULL;
    END IF;
    
    -- 添加 version 字段
    IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name='models' AND column_name='version') THEN
        ALTER TABLE models ADD COLUMN version TEXT;
    END IF;
    
    -- 添加 modality 字段
    IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name='models' AND column_name='modality') THEN
        ALTER TABLE models ADD COLUMN modality TEXT NOT NULL DEFAULT 'text';
    END IF;
    
    -- 添加 release_date 字段
    IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name='models' AND column_name='release_date') THEN
        ALTER TABLE models ADD COLUMN release_date DATE;
    END IF;
    
    -- 添加 elo_score 字段
    IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name='models' AND column_name='elo_score') THEN
        ALTER TABLE models ADD COLUMN elo_score REAL;
    END IF;
    
    -- 添加 benchmark_scores 字段
    IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name='models' AND column_name='benchmark_scores') THEN
        ALTER TABLE models ADD COLUMN benchmark_scores JSONB;
    END IF;
    
    -- 添加 data_confidence 字段
    IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name='models' AND column_name='data_confidence') THEN
        ALTER TABLE models ADD COLUMN data_confidence TEXT DEFAULT 'official';
    END IF;
    
    -- 添加 retrieved_at 字段
    IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name='models' AND column_name='retrieved_at') THEN
        ALTER TABLE models ADD COLUMN retrieved_at TIMESTAMP;
    END IF;
    
    -- 添加 batch_id 字段（血缘追踪）
    IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name='models' AND column_name='batch_id') THEN
        ALTER TABLE models ADD COLUMN batch_id TEXT;
    END IF;
    
    -- 添加 collector_version 字段
    IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name='models' AND column_name='collector_version') THEN
        ALTER TABLE models ADD COLUMN collector_version TEXT DEFAULT 'v1.0';
    END IF;
    
    -- 添加 source_url 字段
    IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name='models' AND column_name='source_url') THEN
        ALTER TABLE models ADD COLUMN source_url TEXT;
    END IF;
    
    -- 添加审计字段
    IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name='models' AND column_name='created_by') THEN
        ALTER TABLE models ADD COLUMN created_by TEXT DEFAULT 'system';
    END IF;
    
    IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name='models' AND column_name='updated_by') THEN
        ALTER TABLE models ADD COLUMN updated_by TEXT DEFAULT 'system';
    END IF;
    
    IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name='models' AND column_name='deleted_at') THEN
        ALTER TABLE models ADD COLUMN deleted_at TIMESTAMP;
    END IF;
END $$;

-- 2.2 为 models 表添加 CHECK 约束
DO $$
BEGIN
    IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname='chk_models_context_length') THEN
        ALTER TABLE models ADD CONSTRAINT chk_models_context_length CHECK (context_length IS NULL OR context_length <= 10000000);
    END IF;
    
    IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname='chk_models_modality') THEN
        ALTER TABLE models ADD CONSTRAINT chk_models_modality CHECK (modality IN ('text', 'vision', 'audio', 'video', 'code', 'multimodal'));
    END IF;
    
    IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname='chk_models_data_confidence') THEN
        ALTER TABLE models ADD CONSTRAINT chk_models_data_confidence CHECK (data_confidence IN ('official', 'inferred', 'unverified', 'stale'));
    END IF;
END $$;

-- 2.3 创建 models 表的新索引
CREATE INDEX IF NOT EXISTS idx_models_provider_id ON models(provider_id);
CREATE INDEX IF NOT EXISTS idx_models_modality ON models(modality);
CREATE INDEX IF NOT EXISTS idx_models_data_confidence ON models(data_confidence);
CREATE INDEX IF NOT EXISTS idx_models_retrieved_at ON models(retrieved_at);
CREATE INDEX IF NOT EXISTS idx_models_batch_id ON models(batch_id);

-- ============================================================
-- 三、审计触发器（自动更新 updated_at）
-- ============================================================

-- 3.1 创建触发器函数
CREATE OR REPLACE FUNCTION update_updated_at_column()
RETURNS TRIGGER AS $$
BEGIN
    NEW.updated_at = CURRENT_TIMESTAMP;
    RETURN NEW;
END;
$$ language 'plpgsql';

-- 3.2 为所有业务表创建触发器
DO $$
DECLARE
    tbl TEXT;
    tables TEXT[] := ARRAY['models', 'model_provider', 'operator', 'region_pricing', 
                           'pricing_history', 'free_tier', 'daily_report', 
                           'user_subscription'];
BEGIN
    FOREACH tbl IN ARRAY tables
    LOOP
        IF NOT EXISTS (SELECT 1 FROM pg_trigger WHERE tgname = tbl || '_updated_at') THEN
            EXECUTE format('CREATE TRIGGER %I_updated_at BEFORE UPDATE ON %I FOR EACH ROW EXECUTE FUNCTION update_updated_at_column()', tbl, tbl);
        END IF;
    END LOOP;
END $$;

-- ============================================================
-- 四、数据迁移（model_prices → region_pricing）
-- ============================================================

-- 4.1 先创建默认 operator（OpenRouter）
INSERT INTO operator (name, name_cn, country, description)
VALUES ('OpenRouter', 'OpenRouter', 'US', 'OpenRouter API聚合平台')
ON CONFLICT (name) DO NOTHING;

-- 4.2 迁移 model_prices 数据到 region_pricing
INSERT INTO region_pricing (
    model_id, operator_id, region, currency, 
    input_price_per_mtok, output_price_per_mtok, 
    is_free, effective_date, source_url, created_at
)
SELECT 
    mp.model_id,
    (SELECT id FROM operator WHERE name = 'OpenRouter' LIMIT 1),
    'global',
    mp.currency,
    COALESCE(mp.input_price_per_mtok, 0),
    COALESCE(mp.output_price_per_mtok, 0),
    COALESCE(m.is_free, FALSE),
    COALESCE(mp.effective_date, CURRENT_DATE),
    mp.source_url,
    mp.created_at
FROM model_prices mp
JOIN models m ON mp.model_id = m.id
ON CONFLICT (model_id, operator_id, region, currency, effective_date) DO NOTHING;

-- ============================================================
-- 五、迁移 report_runs → daily_report
-- ============================================================

INSERT INTO daily_report (report_date, status, output_path, error_message, created_at)
SELECT report_date, status, output_path, error_message, created_at
FROM report_runs
ON CONFLICT (report_date) DO NOTHING;

-- ============================================================
-- 六、完成标记
-- ============================================================

SELECT 'Sprint 1 Schema Migration Complete' AS status;
