from marshmallow import Schema, fields, pre_dump, post_dump

from platypush.schemas import DateTime


class TidalSchema(Schema):
    pass


class TidalArtistSchema(TidalSchema):
    id = fields.String(
        required=True,
        dump_only=True,
        metadata={
            'example': '3288612',
            'description': 'Artist ID',
        },
    )

    url = fields.String(
        required=True,
        dump_only=True,
        metadata={
            'description': 'Artist Tidal URL',
            'example': 'https://tidal.com/artist/3288612',
        },
    )

    name = fields.String(required=True)

    @pre_dump
    def _prefill_url(self, data, *_, **__):
        data.url = f'https://tidal.com/artist/{data.id}'
        return data


class TidalAlbumSchema(TidalSchema):
    def __init__(self, *args, with_tracks=False, **kwargs):
        super().__init__(*args, **kwargs)
        self._with_tracks = with_tracks

    id = fields.String(
        required=True,
        dump_only=True,
        metadata={
            'example': '45288612',
            'description': 'Album ID',
        },
    )

    url = fields.String(
        required=True,
        dump_only=True,
        metadata={
            'description': 'Album Tidal URL',
            'example': 'https://tidal.com/album/45288612',
        },
    )

    name = fields.String(required=True)
    artist = fields.Nested(TidalArtistSchema)
    duration = fields.Int(metadata={'description': 'Album duration, in seconds'})
    year = fields.Integer(metadata={'example': 2003})
    num_tracks = fields.Int(metadata={'example': 10})
    tracks = fields.List(fields.Dict(), attribute='_tracks')

    @pre_dump
    def _prefill_url(self, data, *_, **__):
        data.url = f'https://tidal.com/album/{data.id}'
        return data

    @pre_dump
    def _cache_tracks(self, data, *_, **__):
        if self._with_tracks:
            album_id = str(data.id)
            self.context[album_id] = {
                'tracks': data.tracks(),
            }

        return data

    @post_dump
    def _dump_tracks(self, data, *_, **__):
        if self._with_tracks:
            album_id = str(data['id'])
            ctx = self.context.pop(album_id, {})
            data['tracks'] = TidalTrackSchema().dump(ctx.pop('tracks', []), many=True)

        return data


class TidalTrackSchema(TidalSchema):
    id = fields.String(
        required=True,
        dump_only=True,
        metadata={
            'example': '25288614',
            'description': 'Track ID',
        },
    )

    url = fields.String(
        required=True,
        dump_only=True,
        metadata={
            'description': 'Track Tidal URL',
            'example': 'https://tidal.com/track/25288614',
        },
    )

    artist = fields.Nested(TidalArtistSchema)
    album = fields.Nested(TidalAlbumSchema)
    name = fields.String(metadata={'description': 'Track title'})
    duration = fields.Int(metadata={'description': 'Track duration, in seconds'})
    track_num = fields.Int(
        metadata={'description': 'Index of the track within the album'}
    )

    @pre_dump
    def _prefill_url(self, data, *_, **__):
        data.url = f'https://tidal.com/track/{data.id}'
        return data


class TidalPlaylistSchema(TidalSchema):
    id = fields.String(
        required=True,
        dump_only=True,
        attribute='uuid',
        metadata={
            'example': '2b288612-34f5-11ed-b42d-001500e8f607',
            'description': 'Playlist ID',
        },
    )

    url = fields.String(
        required=True,
        dump_only=True,
        metadata={
            'description': 'Playlist Tidal URL',
            'example': 'https://tidal.com/playlist/2b288612-34f5-11ed-b42d-001500e8f607',
        },
    )

    name = fields.String(required=True)
    description = fields.String()
    duration = fields.Int(metadata={'description': 'Playlist duration, in seconds'})
    public = fields.Boolean(attribute='publicPlaylist')
    owner = fields.String(
        attribute='creator',
        metadata={
            'description': 'Playlist creator/owner ID',
        },
    )

    num_tracks = fields.Int(
        attribute='numberOfTracks',
        dump_default=0,
        metadata={
            'example': 42,
            'description': 'Number of tracks in the playlist',
        },
    )

    created_at = DateTime(
        attribute='created',
        metadata={
            'description': 'When the playlist was created',
        },
    )

    last_updated_at = DateTime(
        attribute='lastUpdated',
        metadata={
            'description': 'When the playlist was last updated',
        },
    )

    tracks = fields.Nested(TidalTrackSchema, many=True)

    def _flatten_object(self, data, *_, **__):
        if not isinstance(data, dict):
            data = {
                'created': data.created,
                'creator': data.creator.id,
                'description': data.description,
                'duration': data.duration,
                'lastUpdated': data.last_updated,
                'uuid': data.id,
                'name': data.name,
                'numberOfTracks': data.num_tracks,
                'publicPlaylist': data.public,
                'tracks': getattr(data, '_tracks', []),
            }

        return data

    def _normalize_owner(self, data, *_, **__):
        owner = data.pop('owner', data.pop('creator', None))
        if owner:
            if isinstance(owner, dict):
                owner = owner['id']
            data['creator'] = owner

        return data

    def _normalize_name(self, data, *_, **__):
        if data.get('title'):
            data['name'] = data.pop('title')
        return data

    @pre_dump
    def normalize(self, data, *_, **__):
        if not isinstance(data, dict):
            data = self._flatten_object(data)

        self._normalize_name(data)
        self._normalize_owner(data)
        if 'tracks' not in data:
            data['tracks'] = []
        return data


class TidalSearchResultsSchema(TidalSchema):
    artists = fields.Nested(TidalArtistSchema, many=True)
    albums = fields.Nested(TidalAlbumSchema, many=True)
    tracks = fields.Nested(TidalTrackSchema, many=True)
    playlists = fields.Nested(TidalPlaylistSchema, many=True)
