from __future__ import annotations

import sqlite3
from collections.abc import Iterator
from contextlib import contextmanager
from pathlib import Path

from app.config import settings


SCHEMA = """
PRAGMA journal_mode = WAL;
PRAGMA foreign_keys = ON;

CREATE TABLE IF NOT EXISTS tasks (
    id TEXT PRIMARY KEY,
    type TEXT NOT NULL,
    app_id TEXT,
    app_name TEXT,
    status TEXT NOT NULL,
    progress INTEGER DEFAULT 0,
    current_step TEXT,
    current_component_id TEXT,
    error_message TEXT,
    created_at TEXT NOT NULL,
    started_at TEXT,
    finished_at TEXT
);

CREATE TABLE IF NOT EXISTS task_logs (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    task_id TEXT NOT NULL,
    timestamp TEXT NOT NULL,
    level TEXT NOT NULL,
    message TEXT NOT NULL,
    FOREIGN KEY (task_id) REFERENCES tasks(id) ON DELETE CASCADE
);

CREATE TABLE IF NOT EXISTS installed_apps (
    app_id TEXT PRIMARY KEY,
    app_name TEXT NOT NULL,
    version TEXT NOT NULL,
    open_url TEXT,
    manifest_hash TEXT,
    status TEXT NOT NULL,
    installed_at TEXT NOT NULL,
    updated_at TEXT NOT NULL
);

CREATE TABLE IF NOT EXISTS installed_components (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    app_id TEXT NOT NULL,
    component_id TEXT NOT NULL,
    type TEXT NOT NULL,
    install_order INTEGER NOT NULL,
    status TEXT NOT NULL,
    package_name TEXT,
    package_version TEXT,
    service_name TEXT,
    service_check_status TEXT DEFAULT 'not-checked',
    service_checks_json TEXT,
    docker_image TEXT,
    docker_digest TEXT,
    container_name TEXT,
    compose_project_name TEXT,
    installed_at TEXT NOT NULL,
    updated_at TEXT NOT NULL,
    UNIQUE(app_id, component_id)
);

CREATE TABLE IF NOT EXISTS task_components (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    task_id TEXT NOT NULL,
    app_id TEXT NOT NULL,
    component_id TEXT NOT NULL,
    type TEXT NOT NULL,
    install_order INTEGER NOT NULL,
    status TEXT NOT NULL,
    progress INTEGER DEFAULT 0,
    current_step TEXT,
    error_message TEXT,
    service_check_status TEXT DEFAULT 'not-checked',
    service_checks_json TEXT,
    started_at TEXT,
    finished_at TEXT,
    FOREIGN KEY (task_id) REFERENCES tasks(id) ON DELETE CASCADE
);

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


def _ensure_column(connection: sqlite3.Connection, table: str, column: str, definition: str) -> None:
    columns = {
        row["name"]
        for row in connection.execute(f"PRAGMA table_info({table})").fetchall()
    }
    if column not in columns:
        connection.execute(f"ALTER TABLE {table} ADD COLUMN {column} {definition}")


@contextmanager
def get_connection() -> Iterator[sqlite3.Connection]:
    connection = sqlite3.connect(settings.db_path, timeout=30)
    connection.row_factory = sqlite3.Row
    try:
        yield connection
        connection.commit()
    except Exception:
        connection.rollback()
        raise
    finally:
        connection.close()


def initialize_database() -> None:
    db_path = Path(settings.db_path)
    db_path.parent.mkdir(parents=True, exist_ok=True)
    settings.cache_dir.mkdir(parents=True, exist_ok=True)
    settings.log_dir.mkdir(parents=True, exist_ok=True)
    with get_connection() as connection:
        connection.executescript(SCHEMA)
        _ensure_column(connection, "installed_apps", "open_url", "TEXT")
        _ensure_column(
            connection,
            "installed_components",
            "service_check_status",
            "TEXT DEFAULT 'not-checked'",
        )
        _ensure_column(connection, "installed_components", "service_checks_json", "TEXT")
        _ensure_column(
            connection,
            "task_components",
            "service_check_status",
            "TEXT DEFAULT 'not-checked'",
        )
        _ensure_column(connection, "task_components", "service_checks_json", "TEXT")
