-- ==========================================
-- Chat Switchboard — 005 Channels & Conversations
-- ==========================================
-- Channels, messages, participants, models, cursors, folders, user prefs.
-- ICD §3 (Channels & Conversations)
-- Note: project_id and workspace_id FKs added by 010_projects.sql
-- ==========================================

-- =========================================
-- FOLDERS
-- =========================================

CREATE TABLE IF NOT EXISTS folders (
    id              UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    user_id         UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
    name            VARCHAR(200) NOT NULL,
    parent_id       UUID REFERENCES folders(id) ON DELETE CASCADE,
    sort_order      INT DEFAULT 0,
    created_at      TIMESTAMPTZ DEFAULT NOW(),
    updated_at      TIMESTAMPTZ DEFAULT NOW(),
    UNIQUE(user_id, name, parent_id)
);

CREATE INDEX IF NOT EXISTS idx_folders_user ON folders(user_id);
CREATE INDEX IF NOT EXISTS idx_folders_parent ON folders(parent_id);
DROP TRIGGER IF EXISTS folders_updated_at ON folders;
CREATE TRIGGER folders_updated_at BEFORE UPDATE ON folders
    FOR EACH ROW EXECUTE FUNCTION update_updated_at();


-- =========================================
-- CHANNELS
-- =========================================

CREATE TABLE IF NOT EXISTS channels (
    id              UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    user_id         UUID REFERENCES users(id) ON DELETE CASCADE,
    title           VARCHAR(500) NOT NULL,
    description     TEXT,
    type            VARCHAR(20) DEFAULT 'direct'
                    CHECK (type IN ('direct', 'group', 'workflow')),
    model           VARCHAR(100),
    system_prompt   TEXT,
    provider_config_id UUID REFERENCES provider_configs(id) ON DELETE SET NULL,
    is_archived     BOOLEAN DEFAULT false,
    is_pinned       BOOLEAN DEFAULT false,
    folder_id       UUID,
    folder          TEXT,
    team_id         UUID REFERENCES teams(id) ON DELETE SET NULL,
    settings        JSONB DEFAULT '{}'::jsonb,
    tags            TEXT[],
    created_at      TIMESTAMPTZ DEFAULT NOW(),
    updated_at      TIMESTAMPTZ DEFAULT NOW()
);

CREATE INDEX IF NOT EXISTS idx_channels_user ON channels(user_id);
CREATE INDEX IF NOT EXISTS idx_channels_updated ON channels(updated_at DESC);
CREATE INDEX IF NOT EXISTS idx_channels_type ON channels(type);
CREATE INDEX IF NOT EXISTS idx_channels_team ON channels(team_id) WHERE team_id IS NOT NULL;
CREATE INDEX IF NOT EXISTS idx_channels_tags ON channels USING GIN(tags);
DROP TRIGGER IF EXISTS channels_updated_at ON channels;
CREATE TRIGGER channels_updated_at BEFORE UPDATE ON channels
    FOR EACH ROW EXECUTE FUNCTION update_updated_at();

-- Deferred FK: channels.folder_id → folders.id
DO $$ BEGIN
    IF NOT EXISTS (
        SELECT 1 FROM pg_constraint WHERE conname = 'fk_channels_folder'
    ) THEN
        ALTER TABLE channels ADD CONSTRAINT fk_channels_folder
            FOREIGN KEY (folder_id) REFERENCES folders(id) ON DELETE SET NULL;
    END IF;
END $$;
CREATE INDEX IF NOT EXISTS idx_channels_folder ON channels(folder_id) WHERE folder_id IS NOT NULL;

COMMENT ON COLUMN channels.type IS 'direct=single-user chat, group=multi-user/multi-model, workflow=staged channel';


-- =========================================
-- MESSAGES
-- =========================================

CREATE TABLE IF NOT EXISTS messages (
    id              UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    channel_id      UUID NOT NULL REFERENCES channels(id) ON DELETE CASCADE,
    role            VARCHAR(20) NOT NULL
                    CHECK (role IN ('user', 'assistant', 'system', 'tool')),
    content         TEXT NOT NULL,
    model           VARCHAR(100),
    tokens_used     INTEGER,
    tool_calls      JSONB,
    metadata        JSONB DEFAULT '{}'::jsonb,
    parent_id       UUID REFERENCES messages(id) ON DELETE SET NULL,
    sibling_index   INTEGER DEFAULT 0,
    participant_type VARCHAR(10) DEFAULT 'user',
    participant_id  VARCHAR(255),
    provider_config_id UUID REFERENCES provider_configs(id) ON DELETE SET NULL,
    deleted_at      TIMESTAMPTZ,
    created_at      TIMESTAMPTZ DEFAULT NOW()
);

CREATE INDEX IF NOT EXISTS idx_messages_channel ON messages(channel_id, created_at);
CREATE INDEX IF NOT EXISTS idx_messages_parent ON messages(parent_id);
CREATE INDEX IF NOT EXISTS idx_messages_alive ON messages(channel_id, created_at)
    WHERE deleted_at IS NULL;
CREATE INDEX IF NOT EXISTS idx_messages_parent_alive ON messages(parent_id, sibling_index)
    WHERE deleted_at IS NULL;

COMMENT ON COLUMN messages.parent_id IS 'Tree parent for conversation forking';
COMMENT ON COLUMN messages.sibling_index IS 'Position among siblings (0-indexed)';


-- =========================================
-- CHANNEL PARTICIPANTS & MODELS
-- =========================================
-- ICD §3.7: Polymorphic participant system.
-- participant_type determines what participant_id references:
--   user    → users.id
--   persona → personas.id
--   session → opaque session token
-- =========================================

CREATE TABLE IF NOT EXISTS channel_participants (
    id              UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    channel_id      UUID NOT NULL REFERENCES channels(id) ON DELETE CASCADE,
    participant_type VARCHAR(10) NOT NULL DEFAULT 'user'
                    CHECK (participant_type IN ('user', 'persona', 'session')),
    participant_id  UUID NOT NULL,
    role            VARCHAR(10) NOT NULL DEFAULT 'member'
                    CHECK (role IN ('owner', 'member', 'observer')),
    display_name    VARCHAR(200),
    avatar_url      TEXT,
    joined_at       TIMESTAMPTZ DEFAULT NOW(),
    last_read_at    TIMESTAMPTZ DEFAULT NOW(),
    UNIQUE(channel_id, participant_type, participant_id)
);

CREATE INDEX IF NOT EXISTS idx_channel_participants_channel ON channel_participants(channel_id);
CREATE INDEX IF NOT EXISTS idx_channel_participants_ref ON channel_participants(participant_type, participant_id);

CREATE TABLE IF NOT EXISTS channel_models (
    id              UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    channel_id      UUID NOT NULL REFERENCES channels(id) ON DELETE CASCADE,
    model_id        VARCHAR(255) NOT NULL,
    provider_config_id UUID REFERENCES provider_configs(id) ON DELETE SET NULL,
    persona_id      UUID REFERENCES personas(id) ON DELETE SET NULL,
    display_name    VARCHAR(100),
    system_prompt   TEXT,
    settings        JSONB DEFAULT '{}'::jsonb,
    is_default      BOOLEAN DEFAULT false,
    added_at        TIMESTAMPTZ DEFAULT NOW()
);

-- Raw model entries (no persona): unique per channel+model+provider
CREATE UNIQUE INDEX IF NOT EXISTS idx_channel_models_raw
    ON channel_models(channel_id, model_id, provider_config_id) WHERE persona_id IS NULL;
-- Persona entries: one per persona per channel (same model allowed for different personas)
CREATE UNIQUE INDEX IF NOT EXISTS idx_channel_models_persona
    ON channel_models(channel_id, persona_id) WHERE persona_id IS NOT NULL;

CREATE INDEX IF NOT EXISTS idx_channel_models_channel ON channel_models(channel_id);


-- =========================================
-- CHANNEL CURSORS (forking navigation)
-- =========================================

CREATE TABLE IF NOT EXISTS channel_cursors (
    id              UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    channel_id      UUID NOT NULL REFERENCES channels(id) ON DELETE CASCADE,
    user_id         UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
    active_leaf_id  UUID REFERENCES messages(id) ON DELETE SET NULL,
    updated_at      TIMESTAMPTZ DEFAULT NOW(),
    UNIQUE(channel_id, user_id)
);

CREATE INDEX IF NOT EXISTS idx_channel_cursors_channel ON channel_cursors(channel_id);
CREATE INDEX IF NOT EXISTS idx_channel_cursors_user ON channel_cursors(user_id);


-- =========================================
-- USER MODEL SETTINGS
-- =========================================

CREATE TABLE IF NOT EXISTS user_model_settings (
    id              UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    user_id         UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
    model_id        TEXT NOT NULL,
    provider_config_id UUID REFERENCES provider_configs(id) ON DELETE CASCADE,
    hidden          BOOLEAN DEFAULT false,
    preferred_temperature   FLOAT,
    preferred_max_tokens    INT,
    sort_order      INT DEFAULT 0,
    created_at      TIMESTAMPTZ DEFAULT NOW(),
    updated_at      TIMESTAMPTZ DEFAULT NOW(),
    UNIQUE(user_id, model_id, provider_config_id)
);

CREATE INDEX IF NOT EXISTS idx_user_model_settings_user ON user_model_settings(user_id);
DROP TRIGGER IF EXISTS user_model_settings_updated_at ON user_model_settings;
CREATE TRIGGER user_model_settings_updated_at BEFORE UPDATE ON user_model_settings
    FOR EACH ROW EXECUTE FUNCTION update_updated_at();
