"""
SQLite schema migration runner for Rivet desktop app.

Uses SQLite's built-in PRAGMA user_version to track the schema version.
Migrations run automatically at startup — no user interaction required.
If a migration fails it is logged and skipped so the app still opens.

Usage:
    Call run_migrations() in app.py on_startup, before create_db_and_tables().

Adding a new migration:
    Append a tuple to MIGRATIONS. Never modify or remove existing entries.
    (version: int, description: str, sql_statements: list[str])
"""

import logging
from sqlalchemy import text
from app.database import engine

logger = logging.getLogger(__name__)

# ── Migration list ────────────────────────────────────────────────────────────
# Version 1 = baseline schema created by SQLModel.metadata.create_all().
# Append new entries here for every schema change going forward.
# ─────────────────────────────────────────────────────────────────────────────
MIGRATIONS: list[tuple[int, str, list[str]]] = [
    (1, "baseline schema", []),
    (2, "add generated_reports table", [
        """
        CREATE TABLE IF NOT EXISTS generated_reports (
            id TEXT PRIMARY KEY,
            user_id TEXT NOT NULL,
            title TEXT NOT NULL,
            user_context TEXT,
            content TEXT NOT NULL,
            scan_ids TEXT NOT NULL,
            created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
        )
        """,
        "CREATE INDEX IF NOT EXISTS ix_generated_reports_user_id ON generated_reports (user_id)",
        "CREATE INDEX IF NOT EXISTS ix_generated_reports_created_at ON generated_reports (created_at)",
    ]),
    # Example of a future migration:
    # (2, "add severity index on scan_records", [
    #     "CREATE INDEX IF NOT EXISTS ix_scan_records_severity "
    #     "ON scan_records ((scan_data->>'$.severity'))",
    # ]),
]


def _get_version(conn) -> int:
    return conn.execute(text("PRAGMA user_version")).scalar() or 0


def _set_version(conn, version: int) -> None:
    # PRAGMA user_version cannot be parameterised — version is always an int
    conn.execute(text(f"PRAGMA user_version = {version}"))


def run_migrations() -> None:
    """Apply any pending migrations. Safe to call on every startup."""
    with engine.begin() as conn:
        current = _get_version(conn)
        pending = [(v, d, s) for v, d, s in MIGRATIONS if v > current]

        if not pending:
            logger.debug("DB schema up to date (version %d)", current)
            return

        for version, description, statements in pending:
            try:
                logger.info("Applying migration v%d: %s", version, description)
                for sql in statements:
                    conn.execute(text(sql))
                _set_version(conn, version)
                logger.info("Migration v%d applied", version)
            except Exception as exc:
                logger.error(
                    "Migration v%d (%s) failed — app will continue with partial schema: %s",
                    version, description, exc,
                )
                break  # stop here; don't skip ahead to higher versions
