-- ==========================================
-- Chat Switchboard — 001 Core
-- ==========================================
-- Users, authentication, platform policies, and global settings.
-- Foundation tables with no external FK dependencies.
--
-- ICD §2 (Auth), §14 (User Profile & Settings), §16 (Platform Admin)
-- v0.22.8: Domain-grouped migration (replaces monolith 001 + incrementals)
-- ==========================================

-- ── Extensions ──────────────────────────────

CREATE EXTENSION IF NOT EXISTS "pgcrypto";     -- gen_random_uuid()
CREATE EXTENSION IF NOT EXISTS "vector";       -- pgvector (KB embeddings)

-- ── Utility: auto-update updated_at ─────────

CREATE OR REPLACE FUNCTION update_updated_at()
RETURNS TRIGGER AS $$
BEGIN
    NEW.updated_at = NOW();
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;


-- =========================================
-- USERS
-- =========================================

CREATE TABLE IF NOT EXISTS users (
    id              UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    username        VARCHAR(50) NOT NULL,
    email           VARCHAR(255) NOT NULL,
    password_hash   TEXT NOT NULL,
    display_name    VARCHAR(100),
    avatar_url      TEXT,
    role            VARCHAR(20) DEFAULT 'user'
                    CHECK (role IN ('user', 'admin')),
    is_active       BOOLEAN DEFAULT true,
    settings        JSONB DEFAULT '{}'::jsonb,

    -- Vault: per-user encryption key (UEK) for BYOK API keys
    encrypted_uek   BYTEA,
    uek_salt        BYTEA,
    uek_nonce       BYTEA,
    vault_set       BOOLEAN NOT NULL DEFAULT false,

    created_at      TIMESTAMPTZ DEFAULT NOW(),
    updated_at      TIMESTAMPTZ DEFAULT NOW(),
    last_login_at   TIMESTAMPTZ
);

-- Case-insensitive unique indexes (no bare UNIQUE constraint)
CREATE UNIQUE INDEX IF NOT EXISTS users_username_ci ON users (LOWER(username));
CREATE UNIQUE INDEX IF NOT EXISTS users_email_ci ON users (LOWER(email));

CREATE INDEX IF NOT EXISTS idx_users_username ON users(username);
CREATE INDEX IF NOT EXISTS idx_users_email ON users(email);

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

COMMENT ON COLUMN users.encrypted_uek IS 'UEK encrypted with Argon2id(password)-derived key';
COMMENT ON COLUMN users.uek_salt      IS 'Argon2id salt for UEK derivation';
COMMENT ON COLUMN users.uek_nonce     IS 'AES-GCM nonce for UEK wrapping';
COMMENT ON COLUMN users.vault_set     IS 'true once UEK has been generated and wrapped';


-- =========================================
-- AUTH — REFRESH TOKENS
-- =========================================

CREATE TABLE IF NOT EXISTS refresh_tokens (
    id          UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    user_id     UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
    token_hash  TEXT NOT NULL UNIQUE,
    expires_at  TIMESTAMPTZ NOT NULL,
    created_at  TIMESTAMPTZ DEFAULT NOW(),
    revoked_at  TIMESTAMPTZ
);

CREATE INDEX IF NOT EXISTS idx_refresh_tokens_user ON refresh_tokens(user_id);
CREATE INDEX IF NOT EXISTS idx_refresh_tokens_hash ON refresh_tokens(token_hash) WHERE revoked_at IS NULL;


-- =========================================
-- PLATFORM POLICIES
-- =========================================

CREATE TABLE IF NOT EXISTS platform_policies (
    key         VARCHAR(50) PRIMARY KEY,
    value       TEXT NOT NULL,
    updated_by  UUID REFERENCES users(id),
    updated_at  TIMESTAMPTZ DEFAULT NOW()
);

-- Secure by default
INSERT INTO platform_policies (key, value) VALUES
    ('allow_user_byok',         'false'),
    ('allow_user_personas',     'false'),
    ('allow_raw_model_access',  'true'),
    ('allow_registration',      'true'),
    ('default_user_active',     'false'),
    ('allow_team_providers',    'true'),
    ('kb_direct_access',        'true')
ON CONFLICT (key) DO NOTHING;

COMMENT ON TABLE platform_policies IS 'Global admin switches controlling platform behavior';


-- =========================================
-- GLOBAL SETTINGS
-- =========================================

CREATE TABLE IF NOT EXISTS global_settings (
    key         VARCHAR(100) PRIMARY KEY,
    value       JSONB NOT NULL DEFAULT '{}'::jsonb,
    updated_at  TIMESTAMPTZ DEFAULT NOW(),
    updated_by  UUID REFERENCES users(id)
);

INSERT INTO global_settings (key, value) VALUES
    ('registration', '{"enabled": true}'::jsonb),
    ('site', '{"name": "Chat Switchboard", "tagline": "Multi-Model AI Chat"}'::jsonb),
    ('banner', '{"enabled": false, "text": "", "bg": "#007a33", "fg": "#ffffff"}'::jsonb),
    ('model_roles', '{
        "utility":    { "primary": null, "fallback": null },
        "embedding":  { "primary": null, "fallback": null },
        "generation": { "primary": null, "fallback": null }
    }'::jsonb)
ON CONFLICT (key) DO NOTHING;
