from dataclasses import dataclass
from functools import partial
from typing import Annotated, TypedDict
from uuid import UUID

from pydantic import Field

from ..tools.types import MaybePlural, option_fmap, un_maybe_plural

option_uuid = partial(option_fmap, UUID)
OptionUUID = Annotated[UUID | None, Field(default=None)]


def to_uuids(values: MaybePlural[str] | None) -> list[UUID]:
	return [UUID(i) for i in un_maybe_plural(values)]


class MusicBrainzTags(TypedDict, total=False):
	"""
	The MusicBrainz tags mpd-now-playable expects and will load (all optional).
	They're named slightly differently than the actual MusicBrainz IDs they
	store - use to_brainz to map them across to their canonical form.
	https://picard-docs.musicbrainz.org/downloads/MusicBrainz_Picard_Tag_Map.html
	"""

	#: MusicBrainz Recording ID
	musicbrainz_trackid: str
	#: MusicBrainz Track ID
	musicbrainz_releasetrackid: str
	#: MusicBrainz Artist ID
	musicbrainz_artistid: MaybePlural[str]
	#: MusicBrainz Release ID
	musicbrainz_albumid: MaybePlural[str]
	#: MusicBrainz Release Artist ID
	musicbrainz_albumartistid: MaybePlural[str]
	#: MusicBrainz Release Group ID
	musicbrainz_releasegroupid: str
	#: MusicBrainz Work ID
	musicbrainz_workid: str


@dataclass(slots=True)
class MusicBrainzIds:
	#: A MusicBrainz recording represents audio from a specific performance.
	#: For example, if the same song was released as a studio recording and as
	#: a live performance, those two versions of the song are different
	#: recordings. The song itself is considered a "work", of which two
	#: recordings were made. However, recordings are not always associated with
	#: a work in the MusicBrainz database, and Picard won't load work IDs by
	#: default (you have to enable "use track relationships" in the options),
	#: so recording IDs are a much more reliable way to identify a particular
	#: song.
	#: https://musicbrainz.org/doc/Recording
	recording: OptionUUID

	#: A MusicBrainz work represents the idea of a particular song or creation
	#: (it doesn't have to be audio). Each work may have multiple recordings
	#: (studio versus live, different performers, etc.), with the work ID
	#: grouping them together.
	#: https://musicbrainz.org/doc/Work
	work: OptionUUID

	#: A MusicBrainz track represents a specific instance of a recording
	#: appearing as part of some release. For example, if the same song appears
	#: on both two-CD and four-CD versions of a soundtrack, then it will be
	#: considered the same "recording" in both cases, but different "tracks".
	#: https://musicbrainz.org/doc/Track
	track: OptionUUID

	#: A MusicBrainz artist is pretty intuitively the artist who recorded the
	#: song. This particular ID refers to the individual recording's artist or
	#: artists, which may be distinct from the release artist below when a
	#: release contains recordings from many different artists.
	#: https://musicbrainz.org/doc/Artist
	artist: list[UUID]

	#: A MusicBrainz release roughly corresponds to an "album", and indeed is
	#: stored in a tag called MUSICBRAINZ_ALBUMID. The more general name is
	#: meant to encompass all the different ways music can be released.
	#: https://musicbrainz.org/doc/Release
	release: list[UUID]

	#: Again, the release artist corresponds to an "album artist". These MBIDs
	#: refer to the same artists in the MusicBrainz database that individual
	#: recordings' artist MBIDs do.
	release_artist: list[UUID]

	#: A MusicBrainz release group roughly corresponds to "all the editions of
	#: a particular album". For example, if the same album were released on CD,
	#: vinyl records, and as a digital download, then all of those would be
	#: different releases but share a release group. Note that MPD's support
	#: for this tag is relatively new (July 2023) and doesn't seem especially
	#: reliable, so it might be missing here even if your music has been tagged
	#: with it. Not sure why. https://musicbrainz.org/doc/Release_Group
	release_group: OptionUUID


def to_brainz(tags: MusicBrainzTags) -> MusicBrainzIds:
	return MusicBrainzIds(
		recording=option_uuid(tags.get("musicbrainz_trackid")),
		work=option_uuid(tags.get("musicbrainz_workid")),
		track=option_uuid(tags.get("musicbrainz_releasetrackid")),
		artist=to_uuids(tags.get("musicbrainz_artistid")),
		release=to_uuids(tags.get("musicbrainz_albumid")),
		release_artist=to_uuids(tags.get("musicbrainz_albumartistid")),
		release_group=option_uuid(tags.get("musicbrainz_releasegroupid")),
	)
