"""Schema version tracking and migration support."""

from __future__ import annotations

from pathlib import Path

from psycopg import AsyncConnection


CURRENT_SCHEMA_VERSION = 9


async def get_schema_version(conn: AsyncConnection) -> int | None:
    """Return the current schema version, or None if the table doesn't exist."""
    result = await conn.execute(
        "SELECT EXISTS ("
        "  SELECT 1 FROM information_schema.tables "
        "  WHERE table_name = 'schema_version'"
        ")"
    )
    row = await result.fetchone()
    if not row or not row[0]:
        return None

    result = await conn.execute(
        "SELECT MAX(version) FROM schema_version"
    )
    row = await result.fetchone()
    return row[0] if row else None


_MIGRATION_V4 = """\
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)
);

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


async def _migrate_to_v4(conn: AsyncConnection) -> None:
    """Create file_progress table for existing databases."""
    await conn.execute(_MIGRATION_V4)
    await conn.commit()


_MIGRATION_V5 = """\
DO $$
BEGIN
    IF NOT EXISTS (
        SELECT 1 FROM information_schema.columns
        WHERE table_name = 'sessions' AND column_name = 'org'
    ) THEN
        ALTER TABLE sessions ADD COLUMN org TEXT;
        CREATE INDEX idx_sessions_org ON sessions (org);
    END IF;
END $$;

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


async def _migrate_to_v5(conn: AsyncConnection) -> None:
    """Add org column to sessions table."""
    await conn.execute(_MIGRATION_V5)
    await conn.commit()


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

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


async def _migrate_to_v6(conn: AsyncConnection) -> None:
    """Create version_config table for server-controlled version gating."""
    await conn.execute(_MIGRATION_V6)
    await conn.commit()


_MIGRATION_V7 = """\
DO $$
BEGIN
    IF NOT EXISTS (
        SELECT 1 FROM information_schema.columns
        WHERE table_name = 'sessions' AND column_name = 'device_id'
    ) THEN
        ALTER TABLE sessions ADD COLUMN device_id TEXT;
        CREATE INDEX idx_sessions_device_id ON sessions (device_id);
    END IF;
END $$;

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


async def _migrate_to_v7(conn: AsyncConnection) -> None:
    """Add device_id column to sessions table."""
    await conn.execute(_MIGRATION_V7)
    await conn.commit()


_MIGRATION_V8 = """\
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);

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


async def _migrate_to_v8(conn: AsyncConnection) -> None:
    """Create daemon_heartbeats table."""
    await conn.execute(_MIGRATION_V8)
    await conn.commit()


_MIGRATION_V9 = """\
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;
"""


async def _migrate_to_v9(conn: AsyncConnection) -> None:
    """Create daemon_commands table for server-initiated command queue."""
    await conn.execute(_MIGRATION_V9)
    await conn.commit()


def _read_schema_sql() -> str:
    """Read the schema.sql file bundled with the package."""
    schema_path = Path(__file__).parent / "schema.sql"
    return schema_path.read_text()


async def ensure_schema(conn: AsyncConnection) -> int:
    """Apply the schema if not present. Returns the current version.

    This is idempotent — safe to call on every startup.
    """
    version = await get_schema_version(conn)
    if version is None:
        sql = _read_schema_sql()
        await conn.execute(sql)
        await conn.commit()
        return CURRENT_SCHEMA_VERSION
    if version < 4:
        await _migrate_to_v4(conn)
        version = 4
    if version < 5:
        await _migrate_to_v5(conn)
        version = 5
    if version < 6:
        await _migrate_to_v6(conn)
        version = 6
    if version < 7:
        await _migrate_to_v7(conn)
        version = 7
    if version < 8:
        await _migrate_to_v8(conn)
        version = 8
    if version < 9:
        await _migrate_to_v9(conn)
        version = 9
    return version
