"""``local`` source — indexes audio files from a mounted folder.

Walks a configured root directory and yields each audio file as a
:class:`SourceFile`. It does **not** parse tags or resolve artist/album — that's
enrichment's job (plan §6.2); this stays a thin discovery layer. ``source_id``
is the path relative to the root, so re-scans are idempotent.
"""

import os
from collections.abc import Iterator
from pathlib import Path

from app.domain.sources import SourceFile, SourceInfo
from app.infrastructure.db.models.enums import TrackSource

# Extensions we treat as audio. Mirrors the formats StreamingService serves.
_AUDIO_EXTENSIONS = frozenset(
    {"mp3", "flac", "m4a", "aac", "ogg", "opus", "wav", "wma", "aiff", "aif", "alac"}
)


class LocalFolderSource:
    """Implements :class:`app.domain.ports.IndexableSource`."""

    name = TrackSource.LOCAL.value

    def __init__(self, root: Path) -> None:
        self._root = root

    def info(self) -> SourceInfo:
        return SourceInfo(
            name=self.name,
            label="Local folder",
            kind="indexable",
            available=self.is_available(),
        )

    def is_available(self) -> bool:
        return self._root.is_dir()

    def scan(self) -> Iterator[SourceFile]:
        if not self.is_available():
            return
        for dirpath, _dirnames, filenames in os.walk(self._root):
            for filename in sorted(filenames):
                ext = Path(filename).suffix.lower().lstrip(".")
                if ext not in _AUDIO_EXTENSIONS:
                    continue
                path = Path(dirpath) / filename
                try:
                    size = path.stat().st_size
                except OSError:
                    continue  # vanished/unreadable between walk and stat → skip
                yield SourceFile(
                    source_id=path.relative_to(self._root).as_posix(),
                    path=path,
                    suggested_title=path.stem or "Unknown",
                    file_format=ext,
                    file_size=size,
                )
