"""
scanner.py — Scanning af musikbiblioteker i baggrunden. v0.9

Skriver til files-tabellen og finder/opretter sange i songs-tabellen.
"""
import os
import logging
import time
from pathlib import Path

logger = logging.getLogger(__name__)

SUPPORTED = {'.mp3', '.flac', '.m4a', '.ogg', '.wav', '.aiff', '.wma'}
import uuid as _uuid_module


def _find_or_create_song_conn(conn, title, artist, album, bpm,
                               duration_sec, mbid, acoustid) -> str:
    """Find eller opret sang via eksisterende forbindelse."""
    if mbid:
        row = conn.execute("SELECT id FROM songs WHERE mbid=?", (mbid,)).fetchone()
        if row:
            return row["id"]
    if acoustid:
        row = conn.execute("SELECT id FROM songs WHERE acoustid=?", (acoustid,)).fetchone()
        if row:
            if mbid:
                conn.execute("UPDATE songs SET mbid=? WHERE id=? AND mbid IS NULL", (mbid, row["id"]))
            return row["id"]
    if title:
        row = conn.execute(
            "SELECT id FROM songs WHERE title=? AND artist=?", (title, artist)
        ).fetchone()
        if row:
            if mbid:
                conn.execute("UPDATE songs SET mbid=? WHERE id=? AND mbid IS NULL", (mbid, row["id"]))
            return row["id"]
    new_id = str(_uuid_module.uuid4())
    conn.execute(
        "INSERT INTO songs (id, title, artist, album, bpm, duration_sec, mbid, acoustid) "
        "VALUES (?,?,?,?,?,?,?,?)",
        (new_id, title, artist, album, bpm, duration_sec, mbid or None, acoustid or None)
    )
    return new_id


def _upsert_file_conn(conn, song_id, local_path, file_format,
                       file_modified_at, extra_tags) -> str:
    """Opret eller opdater fil-post via eksisterende forbindelse."""
    existing = conn.execute(
        "SELECT id FROM files WHERE local_path=?", (local_path,)
    ).fetchone()
    if existing:
        conn.execute("""
            UPDATE files SET song_id=?, file_missing=0,
                file_format=?, file_modified_at=?, extra_tags=?
            WHERE id=?
        """, (song_id, file_format, file_modified_at, extra_tags, existing["id"]))
        return existing["id"]
    else:
        file_id = str(_uuid_module.uuid4())
        conn.execute(
            "INSERT INTO files (id, song_id, local_path, file_format, file_modified_at, extra_tags) "
            "VALUES (?,?,?,?,?,?)",
            (file_id, song_id, local_path, file_format, file_modified_at, extra_tags)
        )
        return file_id




def is_supported(path) -> bool:
    return Path(path).suffix.lower() in SUPPORTED


def get_file_mtime(path: Path) -> str:
    try:
        return str(os.path.getmtime(str(path)))
    except Exception:
        return ""


def scan_library(library_id: int, library_path: str, db_path: str,
                 overwrite_bpm: bool = False,
                 progress_callback=None) -> int:
    """
    Scan ét bibliotek og upsert til files + songs tabellerne.
    Returnerer antal scannede filer.
    """
    import sqlite3
    from local.tag_reader import read_tags

    base = Path(library_path)
    if not base.exists():
        return 0

    # Byg indeks over kendte filer (path → mtime)
    conn = sqlite3.connect(db_path, timeout=10)
    conn.row_factory = sqlite3.Row
    conn.execute("PRAGMA journal_mode=WAL")

    known = {}
    for row in conn.execute(
        "SELECT local_path, file_modified_at FROM files WHERE file_missing=0"
    ).fetchall():
        known[row["local_path"]] = row["file_modified_at"]

    # Find alle musikfiler
    all_files = []
    for dirpath, _, filenames in os.walk(str(base), followlinks=False):
        for fn in filenames:
            fp = Path(dirpath) / fn
            if is_supported(fp):
                all_files.append(fp)

    total = len(all_files)
    done  = 0

    for fp in all_files:
        path_str = str(fp)
        mtime    = get_file_mtime(fp)

        if progress_callback:
            progress_callback(done, total, fp.name)

        # Spring over uændrede filer
        if path_str in known and known[path_str] == mtime:
            done += 1
            time.sleep(0.005)
            continue

        try:
            tags = read_tags(str(fp))
            title  = tags.get("title", "") or fp.stem
            artist = tags.get("artist", "")
            album  = tags.get("album", "")
            bpm    = tags.get("bpm", 0)
            mbid   = tags.get("mbid", "")
            acoustid = tags.get("acoustid", "")
            duration_sec = tags.get("duration_sec", 0)
            file_format  = tags.get("file_format", fp.suffix.lstrip(".").lower())
            import json as _json
            _extra = tags.get("extra_tags", {})
            extra_tags = _json.dumps(_extra) if isinstance(_extra, dict) else (_extra or "{}")

            # Find eller opret sang — alt via samme conn
            song_id = _find_or_create_song_conn(
                conn, title, artist, album, bpm, duration_sec, mbid, acoustid
            )

            # Opdater BPM
            if bpm and bpm > 0:
                conn.execute(
                    "UPDATE songs SET bpm=? WHERE id=? AND (bpm=0 OR bpm IS NULL)",
                    (bpm, song_id)
                )

            # Opret eller opdater fil-post
            _upsert_file_conn(conn, song_id, path_str, file_format, mtime, extra_tags)

            # Dans-tags fra fil — synkroniser altid fra filen
            file_dances = tags.get("dances", [])
            if file_dances:
                import uuid
                # Slet eksisterende og genindsæt fra filen
                conn.execute("DELETE FROM song_dances WHERE song_id=?", (song_id,))
                for order, dance_name in enumerate(file_dances, start=1):
                    dance_row = conn.execute(
                        "SELECT id FROM dances WHERE name=? COLLATE NOCASE LIMIT 1",
                        (dance_name,)
                    ).fetchone()
                    if not dance_row:
                        cur = conn.execute(
                            "INSERT INTO dances (name) VALUES (?)", (dance_name,)
                        )
                        dance_id = cur.lastrowid
                    else:
                        dance_id = dance_row["id"]
                    conn.execute(
                        "INSERT OR IGNORE INTO song_dances (id, song_id, dance_id, dance_order) VALUES (?,?,?,?)",
                        (str(uuid.uuid4()), song_id, dance_id, order)
                    )
            conn.commit()

        except Exception as e:
            logger.warning(f"Scan fejl {fp.name}: {e}")

        done += 1
        time.sleep(0.02)

    # Marker manglende filer
    for path_str in known:
        if not Path(path_str).exists():
            conn.execute(
                "UPDATE files SET file_missing=1 WHERE local_path=?", (path_str,)
            )
    conn.commit()
    conn.close()

    logger.info(f"Scan færdig: {done} filer i {library_path}")
    return done