import sqlite3
from typing import TYPE_CHECKING

from ._styles import STYLES

if TYPE_CHECKING:
    import pathlib


def _has_column(conn: sqlite3.Connection, table: str, column: str) -> bool:
    rows = conn.execute(f"PRAGMA table_info({table})").fetchall()
    return any(row[1] == column for row in rows)


def init_db(db_path: pathlib.Path | str) -> sqlite3.Connection:
    conn = sqlite3.connect(db_path)
    conn.execute("""
        CREATE TABLE IF NOT EXISTS runs (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            input_file_path TEXT,
            resized_file_path TEXT,
            generated_description TEXT,
            description_model_name TEXT,
            desc_temp REAL,
            output_image_path TEXT,
            image_model_name TEXT,
            img_temp REAL,
            desc_gen_ms INTEGER,
            img_gen_ms INTEGER,
            started_at TEXT,
            input_album_photo_id TEXT,
            style TEXT
        )
    """)
    styles_existed = (
        conn.execute(
            "SELECT 1 FROM sqlite_master WHERE type='table' AND name='styles'",
        ).fetchone()
        is not None
    )
    conn.execute("""
        CREATE TABLE IF NOT EXISTS styles (
            name TEXT PRIMARY KEY,
            description TEXT NOT NULL,
            used_count INTEGER NOT NULL DEFAULT 0,
            created_at TEXT NOT NULL DEFAULT (datetime('now'))
        )
    """)
    if not styles_existed:
        conn.executemany(
            "INSERT OR IGNORE INTO styles (name, description) VALUES (?, ?)",
            STYLES,
        )
    conn.execute("""
        CREATE TABLE IF NOT EXISTS config (
            key TEXT PRIMARY KEY,
            value TEXT NOT NULL
        )
    """)
    conn.commit()
    return conn


def get_config(conn: sqlite3.Connection, key: str) -> str | None:
    row = conn.execute(
        "SELECT value FROM config WHERE key = ?",
        (key,),
    ).fetchone()
    return row[0] if row else None


def set_config(conn: sqlite3.Connection, key: str, value: str) -> None:
    conn.execute(
        "INSERT OR REPLACE INTO config (key, value) VALUES (?, ?)",
        (key, value),
    )
    conn.commit()


def insert_run(conn: sqlite3.Connection, input_file_path: str) -> int:
    cur = conn.execute(
        "INSERT INTO runs (input_file_path, started_at) VALUES (?, datetime('now'))",
        (input_file_path,),
    )
    conn.commit()
    if cur.lastrowid is None:
        msg = "INSERT did not return a row ID"
        raise RuntimeError(msg)
    return cur.lastrowid


def update_run(conn: sqlite3.Connection, run_id: int, **kwargs: str | float) -> None:
    cols = ", ".join(f"{k} = ?" for k in kwargs)
    conn.execute(f"UPDATE runs SET {cols} WHERE id = ?", (*kwargs.values(), run_id))
    conn.commit()


def avg_duration_ms(conn: sqlite3.Connection, column: str) -> float | None:
    row = conn.execute(
        f"SELECT AVG({column}) FROM runs WHERE {column} IS NOT NULL",
    ).fetchone()
    val = row[0] if row else None
    return float(val) if val is not None else None


def lookup_description(
    conn: sqlite3.Connection,
    input_file_path: str,
    model: str,
) -> str | None:
    row = conn.execute(
        "SELECT generated_description FROM runs "
        "WHERE input_file_path = ? "
        "AND description_model_name = ? "
        "AND generated_description IS NOT NULL "
        "ORDER BY id DESC LIMIT 1",
        (input_file_path, model),
    ).fetchone()
    return row[0] if row else None


def get_recent_runs(
    conn: sqlite3.Connection,
    limit: int = 20,
) -> list[tuple[str | None, ...]]:
    """Return the most recent runs for display, newest first."""
    return conn.execute(
        "SELECT started_at, input_file_path, style,"
        " desc_gen_ms, img_gen_ms, output_image_path"
        " FROM runs ORDER BY id DESC LIMIT ?",
        (limit,),
    ).fetchall()
