"""SQLite database layer for HomelabToolkit.

Boring and database-first: a single local SQLite file holds the music-library
scan results, MusicBrainz cache, and collection-completeness data. The schema is
created idempotently at startup (``init_db``), which doubles as the migration —
every statement uses ``IF NOT EXISTS``.

Connections are opened per call (cheap for SQLite) so each thread/request gets
its own handle. WAL mode lets the UI keep reading while a scan job writes.
"""

from __future__ import annotations

import os
import sqlite3
from contextlib import contextmanager
from datetime import datetime, timezone
from pathlib import Path

DB_PATH = Path(os.environ.get("DB_PATH", "cache/homelab.db"))


def now_iso() -> str:
    return datetime.now(timezone.utc).isoformat(timespec="seconds").replace("+00:00", "Z")


@contextmanager
def connect():
    DB_PATH.parent.mkdir(parents=True, exist_ok=True)
    conn = sqlite3.connect(str(DB_PATH), timeout=30)
    conn.row_factory = sqlite3.Row
    conn.execute("PRAGMA journal_mode=WAL")
    conn.execute("PRAGMA synchronous=NORMAL")
    conn.execute("PRAGMA foreign_keys=ON")
    conn.execute("PRAGMA busy_timeout=8000")
    try:
        yield conn
        conn.commit()
    except Exception:
        conn.rollback()
        raise
    finally:
        conn.close()


SCHEMA = """
CREATE TABLE IF NOT EXISTS library_scan_runs (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    kind TEXT NOT NULL,                       -- 'scan' | 'metadata'
    status TEXT NOT NULL,                     -- 'running' | 'completed' | 'failed'
    started_at TEXT,
    completed_at TEXT,
    error_message TEXT,
    files_scanned INTEGER DEFAULT 0,
    albums_found INTEGER DEFAULT 0,
    artists_found INTEGER DEFAULT 0,
    progress TEXT
);

CREATE TABLE IF NOT EXISTS library_artists (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    name TEXT NOT NULL,
    name_normalized TEXT NOT NULL UNIQUE,
    mbid TEXT,
    is_various INTEGER DEFAULT 0,
    is_active INTEGER DEFAULT 1,
    created_at TEXT,
    updated_at TEXT
);

CREATE TABLE IF NOT EXISTS library_albums (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    artist_id INTEGER NOT NULL REFERENCES library_artists(id) ON DELETE CASCADE,
    title TEXT NOT NULL,
    title_normalized TEXT NOT NULL,
    year INTEGER DEFAULT 0,
    mbid TEXT,
    is_active INTEGER DEFAULT 1,
    created_at TEXT,
    updated_at TEXT,
    UNIQUE(artist_id, title_normalized, year)
);

CREATE TABLE IF NOT EXISTS library_tracks (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    album_id INTEGER NOT NULL REFERENCES library_albums(id) ON DELETE CASCADE,
    title TEXT,
    track_number INTEGER,
    disc_number INTEGER,
    file_path TEXT NOT NULL UNIQUE,
    file_mtime REAL,
    file_size INTEGER,
    mbid TEXT,
    is_active INTEGER DEFAULT 1,
    last_seen_scan_id INTEGER,
    created_at TEXT,
    updated_at TEXT
);

CREATE TABLE IF NOT EXISTS external_artist_matches (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    artist_id INTEGER NOT NULL UNIQUE REFERENCES library_artists(id) ON DELETE CASCADE,
    mb_artist_mbid TEXT,
    mb_artist_name TEXT,
    confidence REAL DEFAULT 0,
    status TEXT,                              -- 'matched' | 'not_found' | 'error' | 'skipped'
    checked_at TEXT
);

CREATE TABLE IF NOT EXISTS external_releases (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    artist_id INTEGER NOT NULL REFERENCES library_artists(id) ON DELETE CASCADE,
    mb_release_group_mbid TEXT NOT NULL,
    title TEXT NOT NULL,
    title_normalized TEXT,
    first_release_year INTEGER DEFAULT 0,
    primary_type TEXT,
    secondary_types TEXT,                     -- JSON array
    fetched_at TEXT,
    UNIQUE(artist_id, mb_release_group_mbid)
);

CREATE TABLE IF NOT EXISTS collection_completeness (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    artist_id INTEGER NOT NULL REFERENCES library_artists(id) ON DELETE CASCADE,
    release_group_mbid TEXT NOT NULL,
    local_album_id INTEGER REFERENCES library_albums(id) ON DELETE SET NULL,
    title TEXT,
    year INTEGER DEFAULT 0,
    status TEXT NOT NULL,                     -- owned|probably_owned|missing|ignored|uncertain
    confidence REAL DEFAULT 0,
    reason TEXT,
    source TEXT DEFAULT 'musicbrainz',
    manual_override INTEGER DEFAULT 0,
    updated_at TEXT,
    UNIQUE(artist_id, release_group_mbid)
);

CREATE TABLE IF NOT EXISTS mb_cache (
    cache_key TEXT PRIMARY KEY,
    payload TEXT,
    fetched_at TEXT
);

CREATE TABLE IF NOT EXISTS genre_overrides (
    artist_key TEXT PRIMARY KEY,              -- normalized (lowercased) artist name
    artist TEXT NOT NULL,                     -- original display casing
    genre TEXT NOT NULL,
    updated_at TEXT
);

CREATE INDEX IF NOT EXISTS idx_albums_artist ON library_albums(artist_id);
CREATE INDEX IF NOT EXISTS idx_tracks_album ON library_tracks(album_id);
CREATE INDEX IF NOT EXISTS idx_tracks_path ON library_tracks(file_path);
CREATE INDEX IF NOT EXISTS idx_releases_artist ON external_releases(artist_id);
CREATE INDEX IF NOT EXISTS idx_completeness_artist ON collection_completeness(artist_id);
CREATE INDEX IF NOT EXISTS idx_completeness_status ON collection_completeness(status);
CREATE INDEX IF NOT EXISTS idx_scan_runs_kind ON library_scan_runs(kind, id);
"""


def init_db() -> None:
    with connect() as conn:
        conn.executescript(SCHEMA)
