"""Initial PostgreSQL schema — full peon-mcp database.

Revision ID: 0001
Revises: None
"""

from typing import Sequence, Union

import sqlalchemy as sa
from alembic import op

revision: str = "0001"
down_revision: Union[str, None] = None
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None


def upgrade() -> None:
    conn = op.get_bind()

    # -- projects --
    conn.execute(sa.text("""
        CREATE TABLE IF NOT EXISTS projects (
            id TEXT PRIMARY KEY,
            path TEXT NOT NULL DEFAULT '',
            github_url TEXT NOT NULL DEFAULT '',
            context_pruning TEXT NOT NULL DEFAULT '',
            k8s_config TEXT NOT NULL DEFAULT '',
            deploy_config TEXT NOT NULL DEFAULT '',
            created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
        )
    """))

    # -- features --
    conn.execute(sa.text("""
        CREATE TABLE IF NOT EXISTS features (
            id SERIAL PRIMARY KEY,
            project_id TEXT NOT NULL REFERENCES projects(id),
            name TEXT NOT NULL,
            description TEXT NOT NULL DEFAULT '',
            status TEXT NOT NULL DEFAULT 'planned'
                CHECK(status IN ('planned', 'in_progress', 'done', 'cancelled')),
            branch TEXT NOT NULL DEFAULT '',
            pr_url TEXT NOT NULL DEFAULT '',
            created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
            updated_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
        )
    """))
    conn.execute(sa.text(
        "CREATE UNIQUE INDEX IF NOT EXISTS idx_features_project_branch ON features(project_id, branch)"
    ))

    # -- tasks --
    conn.execute(sa.text("""
        CREATE TABLE IF NOT EXISTS tasks (
            id SERIAL PRIMARY KEY,
            project_id TEXT NOT NULL REFERENCES projects(id),
            feature_id INTEGER REFERENCES features(id),
            title TEXT NOT NULL,
            description TEXT NOT NULL DEFAULT '',
            priority TEXT NOT NULL DEFAULT 'medium'
                CHECK(priority IN ('low', 'medium', 'high', 'critical')),
            status TEXT NOT NULL DEFAULT 'todo'
                CHECK(status IN ('grooming', 'todo', 'in_progress', 'done', 'cancelled', 'timeout', 'ready-to-deploy')),
            task_type TEXT NOT NULL DEFAULT 'task'
                CHECK(task_type IN ('task', 'fix', 'verify', 'review')),
            commit_base TEXT NOT NULL DEFAULT '',
            commit_head TEXT NOT NULL DEFAULT '',
            branch TEXT NOT NULL DEFAULT '',
            worktree_path TEXT NOT NULL DEFAULT '',
            pr_url TEXT NOT NULL DEFAULT '',
            host TEXT NOT NULL DEFAULT '',
            progress DOUBLE PRECISION NOT NULL DEFAULT 0.0
                CHECK(progress >= 0.0 AND progress <= 1.0),
            lines_added INTEGER NOT NULL DEFAULT 0,
            lines_deleted INTEGER NOT NULL DEFAULT 0,
            tokens_used INTEGER NOT NULL DEFAULT 0,
            started_at TIMESTAMPTZ,
            completed_at TIMESTAMPTZ,
            created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
            updated_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
        )
    """))

    # -- task_dependencies --
    conn.execute(sa.text("""
        CREATE TABLE IF NOT EXISTS task_dependencies (
            id SERIAL PRIMARY KEY,
            task_id INTEGER NOT NULL REFERENCES tasks(id) ON DELETE CASCADE,
            depends_on_task_id INTEGER NOT NULL REFERENCES tasks(id) ON DELETE CASCADE,
            created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
            UNIQUE(task_id, depends_on_task_id),
            CHECK(task_id != depends_on_task_id)
        )
    """))
    conn.execute(sa.text(
        "CREATE INDEX IF NOT EXISTS idx_task_deps_task_id ON task_dependencies(task_id)"
    ))
    conn.execute(sa.text(
        "CREATE INDEX IF NOT EXISTS idx_task_deps_depends_on ON task_dependencies(depends_on_task_id)"
    ))

    # -- work_logs --
    conn.execute(sa.text("""
        CREATE TABLE IF NOT EXISTS work_logs (
            id SERIAL PRIMARY KEY,
            task_id INTEGER NOT NULL REFERENCES tasks(id),
            project_id TEXT NOT NULL REFERENCES projects(id),
            summary TEXT NOT NULL,
            files_changed TEXT NOT NULL DEFAULT '',
            test_result TEXT NOT NULL DEFAULT ''
                CHECK(test_result IN ('', 'pass', 'fail', 'skip')),
            created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
        )
    """))

    # -- peon_memories --
    conn.execute(sa.text("""
        CREATE TABLE IF NOT EXISTS peon_memories (
            id SERIAL PRIMARY KEY,
            project_id TEXT NOT NULL REFERENCES projects(id),
            category TEXT NOT NULL DEFAULT 'general'
                CHECK(category IN ('env', 'pattern', 'gotcha', 'test', 'dependency', 'general')),
            content TEXT NOT NULL CHECK(length(content) <= 500),
            source_task_id INTEGER REFERENCES tasks(id),
            access_count INTEGER NOT NULL DEFAULT 0,
            created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
            updated_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
        )
    """))
    conn.execute(sa.text(
        "CREATE INDEX IF NOT EXISTS idx_peon_memories_project_id ON peon_memories(project_id)"
    ))

    # -- loop_heartbeats --
    conn.execute(sa.text("""
        CREATE TABLE IF NOT EXISTS loop_heartbeats (
            id TEXT PRIMARY KEY,
            project_id TEXT DEFAULT NULL,
            pid INTEGER NOT NULL,
            hostname TEXT NOT NULL,
            discord_status TEXT NOT NULL DEFAULT '',
            started_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
            last_heartbeat TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
        )
    """))

    # -- agent_sessions --
    conn.execute(sa.text("""
        CREATE TABLE IF NOT EXISTS agent_sessions (
            id SERIAL PRIMARY KEY,
            project_id TEXT NOT NULL REFERENCES projects(id),
            task_id INTEGER REFERENCES tasks(id),
            loop_id TEXT NOT NULL DEFAULT '',
            model TEXT NOT NULL DEFAULT '',
            hostname TEXT NOT NULL DEFAULT '',
            status TEXT NOT NULL DEFAULT 'running'
                CHECK(status IN ('running', 'completed', 'failed', 'timeout', 'cancelled')),
            exit_code INTEGER,
            tokens_input INTEGER NOT NULL DEFAULT 0,
            tokens_output INTEGER NOT NULL DEFAULT 0,
            cost_usd DOUBLE PRECISION NOT NULL DEFAULT 0.0,
            error_summary TEXT NOT NULL DEFAULT '',
            started_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
            ended_at TIMESTAMPTZ,
            duration_seconds DOUBLE PRECISION,
            updated_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
        )
    """))
    conn.execute(sa.text(
        "CREATE INDEX IF NOT EXISTS idx_agent_sessions_project_task ON agent_sessions(project_id, task_id)"
    ))

    # -- session_events --
    conn.execute(sa.text("""
        CREATE TABLE IF NOT EXISTS session_events (
            id SERIAL PRIMARY KEY,
            session_id INTEGER NOT NULL REFERENCES agent_sessions(id),
            event_type TEXT NOT NULL
                CHECK(event_type IN ('tool_call', 'tool_result', 'file_read', 'file_write', 'git_op', 'error', 'info', 'message', 'checkpoint', 'start')),
            tool_name TEXT NOT NULL DEFAULT '',
            detail TEXT NOT NULL DEFAULT '',
            timestamp TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
        )
    """))
    conn.execute(sa.text(
        "CREATE INDEX IF NOT EXISTS idx_session_events_session_id ON session_events(session_id)"
    ))

    # -- discord_configs --
    conn.execute(sa.text("""
        CREATE TABLE IF NOT EXISTS discord_configs (
            id SERIAL PRIMARY KEY,
            project_id TEXT NOT NULL REFERENCES projects(id),
            bot_token TEXT NOT NULL DEFAULT '',
            guild_id TEXT NOT NULL DEFAULT '',
            enabled BOOLEAN NOT NULL DEFAULT TRUE,
            dm_policy TEXT NOT NULL DEFAULT 'disabled'
                CHECK(dm_policy IN ('open', 'pairing', 'disabled')),
            guild_policy TEXT NOT NULL DEFAULT 'disabled'
                CHECK(guild_policy IN ('open', 'allowlist', 'disabled')),
            require_mention BOOLEAN NOT NULL DEFAULT TRUE,
            history_limit INTEGER NOT NULL DEFAULT 20,
            model TEXT NOT NULL DEFAULT '',
            system_prompt TEXT NOT NULL DEFAULT '',
            created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
            updated_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
        )
    """))
    conn.execute(sa.text(
        "CREATE INDEX IF NOT EXISTS idx_discord_configs_project_id ON discord_configs(project_id)"
    ))

    # -- discord_channel_mappings --
    conn.execute(sa.text("""
        CREATE TABLE IF NOT EXISTS discord_channel_mappings (
            id SERIAL PRIMARY KEY,
            discord_config_id INTEGER NOT NULL REFERENCES discord_configs(id) ON DELETE CASCADE,
            channel_id TEXT NOT NULL,
            channel_name TEXT NOT NULL DEFAULT '',
            allowed_user_ids TEXT NOT NULL DEFAULT '[]',
            allowed_role_ids TEXT NOT NULL DEFAULT '[]',
            subscribed_events TEXT NOT NULL DEFAULT '[]',
            enabled BOOLEAN NOT NULL DEFAULT TRUE,
            project_id TEXT NOT NULL DEFAULT '',
            created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
        )
    """))
    conn.execute(sa.text(
        "CREATE INDEX IF NOT EXISTS idx_discord_channel_mappings_config_id ON discord_channel_mappings(discord_config_id)"
    ))

    # -- discord_inbound_messages --
    conn.execute(sa.text("""
        CREATE TABLE IF NOT EXISTS discord_inbound_messages (
            id SERIAL PRIMARY KEY,
            discord_config_id INTEGER NOT NULL REFERENCES discord_configs(id) ON DELETE CASCADE,
            channel_id TEXT NOT NULL,
            channel_name TEXT NOT NULL DEFAULT '',
            author_id TEXT NOT NULL,
            author_name TEXT NOT NULL DEFAULT '',
            content TEXT NOT NULL DEFAULT '',
            status TEXT NOT NULL DEFAULT 'pending'
                CHECK(status IN ('pending', 'processed', 'ignored')),
            created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
        )
    """))
    conn.execute(sa.text(
        "CREATE INDEX IF NOT EXISTS idx_discord_inbound_messages_config_id ON discord_inbound_messages(discord_config_id)"
    ))
    conn.execute(sa.text(
        "CREATE INDEX IF NOT EXISTS idx_discord_inbound_messages_status ON discord_inbound_messages(status)"
    ))

    # -- discord_messages --
    conn.execute(sa.text("""
        CREATE TABLE IF NOT EXISTS discord_messages (
            id SERIAL PRIMARY KEY,
            config_id INTEGER NOT NULL REFERENCES discord_configs(id) ON DELETE CASCADE,
            channel_id TEXT NOT NULL,
            user_id TEXT NOT NULL,
            username TEXT NOT NULL DEFAULT '',
            content TEXT NOT NULL DEFAULT '',
            message_id TEXT NOT NULL DEFAULT '',
            replied_to TEXT NOT NULL DEFAULT '',
            processed BOOLEAN NOT NULL DEFAULT FALSE,
            denied BOOLEAN NOT NULL DEFAULT FALSE,
            deny_reason TEXT NOT NULL DEFAULT '',
            member_roles TEXT NOT NULL DEFAULT '[]',
            created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
        )
    """))
    conn.execute(sa.text(
        "CREATE INDEX IF NOT EXISTS idx_discord_messages_config_id ON discord_messages(config_id)"
    ))
    conn.execute(sa.text(
        "CREATE INDEX IF NOT EXISTS idx_discord_messages_processed ON discord_messages(processed)"
    ))

    # -- discord_paired_users --
    conn.execute(sa.text("""
        CREATE TABLE IF NOT EXISTS discord_paired_users (
            id SERIAL PRIMARY KEY,
            discord_config_id INTEGER NOT NULL REFERENCES discord_configs(id) ON DELETE CASCADE,
            user_id TEXT NOT NULL,
            username TEXT NOT NULL DEFAULT '',
            pairing_code TEXT NOT NULL DEFAULT '',
            approved_by TEXT NOT NULL DEFAULT '',
            paired_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
            UNIQUE(discord_config_id, user_id)
        )
    """))
    conn.execute(sa.text(
        "CREATE INDEX IF NOT EXISTS idx_discord_paired_users_config_id ON discord_paired_users(discord_config_id)"
    ))

    # -- discord_activity_logs --
    conn.execute(sa.text("""
        CREATE TABLE IF NOT EXISTS discord_activity_logs (
            id SERIAL PRIMARY KEY,
            config_id INTEGER NOT NULL REFERENCES discord_configs(id) ON DELETE CASCADE,
            event_type TEXT NOT NULL,
            channel_id TEXT NOT NULL DEFAULT '',
            user_id TEXT NOT NULL DEFAULT '',
            detail TEXT NOT NULL DEFAULT '',
            created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
        )
    """))
    conn.execute(sa.text(
        "CREATE INDEX IF NOT EXISTS idx_discord_activity_logs_config_id ON discord_activity_logs(config_id)"
    ))
    conn.execute(sa.text(
        "CREATE INDEX IF NOT EXISTS idx_discord_activity_logs_event_type ON discord_activity_logs(event_type)"
    ))
    conn.execute(sa.text(
        "CREATE INDEX IF NOT EXISTS idx_discord_activity_logs_created_at ON discord_activity_logs(created_at)"
    ))

    # -- project_relations --
    conn.execute(sa.text("""
        CREATE TABLE IF NOT EXISTS project_relations (
            id SERIAL PRIMARY KEY,
            project_id TEXT NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
            related_project_id TEXT NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
            created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
            UNIQUE(project_id, related_project_id),
            CHECK(project_id != related_project_id)
        )
    """))
    conn.execute(sa.text(
        "CREATE INDEX IF NOT EXISTS idx_project_relations_project_id ON project_relations(project_id)"
    ))
    conn.execute(sa.text(
        "CREATE INDEX IF NOT EXISTS idx_project_relations_related_project_id ON project_relations(related_project_id)"
    ))

    # -- github_webhook_events --
    conn.execute(sa.text("""
        CREATE TABLE IF NOT EXISTS github_webhook_events (
            id SERIAL PRIMARY KEY,
            delivery_id TEXT NOT NULL DEFAULT '',
            event_type TEXT NOT NULL DEFAULT '',
            action TEXT NOT NULL DEFAULT '',
            status TEXT NOT NULL DEFAULT 'no_handler'
                CHECK(status IN ('dispatched', 'no_handler')),
            received_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
        )
    """))
    conn.execute(sa.text(
        "CREATE INDEX IF NOT EXISTS idx_github_webhook_events_event_type ON github_webhook_events(event_type)"
    ))
    conn.execute(sa.text(
        "CREATE INDEX IF NOT EXISTS idx_github_webhook_events_received_at ON github_webhook_events(received_at)"
    ))


def downgrade() -> None:
    # Not reversible — dropping all tables would destroy data
    pass
