"""
tag_reader.py — Læser og skriver metadata fra lydfiler.

Understøttede formater og danse-tag support:
  MP3   — læs + skriv danse (ID3 TXXX-felter)
  FLAC  — læs + skriv danse (Vorbis Comments)
  OGG   — læs + skriv danse (Vorbis Comments)
  OPUS  — læs + skriv danse (Vorbis Comments)
  M4A   — læs + skriv danse (MP4 custom felt ----:LINEDANCE:DANCE)
  WAV   — læs metadata, ingen danse-tag support
  WMA   — læs metadata, ingen danse-tag support
  AIFF  — læs metadata, ingen danse-tag support

Danse gemmes ALTID i SQLite uanset format.
Fil-skrivning er kun muligt for de formater der understøtter custom tags.
"""

import os
from datetime import datetime, timezone
from pathlib import Path

try:
    from mutagen import File as MutagenFile
    from mutagen.id3 import ID3, TXXX
    from mutagen.flac import FLAC
    from mutagen.mp4 import MP4, MP4FreeForm
    MUTAGEN_AVAILABLE = True
except ImportError:
    MUTAGEN_AVAILABLE = False
    print("Advarsel: mutagen ikke installeret — tag-læsning deaktiveret")


# Filtyper vi høster metadata fra
SUPPORTED_EXTENSIONS = {
    ".mp3", ".flac", ".wav", ".m4a", ".aac",
    ".ogg", ".opus", ".wma", ".aiff", ".aif",
}

# Formater der understøtter skrivning af danse-tags til fil
WRITABLE_DANCE_FORMATS = {".mp3", ".flac", ".ogg", ".opus", ".m4a"}

# Tag-nøgler brugt på tværs af formater
TXXX_DANCE_PREFIX  = "LINEDANCE_DANCE_"        # MP3:  TXXX:LINEDANCE_DANCE_1
VORBIS_DANCE_KEY   = "linedance_dance"          # FLAC/OGG: linedance_dance.1
M4A_DANCE_FREEFORM = "----:LINEDANCE:DANCE"     # M4A:  ----:LINEDANCE:DANCE (liste)


def is_supported(path: str | Path) -> bool:
    return Path(path).suffix.lower() in SUPPORTED_EXTENSIONS


def can_write_dances(path: str | Path) -> bool:
    """Returnerer True hvis formatet understøtter skrivning af danse-tags til fil."""
    return Path(path).suffix.lower() in WRITABLE_DANCE_FORMATS


def get_file_modified_at(path: str | Path) -> str:
    ts = os.path.getmtime(str(path))
    return datetime.fromtimestamp(ts, tz=timezone.utc).isoformat()


# ── Læsning ───────────────────────────────────────────────────────────────────

def read_tags(path: str | Path) -> dict:
    """
    Læser metadata og danse fra en lydfil.
    Returnerer dict med: title, artist, album, bpm, duration_sec,
                         file_format, file_modified_at, dances, can_write_dances.
    """
    path = Path(path)
    result = {
        "local_path":       str(path),
        "title":            path.stem,
        "artist":           "",
        "album":            "",
        "bpm":              0,
        "duration_sec":     0,
        "file_format":      path.suffix.lower().lstrip("."),
        "file_modified_at": get_file_modified_at(path),
        "dances":           [],
        "can_write_dances": can_write_dances(path),
    }

    if not MUTAGEN_AVAILABLE:
        return result

    try:
        audio = MutagenFile(str(path), easy=False)
        if audio is None:
            return result

        if hasattr(audio, "info") and audio.info:
            result["duration_sec"] = int(getattr(audio.info, "length", 0))

        ext = path.suffix.lower()

        if ext == ".mp3":
            _read_mp3(audio, result)
        elif ext == ".flac":
            _read_vorbis(audio, result)
        elif ext in (".ogg", ".opus"):
            _read_vorbis(audio, result)
        elif ext in (".m4a", ".aac", ".mp4"):
            _read_m4a(audio, result)
        else:
            _read_generic(audio, result)

    except Exception as e:
        print(f"Fejl ved læsning af {path}: {e}")

    return result


def _read_mp3(audio, result: dict):
    tags = audio.tags
    if not tags:
        return
    if "TIT2" in tags:
        result["title"]  = str(tags["TIT2"].text[0])
    if "TPE1" in tags:
        result["artist"] = str(tags["TPE1"].text[0])
    if "TALB" in tags:
        result["album"]  = str(tags["TALB"].text[0])
    if "TBPM" in tags:
        try:
            result["bpm"] = int(float(str(tags["TBPM"].text[0])))
        except (ValueError, TypeError):
            pass
    dances = {}
    for key, frame in tags.items():
        if key.startswith("TXXX:") and TXXX_DANCE_PREFIX in key:
            try:
                num = int(key.replace(f"TXXX:{TXXX_DANCE_PREFIX}", ""))
                dances[num] = str(frame.text[0])
            except (ValueError, IndexError):
                pass
    result["dances"] = [dances[k] for k in sorted(dances.keys())]


def _read_vorbis(audio, result: dict):
    """FLAC og OGG/Opus bruger begge Vorbis Comments."""
    tags = audio.tags
    if not tags:
        return
    result["title"]  = tags.get("title",  [result["title"]])[0]
    result["artist"] = tags.get("artist", [""])[0]
    result["album"]  = tags.get("album",  [""])[0]
    try:
        result["bpm"] = int(tags.get("bpm", [0])[0])
    except (ValueError, TypeError):
        pass
    # Danse gemmes som linedance_dance.1, linedance_dance.2 ...
    dances = {}
    for key, values in tags.items():
        if key.lower().startswith(f"{VORBIS_DANCE_KEY}."):
            try:
                num = int(key.split(".")[-1])
                dances[num] = values[0]
            except (ValueError, IndexError):
                pass
    # Fallback: enkelt felt linedance_dance med komma-separeret liste
    if not dances and VORBIS_DANCE_KEY in tags:
        result["dances"] = [d.strip() for d in tags[VORBIS_DANCE_KEY][0].split(",") if d.strip()]
        return
    result["dances"] = [dances[k] for k in sorted(dances.keys())]


def _read_m4a(audio, result: dict):
    tags = audio.tags
    if not tags:
        return
    if "\xa9nam" in tags:
        result["title"]  = str(tags["\xa9nam"][0])
    if "\xa9ART" in tags:
        result["artist"] = str(tags["\xa9ART"][0])
    if "\xa9alb" in tags:
        result["album"]  = str(tags["\xa9alb"][0])
    if "tmpo" in tags:
        try:
            result["bpm"] = int(tags["tmpo"][0])
        except (ValueError, TypeError):
            pass
    # Danse gemmes som ----:LINEDANCE:DANCE — én værdi per dans
    if M4A_DANCE_FREEFORM in tags:
        result["dances"] = [
            v.decode("utf-8") if isinstance(v, (bytes, MP4FreeForm)) else str(v)
            for v in tags[M4A_DANCE_FREEFORM]
        ]


def _read_generic(audio, result: dict):
    try:
        easy = MutagenFile(result["local_path"], easy=True)
        if easy and easy.tags:
            result["title"]  = easy.tags.get("title",  [result["title"]])[0]
            result["artist"] = easy.tags.get("artist", [""])[0]
            result["album"]  = easy.tags.get("album",  [""])[0]
    except Exception:
        pass


# ── Skrivning ─────────────────────────────────────────────────────────────────

def write_dances(path: str | Path, dances: list[str]) -> bool:
    """
    Skriver danse til filen hvis formatet understøtter det.
    Returnerer True ved succes, False hvis formatet ikke understøtter det.
    Kaster Exception ved fejl under skrivning.
    """
    if not MUTAGEN_AVAILABLE:
        return False

    path = Path(path)
    ext = path.suffix.lower()

    if ext not in WRITABLE_DANCE_FORMATS:
        return False

    if ext == ".mp3":
        return _write_mp3_dances(path, dances)
    elif ext in (".flac", ".ogg", ".opus"):
        return _write_vorbis_dances(path, dances)
    elif ext in (".m4a", ".aac"):
        return _write_m4a_dances(path, dances)

    return False


def _write_mp3_dances(path: Path, dances: list[str]) -> bool:
    try:
        tags = ID3(str(path))
        for key in [k for k in tags.keys() if TXXX_DANCE_PREFIX in k]:
            del tags[key]
        for i, name in enumerate(dances, start=1):
            tags.add(TXXX(encoding=3, desc=f"{TXXX_DANCE_PREFIX}{i}", text=name))
        tags.save(str(path))
        return True
    except Exception as e:
        print(f"MP3 skrivefejl {path}: {e}")
        return False


def _write_vorbis_dances(path: Path, dances: list[str]) -> bool:
    try:
        audio = MutagenFile(str(path), easy=False)
        if audio is None or audio.tags is None:
            return False
        # Slet eksisterende danse-felter
        keys_to_delete = [k for k in audio.tags.keys() if k.lower().startswith(f"{VORBIS_DANCE_KEY}.")]
        for key in keys_to_delete:
            del audio.tags[key]
        # Skriv nye — ét felt per dans
        for i, name in enumerate(dances, start=1):
            audio.tags[f"{VORBIS_DANCE_KEY}.{i}"] = name
        audio.save()
        return True
    except Exception as e:
        print(f"Vorbis skrivefejl {path}: {e}")
        return False


def _write_m4a_dances(path: Path, dances: list[str]) -> bool:
    try:
        audio = MP4(str(path))
        audio.tags[M4A_DANCE_FREEFORM] = [
            MP4FreeForm(name.encode("utf-8")) for name in dances
        ]
        audio.save()
        return True
    except Exception as e:
        print(f"M4A skrivefejl {path}: {e}")
        return False


# ── Hurtig læsning af kun danse (uden fuld tag-scan) ─────────────────────────

def read_dances_from_file(path: str | Path) -> list[str]:
    """Læser kun danse fra en fil — hurtigere end fuld read_tags()."""
    tags = read_tags(path)
    return tags.get("dances", [])
