import logging

from deezer.errors import GWAPIError, APIError
from deezer.utils import map_user_playlist, map_track, map_album

from deemix.types.DownloadObjects import Single, Collection
from deemix.errors import GenerationError, ISRCnotOnDeezer, InvalidID, NotYourPrivatePlaylist

logger = logging.getLogger('deemix')

def generateTrackItem(dz, link_id, bitrate, trackAPI=None, albumAPI=None):
    # Get essential track info
    if not trackAPI:
        if str(link_id).startswith("isrc") or int(link_id) > 0:
            try:
                trackAPI = dz.api.get_track(link_id)
            except APIError as e:
                raise GenerationError(f"https://deezer.com/track/{link_id}", str(e)) from e

            # Check if is an isrc: url
            if str(link_id).startswith("isrc"):
                if 'id' in trackAPI and 'title' in trackAPI:
                    link_id = trackAPI['id']
                else:
                    raise ISRCnotOnDeezer(f"https://deezer.com/track/{link_id}")
        else:
            trackAPI_gw = dz.gw.get_track(link_id)
            trackAPI = map_track(trackAPI_gw)
    else:
        link_id = trackAPI['id']
    if not str(link_id).strip('-').isdecimal(): raise InvalidID(f"https://deezer.com/track/{link_id}")

    cover = None
    if trackAPI['album']['cover_small']:
        cover = trackAPI['album']['cover_small'][:-24] + '/75x75-000000-80-0-0.jpg'
    else:
        cover = f"https://e-cdns-images.dzcdn.net/images/cover/{trackAPI['md5_image']}/75x75-000000-80-0-0.jpg"

    if 'track_token' in trackAPI: del trackAPI['track_token']

    return Single({
        'type': 'track',
        'id': link_id,
        'bitrate': bitrate,
        'title': trackAPI['title'],
        'artist': trackAPI['artist']['name'],
        'cover': cover,
        'explicit': trackAPI['explicit_lyrics'],
        'single': {
            'trackAPI': trackAPI,
            'albumAPI': albumAPI
        }
    })

def generateAlbumItem(dz, link_id, bitrate, rootArtist=None):
    # Get essential album info
    if str(link_id).startswith('upc'):
        upcs = [link_id[4:],]
        upcs.append(int(upcs[0]))
        lastError = None
        for upc in upcs:
            try:
                albumAPI = dz.api.get_album(f"upc:{upc}")
            except APIError as e:
                lastError = e
                albumAPI = None
        if not albumAPI:
            raise GenerationError(f"https://deezer.com/album/{link_id}", str(lastError)) from lastError
        link_id = albumAPI['id']
    else:
        try:
            albumAPI_gw_page = dz.gw.get_album_page(link_id)
            if 'DATA' in albumAPI_gw_page:
                albumAPI = map_album(albumAPI_gw_page['DATA'])
                link_id = albumAPI_gw_page['DATA']['ALB_ID']
                albumAPI_new = dz.api.get_album(link_id)
                albumAPI.update(albumAPI_new)
            else:
                raise GenerationError(f"https://deezer.com/album/{link_id}", "Can't find the album")
        except APIError as e:
            raise GenerationError(f"https://deezer.com/album/{link_id}", str(e)) from e

    if not str(link_id).isdecimal(): raise InvalidID(f"https://deezer.com/album/{link_id}")

    # Get extra info about album
    # This saves extra api calls when downloading
    albumAPI_gw = dz.gw.get_album(link_id)
    albumAPI_gw = map_album(albumAPI_gw)
    albumAPI_gw.update(albumAPI)
    albumAPI = albumAPI_gw
    albumAPI['root_artist'] = rootArtist

    # If the album is a single download as a track
    if albumAPI['nb_tracks'] == 1:
        if len(albumAPI['tracks']['data']):
            return generateTrackItem(dz, albumAPI['tracks']['data'][0]['id'], bitrate, albumAPI=albumAPI)
        raise GenerationError(f"https://deezer.com/album/{link_id}", "Single has no tracks.")

    tracksArray = dz.gw.get_album_tracks(link_id)

    if albumAPI['cover_small'] is not None:
        cover = albumAPI['cover_small'][:-24] + '/75x75-000000-80-0-0.jpg'
    else:
        cover = f"https://e-cdns-images.dzcdn.net/images/cover/{albumAPI['md5_image']}/75x75-000000-80-0-0.jpg"

    totalSize = len(tracksArray)
    albumAPI['nb_tracks'] = totalSize
    collection = []
    for pos, trackAPI in enumerate(tracksArray, start=1):
        trackAPI = map_track(trackAPI)
        if 'track_token' in trackAPI: del trackAPI['track_token']
        trackAPI['position'] = pos
        collection.append(trackAPI)

    return Collection({
        'type': 'album',
        'id': link_id,
        'bitrate': bitrate,
        'title': albumAPI['title'],
        'artist': albumAPI['artist']['name'],
        'cover': cover,
        'explicit': albumAPI['explicit_lyrics'],
        'size': totalSize,
        'collection': {
            'tracks': collection,
            'albumAPI': albumAPI
        }
    })

def generatePlaylistItem(dz, link_id, bitrate, playlistAPI=None, playlistTracksAPI=None):
    if not playlistAPI:
        if not str(link_id).isdecimal(): raise InvalidID(f"https://deezer.com/playlist/{link_id}")
        # Get essential playlist info
        try:
            playlistAPI = dz.api.get_playlist(link_id)
        except APIError:
            playlistAPI = None
        # Fallback to gw api if the playlist is private
        if not playlistAPI:
            try:
                userPlaylist = dz.gw.get_playlist_page(link_id)
                playlistAPI = map_user_playlist(userPlaylist['DATA'])
            except GWAPIError as e:
                raise GenerationError(f"https://deezer.com/playlist/{link_id}", str(e)) from e

        # Check if private playlist and owner
        if not playlistAPI.get('public', False) and playlistAPI['creator']['id'] != str(dz.current_user['id']):
            logger.warning("You can't download others private playlists.")
            raise NotYourPrivatePlaylist(f"https://deezer.com/playlist/{link_id}")

    if not playlistTracksAPI:
        playlistTracksAPI = dz.gw.get_playlist_tracks(link_id)
    playlistAPI['various_artist'] = dz.api.get_artist(5080) # Useful for save as compilation

    totalSize = len(playlistTracksAPI)
    playlistAPI['nb_tracks'] = totalSize
    collection = []
    for pos, trackAPI in enumerate(playlistTracksAPI, start=1):
        trackAPI = map_track(trackAPI)
        if trackAPI['explicit_lyrics']:
            playlistAPI['explicit'] = True
        if 'track_token' in trackAPI: del trackAPI['track_token']
        trackAPI['position'] = pos
        collection.append(trackAPI)

    if 'explicit' not in playlistAPI: playlistAPI['explicit'] = False

    return Collection({
        'type': 'playlist',
        'id': link_id,
        'bitrate': bitrate,
        'title': playlistAPI['title'],
        'artist': playlistAPI['creator']['name'],
        'cover': playlistAPI['picture_small'][:-24] + '/75x75-000000-80-0-0.jpg',
        'explicit': playlistAPI['explicit'],
        'size': totalSize,
        'collection': {
            'tracks': collection,
            'playlistAPI': playlistAPI
        }
    })

def generateArtistItem(dz, link_id, bitrate, listener=None):
    if not str(link_id).isdecimal(): raise InvalidID(f"https://deezer.com/artist/{link_id}")
    # Get essential artist info
    try:
        artistAPI = dz.api.get_artist(link_id)
    except APIError as e:
        raise GenerationError(f"https://deezer.com/artist/{link_id}", str(e)) from e

    rootArtist = {
        'id': artistAPI['id'],
        'name': artistAPI['name'],
        'picture_small': artistAPI['picture_small']
    }
    if listener: listener.send("startAddingArtist", rootArtist)

    artistDiscographyAPI = dz.gw.get_artist_discography_tabs(link_id, 100)
    allReleases = artistDiscographyAPI.pop('all', [])
    albumList = []
    for album in allReleases:
        try:
            albumList.append(generateAlbumItem(dz, album['id'], bitrate, rootArtist=rootArtist))
        except GenerationError as e:
            logger.warning("Album %s has no data: %s", str(album['id']), str(e))

    if listener: listener.send("finishAddingArtist", rootArtist)
    return albumList

def generateArtistDiscographyItem(dz, link_id, bitrate, listener=None):
    if not str(link_id).isdecimal(): raise InvalidID(f"https://deezer.com/artist/{link_id}/discography")
    # Get essential artist info
    try:
        artistAPI = dz.api.get_artist(link_id)
    except APIError as e:
        raise GenerationError(f"https://deezer.com/artist/{link_id}/discography", str(e)) from e

    rootArtist = {
        'id': artistAPI['id'],
        'name': artistAPI['name'],
        'picture_small': artistAPI['picture_small']
    }
    if listener: listener.send("startAddingArtist", rootArtist)

    artistDiscographyAPI = dz.gw.get_artist_discography_tabs(link_id, 100)
    artistDiscographyAPI.pop('all', None) # all contains albums and singles, so its all duplicates. This removes them
    albumList = []
    for releaseType in artistDiscographyAPI:
        for album in artistDiscographyAPI[releaseType]:
            try:
                albumList.append(generateAlbumItem(dz, album['id'], bitrate, rootArtist=rootArtist))
            except GenerationError as e:
                logger.warning("Album %s has no data: %s", str(album['id']), str(e))

    if listener: listener.send("finishAddingArtist", rootArtist)
    return albumList

def generateArtistTopItem(dz, link_id, bitrate):
    if not str(link_id).isdecimal(): raise InvalidID(f"https://deezer.com/artist/{link_id}/top_track")
    # Get essential artist info
    try:
        artistAPI = dz.api.get_artist(link_id)
    except APIError as e:
        raise GenerationError(f"https://deezer.com/artist/{link_id}/top_track", str(e)) from e

    # Emulate the creation of a playlist
    # Can't use generatePlaylistItem directly as this is not a real playlist
    playlistAPI = {
        'id':f"{artistAPI['id']}_top_track",
        'title': f"{artistAPI['name']} - Top Tracks",
        'description': f"Top Tracks for {artistAPI['name']}",
        'duration': 0,
        'public': True,
        'is_loved_track': False,
        'collaborative': False,
        'nb_tracks': 0,
        'fans': artistAPI['nb_fan'],
        'link': f"https://www.deezer.com/artist/{artistAPI['id']}/top_track",
        'share': None,
        'picture': artistAPI['picture'],
        'picture_small': artistAPI['picture_small'],
        'picture_medium': artistAPI['picture_medium'],
        'picture_big': artistAPI['picture_big'],
        'picture_xl': artistAPI['picture_xl'],
        'checksum': None,
        'tracklist': f"https://api.deezer.com/artist/{artistAPI['id']}/top",
        'creation_date': "XXXX-00-00",
        'creator': {
            'id': f"art_{artistAPI['id']}",
            'name': artistAPI['name'],
            'type': "user"
        },
        'type': "playlist"
    }

    artistTopTracksAPI_gw = dz.gw.get_artist_toptracks(link_id)
    return generatePlaylistItem(dz, playlistAPI['id'], bitrate, playlistAPI=playlistAPI, playlistTracksAPI=artistTopTracksAPI_gw)