-- ==========================================
-- Chat Switchboard — 003 Providers & Routing
-- ==========================================
-- Provider configs, model catalog, pricing, health, capability
-- overrides, and routing policies.
-- ICD §10 (Providers & Routing), §11 (Models & Preferences)
-- ==========================================

-- =========================================
-- 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,
    created_at      TIMESTAMPTZ DEFAULT NOW(),
    updated_at      TIMESTAMPTZ DEFAULT NOW()
);

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)';


-- =========================================
-- 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 (v0.22.0)
-- =========================================

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 (v0.22.0)
-- =========================================

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 (v0.22.2)
-- =========================================

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')),
    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();
