\set ON_ERROR_STOP on

BEGIN;

CREATE TABLE IF NOT EXISTS bot_instance (
    id TEXT PRIMARY KEY,
    name TEXT NOT NULL,
    enabled BOOLEAN NOT NULL DEFAULT TRUE,
    access_password TEXT NOT NULL DEFAULT '',
    workspace_dir TEXT NOT NULL UNIQUE,
    docker_status TEXT NOT NULL DEFAULT 'STOPPED',
    current_state TEXT DEFAULT 'IDLE',
    last_action TEXT,
    image_tag TEXT NOT NULL DEFAULT 'nanobot-base:v0.1.4',
    created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);

CREATE TABLE IF NOT EXISTS bot_image (
    tag TEXT PRIMARY KEY,
    image_id TEXT,
    version TEXT NOT NULL,
    status TEXT NOT NULL DEFAULT 'READY',
    source_dir TEXT,
    created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);

CREATE TABLE IF NOT EXISTS bot_message (
    id SERIAL PRIMARY KEY,
    bot_id TEXT NOT NULL REFERENCES bot_instance(id),
    role TEXT NOT NULL,
    text TEXT NOT NULL,
    media_json TEXT,
    feedback TEXT,
    feedback_at TIMESTAMP,
    created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);

CREATE TABLE IF NOT EXISTS sys_login_log (
    id SERIAL PRIMARY KEY,
    auth_type TEXT NOT NULL,
    token_hash TEXT NOT NULL,
    subject_id TEXT NOT NULL,
    bot_id TEXT,
    auth_source TEXT NOT NULL DEFAULT '',
    created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
    expires_at TIMESTAMP NOT NULL,
    last_seen_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
    revoked_at TIMESTAMP,
    revoke_reason TEXT,
    client_ip TEXT,
    user_agent TEXT,
    device_info TEXT
);

CREATE TABLE IF NOT EXISTS sys_setting (
    key VARCHAR(120) PRIMARY KEY,
    name VARCHAR(200) NOT NULL DEFAULT '',
    category VARCHAR(64) NOT NULL DEFAULT 'general',
    description TEXT NOT NULL DEFAULT '',
    value_type VARCHAR(32) NOT NULL DEFAULT 'json',
    value_json TEXT NOT NULL DEFAULT '{}',
    is_public BOOLEAN NOT NULL DEFAULT FALSE,
    sort_order INTEGER NOT NULL DEFAULT 100,
    created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);

CREATE TABLE IF NOT EXISTS bot_request_usage (
    id SERIAL PRIMARY KEY,
    bot_id TEXT NOT NULL REFERENCES bot_instance(id),
    message_id INTEGER,
    request_id VARCHAR(120) NOT NULL,
    channel VARCHAR(64) NOT NULL DEFAULT 'dashboard',
    status VARCHAR(32) NOT NULL DEFAULT 'PENDING',
    provider VARCHAR(120),
    model VARCHAR(255),
    token_source VARCHAR(32) NOT NULL DEFAULT 'estimated',
    input_tokens INTEGER NOT NULL DEFAULT 0,
    output_tokens INTEGER NOT NULL DEFAULT 0,
    total_tokens INTEGER NOT NULL DEFAULT 0,
    input_text_preview TEXT,
    output_text_preview TEXT,
    attachments_json TEXT,
    error_text TEXT,
    metadata_json TEXT,
    started_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
    completed_at TIMESTAMP,
    created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);

CREATE TABLE IF NOT EXISTS bot_activity_event (
    id SERIAL PRIMARY KEY,
    bot_id TEXT NOT NULL REFERENCES bot_instance(id),
    request_id VARCHAR(120),
    event_type VARCHAR(64) NOT NULL,
    channel VARCHAR(64) NOT NULL DEFAULT 'dashboard',
    detail TEXT,
    metadata_json TEXT,
    created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);

CREATE TABLE IF NOT EXISTS skill_market_item (
    id SERIAL PRIMARY KEY,
    skill_key VARCHAR(120) NOT NULL,
    display_name VARCHAR(255) NOT NULL DEFAULT '',
    description TEXT NOT NULL DEFAULT '',
    zip_filename VARCHAR(255) NOT NULL,
    zip_size_bytes INTEGER NOT NULL DEFAULT 0,
    entry_names_json TEXT NOT NULL DEFAULT '[]',
    created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
    CONSTRAINT uq_skill_market_item_skill_key UNIQUE (skill_key),
    CONSTRAINT uq_skill_market_item_zip_filename UNIQUE (zip_filename)
);

CREATE TABLE IF NOT EXISTS bot_skill_install (
    id SERIAL PRIMARY KEY,
    bot_id TEXT NOT NULL REFERENCES bot_instance(id),
    skill_market_item_id INTEGER NOT NULL REFERENCES skill_market_item(id),
    installed_entries_json TEXT NOT NULL DEFAULT '[]',
    source_zip_filename VARCHAR(255) NOT NULL DEFAULT '',
    status VARCHAR(32) NOT NULL DEFAULT 'INSTALLED',
    last_error TEXT,
    installed_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
    CONSTRAINT uq_bot_skill_install_bot_market UNIQUE (bot_id, skill_market_item_id)
);

CREATE TABLE IF NOT EXISTS topic_topic (
    id SERIAL PRIMARY KEY,
    bot_id TEXT NOT NULL REFERENCES bot_instance(id),
    topic_key TEXT NOT NULL,
    name TEXT NOT NULL DEFAULT '',
    description TEXT NOT NULL DEFAULT '',
    is_active BOOLEAN NOT NULL DEFAULT TRUE,
    is_default_fallback BOOLEAN NOT NULL DEFAULT FALSE,
    routing_json TEXT NOT NULL DEFAULT '{}',
    view_schema_json TEXT NOT NULL DEFAULT '{}',
    created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
    CONSTRAINT uq_topic_topic_bot_topic_key UNIQUE (bot_id, topic_key)
);

CREATE TABLE IF NOT EXISTS topic_item (
    id SERIAL PRIMARY KEY,
    bot_id TEXT NOT NULL REFERENCES bot_instance(id),
    topic_key TEXT NOT NULL,
    title TEXT NOT NULL DEFAULT '',
    content TEXT NOT NULL DEFAULT '',
    level TEXT NOT NULL DEFAULT 'info',
    tags_json TEXT,
    view_json TEXT,
    source TEXT NOT NULL DEFAULT 'mcp',
    dedupe_key TEXT,
    is_read BOOLEAN NOT NULL DEFAULT FALSE,
    created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);

CREATE INDEX IF NOT EXISTS idx_bot_instance_enabled ON bot_instance (enabled);
CREATE INDEX IF NOT EXISTS idx_bot_instance_docker_status ON bot_instance (docker_status);

CREATE INDEX IF NOT EXISTS idx_bot_message_bot_id ON bot_message (bot_id);
CREATE INDEX IF NOT EXISTS idx_bot_message_role ON bot_message (role);
CREATE INDEX IF NOT EXISTS idx_bot_message_feedback ON bot_message (feedback);
CREATE INDEX IF NOT EXISTS idx_bot_message_created_at ON bot_message (created_at);

CREATE UNIQUE INDEX IF NOT EXISTS idx_sys_login_log_token_hash ON sys_login_log (token_hash);
CREATE INDEX IF NOT EXISTS idx_sys_login_log_auth_type ON sys_login_log (auth_type);
CREATE INDEX IF NOT EXISTS idx_sys_login_log_subject_id ON sys_login_log (subject_id);
CREATE INDEX IF NOT EXISTS idx_sys_login_log_bot_id ON sys_login_log (bot_id);
CREATE INDEX IF NOT EXISTS idx_sys_login_log_auth_source ON sys_login_log (auth_source);
CREATE INDEX IF NOT EXISTS idx_sys_login_log_created_at ON sys_login_log (created_at);
CREATE INDEX IF NOT EXISTS idx_sys_login_log_expires_at ON sys_login_log (expires_at);
CREATE INDEX IF NOT EXISTS idx_sys_login_log_last_seen_at ON sys_login_log (last_seen_at);
CREATE INDEX IF NOT EXISTS idx_sys_login_log_revoked_at ON sys_login_log (revoked_at);

CREATE INDEX IF NOT EXISTS idx_sys_setting_category ON sys_setting (category);
CREATE INDEX IF NOT EXISTS idx_sys_setting_is_public ON sys_setting (is_public);
CREATE INDEX IF NOT EXISTS idx_sys_setting_sort_order ON sys_setting (sort_order);
CREATE INDEX IF NOT EXISTS idx_sys_setting_updated_at ON sys_setting (updated_at);

CREATE INDEX IF NOT EXISTS idx_bot_request_usage_bot_id ON bot_request_usage (bot_id);
CREATE INDEX IF NOT EXISTS idx_bot_request_usage_message_id ON bot_request_usage (message_id);
CREATE INDEX IF NOT EXISTS idx_bot_request_usage_request_id ON bot_request_usage (request_id);
CREATE INDEX IF NOT EXISTS idx_bot_request_usage_channel ON bot_request_usage (channel);
CREATE INDEX IF NOT EXISTS idx_bot_request_usage_status ON bot_request_usage (status);
CREATE INDEX IF NOT EXISTS idx_bot_request_usage_started_at ON bot_request_usage (started_at);
CREATE INDEX IF NOT EXISTS idx_bot_request_usage_completed_at ON bot_request_usage (completed_at);
CREATE INDEX IF NOT EXISTS idx_bot_request_usage_created_at ON bot_request_usage (created_at);
CREATE INDEX IF NOT EXISTS idx_bot_request_usage_updated_at ON bot_request_usage (updated_at);
CREATE INDEX IF NOT EXISTS idx_bot_request_usage_started_at_bot_id ON bot_request_usage (started_at, bot_id);

CREATE INDEX IF NOT EXISTS idx_bot_activity_event_bot_id ON bot_activity_event (bot_id);
CREATE INDEX IF NOT EXISTS idx_bot_activity_event_request_id ON bot_activity_event (request_id);
CREATE INDEX IF NOT EXISTS idx_bot_activity_event_event_type ON bot_activity_event (event_type);
CREATE INDEX IF NOT EXISTS idx_bot_activity_event_channel ON bot_activity_event (channel);
CREATE INDEX IF NOT EXISTS idx_bot_activity_event_created_at ON bot_activity_event (created_at);
CREATE INDEX IF NOT EXISTS idx_bot_activity_event_bot_id_request_present
ON bot_activity_event (bot_id)
WHERE request_id IS NOT NULL AND request_id <> '';

CREATE INDEX IF NOT EXISTS idx_skill_market_item_created_at ON skill_market_item (created_at);
CREATE INDEX IF NOT EXISTS idx_skill_market_item_updated_at ON skill_market_item (updated_at);

CREATE INDEX IF NOT EXISTS idx_bot_skill_install_bot_id ON bot_skill_install (bot_id);
CREATE INDEX IF NOT EXISTS idx_bot_skill_install_skill_market_item_id ON bot_skill_install (skill_market_item_id);
CREATE INDEX IF NOT EXISTS idx_bot_skill_install_status ON bot_skill_install (status);
CREATE INDEX IF NOT EXISTS idx_bot_skill_install_installed_at ON bot_skill_install (installed_at);
CREATE INDEX IF NOT EXISTS idx_bot_skill_install_updated_at ON bot_skill_install (updated_at);

CREATE INDEX IF NOT EXISTS idx_topic_topic_bot_id ON topic_topic (bot_id);
CREATE INDEX IF NOT EXISTS idx_topic_topic_topic_key ON topic_topic (topic_key);
CREATE INDEX IF NOT EXISTS idx_topic_topic_created_at ON topic_topic (created_at);
CREATE INDEX IF NOT EXISTS idx_topic_topic_updated_at ON topic_topic (updated_at);
CREATE INDEX IF NOT EXISTS idx_topic_topic_bot_fallback ON topic_topic (bot_id, is_default_fallback);

CREATE INDEX IF NOT EXISTS idx_topic_item_bot_id ON topic_item (bot_id);
CREATE INDEX IF NOT EXISTS idx_topic_item_topic_key ON topic_item (topic_key);
CREATE INDEX IF NOT EXISTS idx_topic_item_level ON topic_item (level);
CREATE INDEX IF NOT EXISTS idx_topic_item_source ON topic_item (source);
CREATE INDEX IF NOT EXISTS idx_topic_item_is_read ON topic_item (is_read);
CREATE INDEX IF NOT EXISTS idx_topic_item_created_at ON topic_item (created_at);
CREATE INDEX IF NOT EXISTS idx_topic_item_bot_topic_created_at ON topic_item (bot_id, topic_key, created_at);
CREATE INDEX IF NOT EXISTS idx_topic_item_bot_dedupe ON topic_item (bot_id, dedupe_key);

COMMIT;
