-- qc-trace database schema v9

CREATE TABLE IF NOT EXISTS schema_version (
    version INT PRIMARY KEY,
    applied_at TIMESTAMPTZ DEFAULT NOW()
);

CREATE TABLE IF NOT EXISTS sessions (
    id TEXT PRIMARY KEY,
    source TEXT NOT NULL,
    model TEXT,
    user_email TEXT,
    user_name TEXT,
    device_name TEXT,
    device_id TEXT,
    cwd TEXT,
    repo_url TEXT,
    repo_name TEXT,
    git_branch TEXT,
    git_commit TEXT,
    project_hash TEXT,
    org TEXT,
    first_seen TIMESTAMPTZ DEFAULT NOW(),
    last_updated TIMESTAMPTZ DEFAULT NOW(),
    raw_file_path TEXT
);

CREATE TABLE IF NOT EXISTS messages (
    id TEXT PRIMARY KEY,
    session_id TEXT NOT NULL REFERENCES sessions(id) ON DELETE CASCADE,
    source TEXT NOT NULL,
    source_schema_version INT NOT NULL,
    msg_type TEXT NOT NULL,
    timestamp TIMESTAMPTZ,
    content TEXT,
    thinking TEXT,
    model TEXT,
    raw_data JSONB,
    raw_file_path TEXT,
    raw_line_number INT,
    ingested_at TIMESTAMPTZ DEFAULT NOW()
);

CREATE TABLE IF NOT EXISTS token_usage (
    message_id TEXT PRIMARY KEY REFERENCES messages(id) ON DELETE CASCADE,
    input_tokens INT DEFAULT 0,
    output_tokens INT DEFAULT 0,
    cached_tokens INT DEFAULT 0,
    thinking_tokens INT DEFAULT 0
);

CREATE TABLE IF NOT EXISTS tool_calls (
    message_id TEXT PRIMARY KEY REFERENCES messages(id) ON DELETE CASCADE,
    tool_id TEXT,
    tool_name TEXT NOT NULL,
    tool_input JSONB
);

CREATE TABLE IF NOT EXISTS tool_results (
    message_id TEXT PRIMARY KEY REFERENCES messages(id) ON DELETE CASCADE,
    call_id TEXT,
    output TEXT,
    status TEXT CHECK (status IN ('success', 'failure'))
);

CREATE INDEX IF NOT EXISTS idx_messages_session ON messages(session_id);
CREATE INDEX IF NOT EXISTS idx_messages_type ON messages(msg_type);
CREATE INDEX IF NOT EXISTS idx_messages_source ON messages(source);
CREATE INDEX IF NOT EXISTS idx_messages_timestamp ON messages(timestamp);
CREATE INDEX IF NOT EXISTS idx_messages_ingested_at ON messages(ingested_at);
CREATE INDEX IF NOT EXISTS idx_messages_raw_file_path ON messages(raw_file_path) WHERE raw_file_path IS NOT NULL;
CREATE INDEX IF NOT EXISTS idx_sessions_source ON sessions(source);
CREATE INDEX IF NOT EXISTS idx_sessions_user ON sessions(user_email);
CREATE INDEX IF NOT EXISTS idx_sessions_repo ON sessions(repo_name);
CREATE INDEX IF NOT EXISTS idx_sessions_org ON sessions(org);
CREATE INDEX IF NOT EXISTS idx_sessions_device_id ON sessions(device_id);

CREATE TABLE IF NOT EXISTS file_progress (
    raw_file_path TEXT NOT NULL,
    source TEXT NOT NULL,
    last_line_read INT NOT NULL DEFAULT 0,
    content_hash TEXT,
    updated_at TIMESTAMPTZ DEFAULT NOW(),
    PRIMARY KEY (raw_file_path, source)
);

CREATE TABLE IF NOT EXISTS version_config (
    key TEXT PRIMARY KEY,
    value TEXT NOT NULL
);

CREATE TABLE IF NOT EXISTS daemon_heartbeats (
    device_id TEXT NOT NULL,
    org TEXT,
    device_name TEXT,
    daemon_version TEXT,
    status TEXT NOT NULL,
    queue_size INT DEFAULT 0,
    current_backoff FLOAT DEFAULT 0,
    messages_this_session INT DEFAULT 0,
    last_push_at TIMESTAMPTZ,
    uptime_seconds FLOAT DEFAULT 0,
    source_stats JSONB,
    recent_errors JSONB,
    last_seen_at TIMESTAMPTZ DEFAULT NOW(),
    PRIMARY KEY (device_id)
);
CREATE INDEX IF NOT EXISTS idx_heartbeats_org ON daemon_heartbeats(org);
CREATE INDEX IF NOT EXISTS idx_heartbeats_last_seen ON daemon_heartbeats(last_seen_at);

CREATE TABLE IF NOT EXISTS daemon_commands (
    id SERIAL PRIMARY KEY,
    device_id TEXT NOT NULL,
    command TEXT NOT NULL,
    params JSONB DEFAULT '{}',
    status TEXT NOT NULL DEFAULT 'pending',
    created_at TIMESTAMPTZ DEFAULT NOW(),
    acked_at TIMESTAMPTZ,
    completed_at TIMESTAMPTZ,
    result JSONB
);
CREATE INDEX IF NOT EXISTS idx_daemon_commands_pending
    ON daemon_commands (device_id, status) WHERE status = 'pending';

INSERT INTO schema_version (version) VALUES (9) ON CONFLICT DO NOTHING;
