from typing import Dict, List

from melodine.configs import YTMUSIC
from melodine.models import ytmusic
from melodine.models.ytmusic.artist import Artist
from melodine.models.ytmusic.track import Track
from melodine.utils import Image, URIBase


class Album(URIBase):

    __slots__ = (
        '_data',
        '_id',
        '_name',
        '_href',
        'uri',
        '_audio_playlist_id',
        '_type',
        '_description',
        '_year',
        '_total_tracks',
        '_duration',
        '_images',
        '_artists',
        '_tracks',
    )

    def __init__(self, data: Dict) -> None:
        self._data = None

        self._id: str = data.get(
            'browseId',
            str()
        )
        self._name: str = data.get('title')
        self._href: str
        self.uri: str = f"ytmusic:album:{self.id}"

        self._audio_playlist_id: str = data.get('audioPlaylistId', None)

        self._type: str = data.get('type', None)
        self._description: str = data.get('description', None)
        self._year: str = data.get('year', None)
        self._total_tracks: int = data.get('trackCount', None)
        self._duration: int = data.get('duration_seconds', None)

        self._images: List[Image] = [
            Image(**image)
            for image in data.get('thumbnails', [])
        ]

        self._artists: List[Dict] = data.get('artists', [])
        self._tracks: List[Dict] = data.get('tracks', [])

    def _get_data(self) -> None:
        self._data = YTMUSIC.get_album(self.id)

    @property
    def id(self) -> str:
        if not self._id:
            self._id = YTMUSIC.get_album_browse_id(
                self._data.get('audioPlaylistId')
            )
        return self._id

    @classmethod
    def from_id(cls, id: str) -> "Album":
        # the given id could be a playlistsId or a browseId.
        # a browseId starts with 'MPREb' and a audioPlaylistId starts with 'OLAK5uy'
        return cls(data={'browseId' if id.startswith("MPREb") else 'audioPlaylistId': id})

    @classmethod
    def partial(cls, data: Dict) -> "Album":
        """
        for data obtained from tracks and other shorter sources.
            `{'name': '<album-name>', 'id': '<album-id>'}`
        """
        return cls(data={
            'title': data['name'],
            'browseId': data['id']
        })

    @property
    def name(self) -> str:
        if self._name is None:
            if self._data is None:
                self._get_data()
            self._name = self._data['title']
        return self._name

    @property
    def href(self) -> str:
        return "https://music.youtube.com/playlist?list=" + self.audio_playlist_id

    @property
    def audio_playlist_id(self) -> str:
        if self._audio_playlist_id is None:
            if self._data is None:
                self._get_data()
            self._audio_playlist_id = self._data.get('audioPlaylistId')
        return self._audio_playlist_id

    @property
    def type(self) -> str:
        if self._type is None:
            if self._data is None:
                self._get_data()
            self._type = self._data.get('type')
        return self._type

    @property
    def description(self) -> str:
        if self._description is None:
            if self._data is None:
                self._get_data()
            self._description = self._data.get('description')
        return self._description

    @property
    def year(self) -> str:
        if self._year is None:
            if self._data is None:
                self._get_data()
            self._year = self._data.get('year')
        return self._year

    @property
    def total_tracks(self) -> int:
        if self._total_tracks is None:
            if self._data is None:
                self._get_data()
            self._total_tracks = self._data.get('trackCount')
        return self._total_tracks

    @property
    def duration(self) -> int:
        if self._duration is None:
            if self._data is None:
                self._get_data()
            self._duration = self._data.get('duration_seconds')
        return self._duration

    @property
    def images(self) -> List[Image]:
        if len(self._images) == 0:
            if self._data is None:
                self._get_data()
            self._images = [
                Image(**image)
                for image in self._data['thumbnails']
            ]
        return self._images

    @property
    def artists(self) -> List["ytmusic.Artist"]:
        if len(self._artists) == 0:
            if self._data is None:
                self._get_data()
            self._artists = self._data.get('artists')
        for idx, artist in enumerate(self._artists.copy()):
            if not isinstance(artist, Artist):
                artist = Artist.partial(artist)
                self._artists.insert(idx, artist)
                del self._artists[idx + 1]
        return self._artists

    @property
    def tracks(self) -> List["ytmusic.Track"]:
        if len(self._tracks) == 0:
            if self._data is None:
                self._get_data()
            self._tracks = self._data.get('tracks')
        for idx, track in enumerate(self._tracks.copy()):
            if not isinstance(track, Track):
                track = Track(track, album=self)
                self._tracks.insert(idx, track)
                del self._tracks[idx + 1]
        return self._tracks
