-- Migration 010: Sessions table with bulletproof auto-incrementing
-- Purpose: Store session metadata with unique session numbers per project
-- Dependencies: 001_base_schema.sql (projects table, pgvector extension)

-- Sessions table: Core session metadata with temporal tracking
CREATE TABLE IF NOT EXISTS sessions (
    id TEXT PRIMARY KEY,  -- Format: "session_{timestamp}_{uuid}"
    project TEXT REFERENCES projects(key) ON DELETE SET NULL,
    session_number INTEGER,  -- Auto-increment per project (e.g., "Session 439")

    -- Temporal tracking
    started_at TIMESTAMP WITH TIME ZONE NOT NULL,
    ended_at TIMESTAMP WITH TIME ZONE,
    duration_minutes INTEGER GENERATED ALWAYS AS
        (EXTRACT(EPOCH FROM (ended_at - started_at)) / 60) STORED,

    -- Context
    working_directory TEXT,
    git_branch TEXT,
    initial_prompt TEXT,  -- First user message
    summary TEXT,  -- Auto-generated summary

    -- Semantic search
    embedding vector(1024),  -- Embedding of summary for similarity search

    -- Metrics
    message_count INTEGER DEFAULT 0,
    token_count INTEGER DEFAULT 0,
    tools_used TEXT[],  -- Array of tool names used

    -- Status
    status TEXT DEFAULT 'active' CHECK (status IN ('active', 'completed', 'interrupted')),

    created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
    updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);

-- Indexes for efficient querying
CREATE INDEX idx_sessions_project ON sessions(project);
CREATE INDEX idx_sessions_started ON sessions(started_at DESC);
CREATE INDEX idx_sessions_status ON sessions(status);
CREATE INDEX idx_sessions_number ON sessions(project, session_number DESC);

-- HNSW index for semantic similarity search (requires pgvector)
CREATE INDEX idx_sessions_embedding ON sessions USING hnsw (embedding vector_cosine_ops);

-- Unique session number per project (partial index - only when project is set)
CREATE UNIQUE INDEX idx_sessions_project_number ON sessions(project, session_number)
    WHERE project IS NOT NULL;

-- Session number sequences per project (bulletproof autoincrement)
CREATE TABLE IF NOT EXISTS session_sequences (
    project TEXT PRIMARY KEY REFERENCES projects(key) ON DELETE CASCADE,
    next_number INTEGER DEFAULT 1,
    last_updated TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);

-- Function for atomic session number generation (prevents race conditions)
CREATE OR REPLACE FUNCTION get_next_session_number(p_project TEXT)
RETURNS INTEGER AS $$
DECLARE
    v_number INTEGER;
BEGIN
    -- Insert project if doesn't exist
    INSERT INTO projects (key, name) VALUES (p_project, p_project)
    ON CONFLICT (key) DO NOTHING;

    -- Insert sequence if doesn't exist
    INSERT INTO session_sequences (project, next_number)
    VALUES (p_project, 1)
    ON CONFLICT (project) DO NOTHING;

    -- Atomically increment and return (no race conditions possible)
    UPDATE session_sequences
    SET next_number = next_number + 1,
        last_updated = NOW()
    WHERE project = p_project
    RETURNING next_number - 1 INTO v_number;

    RETURN v_number;
END;
$$ LANGUAGE plpgsql;

-- Trigger to auto-assign session numbers on insert
CREATE OR REPLACE FUNCTION assign_session_number()
RETURNS TRIGGER AS $$
BEGIN
    IF NEW.project IS NOT NULL AND NEW.session_number IS NULL THEN
        NEW.session_number := get_next_session_number(NEW.project);
    END IF;
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER trg_assign_session_number
    BEFORE INSERT ON sessions
    FOR EACH ROW
    EXECUTE FUNCTION assign_session_number();

-- Session-Task relationship enhancement
-- Add foreign key constraint to existing task_activity table
ALTER TABLE task_activity
    DROP CONSTRAINT IF EXISTS fk_task_activity_session;

-- Drop NOT NULL constraint to allow NULL session_ids (for orphaned references)
ALTER TABLE task_activity
    ALTER COLUMN session_id DROP NOT NULL;

-- First, set NULL for any session_ids that don't exist (orphaned references)
-- This cleans up existing data before adding the constraint
UPDATE task_activity
SET session_id = NULL
WHERE session_id IS NOT NULL
  AND NOT EXISTS (SELECT 1 FROM sessions WHERE id = task_activity.session_id);

ALTER TABLE task_activity
    ADD CONSTRAINT fk_task_activity_session
    FOREIGN KEY (session_id) REFERENCES sessions(id) ON DELETE SET NULL;

-- Helper view: Session tasks with activity summary
CREATE OR REPLACE VIEW session_tasks AS
SELECT DISTINCT
    ta.session_id,
    ta.task_id,
    t.project,
    t.title,
    t.status,
    MIN(ta.created_at) as first_touched,
    MAX(ta.created_at) as last_touched,
    COUNT(*) as activity_count
FROM task_activity ta
JOIN tasks t ON ta.task_id = t.id
GROUP BY ta.session_id, ta.task_id, t.project, t.title, t.status;

-- Session-Commit linking table
CREATE TABLE IF NOT EXISTS session_commits (
    id SERIAL PRIMARY KEY,
    session_id TEXT NOT NULL REFERENCES sessions(id) ON DELETE CASCADE,
    commit_sha TEXT NOT NULL,
    repo TEXT NOT NULL,
    commit_message TEXT,
    committed_at TIMESTAMP WITH TIME ZONE,
    created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
    UNIQUE(session_id, commit_sha)
);

CREATE INDEX idx_session_commits_session ON session_commits(session_id);
CREATE INDEX idx_session_commits_sha ON session_commits(commit_sha);

-- Helper view: Link sessions to versions through commits
CREATE OR REPLACE VIEW session_versions AS
SELECT DISTINCT
    sc.session_id,
    tc.task_id,
    t.version_id,
    v.version,
    v.status as version_status
FROM session_commits sc
JOIN task_commits tc ON sc.commit_sha = tc.commit_sha
JOIN tasks t ON tc.task_id = t.id
LEFT JOIN versions v ON t.version_id = v.id
WHERE v.id IS NOT NULL;

-- Comments for documentation
COMMENT ON TABLE sessions IS 'Session metadata with unique session numbers per project';
COMMENT ON TABLE session_sequences IS 'Atomic counters for session numbers per project';
COMMENT ON TABLE session_commits IS 'Links sessions to git commits';
COMMENT ON COLUMN sessions.session_number IS 'Auto-incrementing number per project (1, 2, 3, ...)';
COMMENT ON COLUMN sessions.embedding IS 'Vector embedding of session summary for semantic search';
COMMENT ON FUNCTION get_next_session_number IS 'Atomic function to get next session number for a project';
