"""MutagenTagReader — reads embedded tags from a local audio file.

The offline first pass of enrichment (plan §6.2): well-tagged files get correct
artist/album/title without any network call. mutagen's ``easy=True`` mode
normalises tag keys across ID3 / Vorbis / MP4, so one code path covers all the
formats the library accepts. Parsing is blocking, so it runs in a worker thread.
"""

import re
from pathlib import Path

import anyio
from mutagen import File as MutagenFile  # type: ignore[attr-defined]

from app.core.logging import get_logger
from app.domain.entities.metadata import AudioTags

log = get_logger(__name__)

_YEAR_RE = re.compile(r"(\d{4})")


def _first(value: object) -> str | None:
    """EasyXxx tags expose values as lists; take the first non-empty string."""
    if isinstance(value, list):
        value = value[0] if value else None
    if value is None:
        return None
    text = str(value).strip()
    return text or None


def _parse_year(value: object) -> int | None:
    text = _first(value)
    if text is None:
        return None
    m = _YEAR_RE.search(text)
    return int(m.group(1)) if m else None


def _parse_track_number(value: object) -> int | None:
    text = _first(value)
    if text is None:
        return None
    # "3" or "3/12" → 3
    head = text.split("/", 1)[0].strip()
    return int(head) if head.isdigit() else None


class MutagenTagReader:
    """Implements :class:`app.domain.ports.AudioTagReader`."""

    async def read(self, path: Path) -> AudioTags | None:
        try:
            return await anyio.to_thread.run_sync(self._read_sync, path)
        except Exception:
            log.warning("tag_read_failed", path=str(path))
            return None

    def _read_sync(self, path: Path) -> AudioTags | None:
        audio = MutagenFile(str(path), easy=True)
        if audio is None:
            return None  # unrecognised container

        tags = audio.tags or {}
        info = getattr(audio, "info", None)

        duration = None
        bitrate = None
        if info is not None:
            length = getattr(info, "length", None)
            if length:
                duration = round(float(length))
            raw_bitrate = getattr(info, "bitrate", None)
            if raw_bitrate:
                bitrate = int(raw_bitrate) // 1000  # bits/s → kbps for display

        return AudioTags(
            title=_first(tags.get("title")),
            artist=_first(tags.get("artist")),
            album=_first(tags.get("album")),
            album_artist=_first(tags.get("albumartist")),
            genre=_first(tags.get("genre")),
            year=_parse_year(tags.get("date") or tags.get("year")),
            track_number=_parse_track_number(tags.get("tracknumber")),
            duration_seconds=duration,
            bitrate=bitrate,
        )
