"""Stable, reversible mapping between Subsonic opaque string ids and our UUIDs.

Subsonic ids are opaque strings; ours are UUIDs. We use a type-prefixed,
human-debuggable convention (``tr-<uuid>`` track, ``al-<uuid>`` album,
``ar-<uuid>`` artist, ``pl-<uuid>`` playlist). Cover-art ids reuse the entity's
own id (an album cover is ``al-<uuid>``, a track cover ``tr-<uuid>``). Centralize
encode/decode here so the convention lives in exactly one place.
"""

import uuid
from enum import StrEnum

from app.domain.errors import NotFoundError


class IdKind(StrEnum):
    TRACK = "tr"
    ALBUM = "al"
    ARTIST = "ar"
    PLAYLIST = "pl"


def encode(kind: IdKind, value: uuid.UUID) -> str:
    return f"{kind.value}-{value}"


def encode_track(value: uuid.UUID) -> str:
    return encode(IdKind.TRACK, value)


def encode_album(value: uuid.UUID) -> str:
    return encode(IdKind.ALBUM, value)


def encode_artist(value: uuid.UUID) -> str:
    return encode(IdKind.ARTIST, value)


def encode_playlist(value: uuid.UUID) -> str:
    return encode(IdKind.PLAYLIST, value)


def parse(raw: str) -> tuple[IdKind, uuid.UUID]:
    """Decode any prefixed id into its kind + UUID. Raises ``NotFoundError`` on a
    malformed id (an unknown id is, from the client's view, simply not found)."""
    prefix, _, rest = raw.partition("-")
    try:
        kind = IdKind(prefix)
        value = uuid.UUID(rest)
    except ValueError as exc:
        raise NotFoundError(f"Unknown id {raw!r}.") from exc
    return kind, value


def _decode_as(raw: str, expected: IdKind) -> uuid.UUID:
    kind, value = parse(raw)
    if kind is not expected:
        raise NotFoundError(f"Expected a {expected.name.lower()} id, got {raw!r}.")
    return value


def decode_track(raw: str) -> uuid.UUID:
    return _decode_as(raw, IdKind.TRACK)


def decode_album(raw: str) -> uuid.UUID:
    return _decode_as(raw, IdKind.ALBUM)


def decode_artist(raw: str) -> uuid.UUID:
    return _decode_as(raw, IdKind.ARTIST)


def decode_playlist(raw: str) -> uuid.UUID:
    return _decode_as(raw, IdKind.PLAYLIST)
