-- ==========================================
-- Chat Switchboard — 003 Providers & Routing
-- ==========================================
-- Provider configs, model catalog, pricing, health, capability
-- overrides, and routing policies.
-- Consolidated v0.28.0: adds capability_match policy type.
-- ==========================================

-- =========================================
-- PROVIDER CONFIGS
-- =========================================

CREATE TABLE IF NOT EXISTS provider_configs (
    id              UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    scope           VARCHAR(10) NOT NULL
                    CHECK (scope IN ('global', 'team', 'personal')),
    owner_id        UUID,
    name            VARCHAR(100) NOT NULL,
    provider        VARCHAR(50) NOT NULL,
    endpoint        TEXT NOT NULL,

    -- Encrypted API key (AES-256-GCM)
    api_key_enc     BYTEA,
    key_nonce       BYTEA,
    key_scope       TEXT NOT NULL DEFAULT 'global',

    model_default   VARCHAR(100),
    config          JSONB DEFAULT '{}'::jsonb,
    headers         JSONB DEFAULT '{}'::jsonb,
    settings        JSONB DEFAULT '{}'::jsonb,
    is_active       BOOLEAN DEFAULT true,
    is_private      BOOLEAN DEFAULT false,
    proxy_mode      TEXT NOT NULL DEFAULT 'system'
                    CHECK (proxy_mode IN ('system', 'direct', 'custom')),
    proxy_url       TEXT,
    created_at      TIMESTAMPTZ DEFAULT NOW(),
    updated_at      TIMESTAMPTZ DEFAULT NOW(),
    CONSTRAINT chk_proxy_url CHECK (proxy_mode != 'custom' OR proxy_url IS NOT NULL)
);

CREATE INDEX IF NOT EXISTS idx_provider_configs_scope ON provider_configs(scope);
CREATE INDEX IF NOT EXISTS idx_provider_configs_owner ON provider_configs(owner_id) WHERE owner_id IS NOT NULL;
CREATE INDEX IF NOT EXISTS idx_provider_configs_active ON provider_configs(is_active) WHERE is_active = true;
DROP TRIGGER IF EXISTS provider_configs_updated_at ON provider_configs;
CREATE TRIGGER provider_configs_updated_at BEFORE UPDATE ON provider_configs
    FOR EACH ROW EXECUTE FUNCTION update_updated_at();

COMMENT ON COLUMN provider_configs.scope IS 'global=admin-managed, team=team-scoped, personal=user BYOK';
COMMENT ON COLUMN provider_configs.owner_id IS 'NULL for global; teams.id for team scope; users.id for personal scope';
COMMENT ON COLUMN provider_configs.api_key_enc IS 'AES-256-GCM encrypted API key (BYTEA)';
COMMENT ON COLUMN provider_configs.key_nonce IS 'AES-GCM nonce for api_key_enc';
COMMENT ON COLUMN provider_configs.key_scope IS 'Encryption tier: global/team use env key, personal uses UEK';
COMMENT ON COLUMN provider_configs.headers IS 'Custom HTTP headers (e.g. OpenRouter HTTP-Referer)';
COMMENT ON COLUMN provider_configs.settings IS 'Provider-specific params (e.g. Venice web_search, OpenRouter route)';
COMMENT ON COLUMN provider_configs.is_private IS 'Data stays on-prem (local/self-hosted provider)';
COMMENT ON COLUMN provider_configs.proxy_mode IS 'system=env proxy, direct=no proxy, custom=explicit URL';
COMMENT ON COLUMN provider_configs.proxy_url IS 'Explicit proxy URL when proxy_mode=custom (http/https/socks5)';


-- =========================================
-- MODEL CATALOG
-- =========================================

CREATE TABLE IF NOT EXISTS model_catalog (
    id                  UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    provider_config_id  UUID NOT NULL REFERENCES provider_configs(id) ON DELETE CASCADE,
    model_id            TEXT NOT NULL,
    display_name        TEXT,
    model_type          VARCHAR(20) DEFAULT 'chat',
    capabilities        JSONB NOT NULL DEFAULT '{}',
    pricing             JSONB,
    visibility          VARCHAR(10) DEFAULT 'disabled'
                        CHECK (visibility IN ('enabled', 'disabled', 'team')),
    last_synced_at      TIMESTAMPTZ,
    created_at          TIMESTAMPTZ DEFAULT NOW(),
    updated_at          TIMESTAMPTZ DEFAULT NOW(),
    UNIQUE(provider_config_id, model_id)
);

CREATE INDEX IF NOT EXISTS idx_model_catalog_provider ON model_catalog(provider_config_id);
CREATE INDEX IF NOT EXISTS idx_model_catalog_enabled ON model_catalog(visibility) WHERE visibility = 'enabled';
CREATE INDEX IF NOT EXISTS idx_model_catalog_type ON model_catalog(model_type);
DROP TRIGGER IF EXISTS model_catalog_updated_at ON model_catalog;
CREATE TRIGGER model_catalog_updated_at BEFORE UPDATE ON model_catalog
    FOR EACH ROW EXECUTE FUNCTION update_updated_at();

COMMENT ON COLUMN model_catalog.visibility IS 'disabled by default — admin must enable for global models';
COMMENT ON COLUMN model_catalog.model_type IS 'chat, embedding, image — from provider API sync';
COMMENT ON COLUMN model_catalog.capabilities IS 'Intrinsic: {"streaming","tool_calling","vision","thinking","reasoning","code_optimized","web_search","max_context","max_output_tokens"}';


-- =========================================
-- MODEL PRICING
-- =========================================

CREATE TABLE IF NOT EXISTS model_pricing (
    id                  UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    provider_config_id  UUID NOT NULL REFERENCES provider_configs(id) ON DELETE CASCADE,
    model_id            TEXT NOT NULL,
    input_per_m         NUMERIC(10,6),
    output_per_m        NUMERIC(10,6),
    cache_create_per_m  NUMERIC(10,6),
    cache_read_per_m    NUMERIC(10,6),
    currency            TEXT NOT NULL DEFAULT 'USD',
    source              TEXT NOT NULL DEFAULT 'manual',
    updated_at          TIMESTAMPTZ DEFAULT NOW(),
    updated_by          UUID REFERENCES users(id),
    UNIQUE(provider_config_id, model_id)
);


-- =========================================
-- PROVIDER HEALTH
-- =========================================

CREATE TABLE IF NOT EXISTS provider_health (
    id                  UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    provider_config_id  UUID NOT NULL REFERENCES provider_configs(id) ON DELETE CASCADE,
    window_start        TIMESTAMPTZ NOT NULL,
    request_count       INT NOT NULL DEFAULT 0,
    error_count         INT NOT NULL DEFAULT 0,
    timeout_count       INT NOT NULL DEFAULT 0,
    total_latency_ms    BIGINT NOT NULL DEFAULT 0,
    max_latency_ms      INT NOT NULL DEFAULT 0,
    rate_limit_count    INT NOT NULL DEFAULT 0,
    last_error          TEXT,
    last_error_at       TIMESTAMPTZ,
    updated_at          TIMESTAMPTZ NOT NULL DEFAULT now(),

    UNIQUE (provider_config_id, window_start)
);

CREATE INDEX IF NOT EXISTS idx_provider_health_window
    ON provider_health (provider_config_id, window_start DESC);


-- =========================================
-- CAPABILITY OVERRIDES
-- =========================================

CREATE TABLE IF NOT EXISTS capability_overrides (
    id                  UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    provider_config_id  UUID REFERENCES provider_configs(id) ON DELETE CASCADE,
    model_id            TEXT NOT NULL,
    field               TEXT NOT NULL,
    value               TEXT NOT NULL,
    set_by              UUID REFERENCES users(id) ON DELETE SET NULL,
    created_at          TIMESTAMPTZ NOT NULL DEFAULT now(),

    UNIQUE (provider_config_id, model_id, field)
);

CREATE INDEX IF NOT EXISTS idx_capability_overrides_model
    ON capability_overrides (model_id);


-- =========================================
-- ROUTING POLICIES
-- =========================================

CREATE TABLE IF NOT EXISTS routing_policies (
    id              UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    name            VARCHAR(200) NOT NULL,
    scope           VARCHAR(10) NOT NULL DEFAULT 'global'
                    CHECK (scope IN ('global', 'team')),
    team_id         UUID REFERENCES teams(id) ON DELETE CASCADE,
    priority        INTEGER NOT NULL DEFAULT 100,
    policy_type     VARCHAR(30) NOT NULL
                    CHECK (policy_type IN ('provider_prefer', 'team_route', 'cost_limit', 'model_alias', 'capability_match')),
    config          JSONB NOT NULL DEFAULT '{}'::jsonb,
    is_active       BOOLEAN NOT NULL DEFAULT true,
    created_at      TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    updated_at      TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

CREATE INDEX IF NOT EXISTS idx_routing_policies_active
    ON routing_policies(is_active, priority) WHERE is_active = true;
CREATE INDEX IF NOT EXISTS idx_routing_policies_team
    ON routing_policies(team_id) WHERE team_id IS NOT NULL;

DROP TRIGGER IF EXISTS routing_policies_updated_at ON routing_policies;
CREATE TRIGGER routing_policies_updated_at BEFORE UPDATE ON routing_policies
    FOR EACH ROW EXECUTE FUNCTION update_updated_at();
