from google.cloud import firestore

import datetime
import logging

from spotframework.engine.playlistengine import PlaylistEngine, PlaylistSource, RecommendationSource, LibraryTrackSource
from spotframework.engine.processor.shuffle import Shuffle
from spotframework.engine.processor.sort import SortReleaseDate
from spotframework.engine.processor.deduplicate import DeduplicateByID

from spotframework.model.uri import Uri

from spotfm.engine.chart_source import ChartSource

import music.db.database as database
from music.db.part_generator import PartGenerator
from music.model.playlist import RecentsPlaylist, LastFMChartPlaylist

db = firestore.Client()

logger = logging.getLogger(__name__)


def run_user_playlist(username, playlist_name):
    user = database.get_user(username)

    logger.info(f'running {username} / {playlist_name}')

    if user:

        playlist = database.get_playlist(username=username, name=playlist_name)

        if playlist is not None:

            if playlist.uri is None:
                logger.critical(f'no playlist id to populate ({username}/{playlist_name})')
                return None

            net = database.get_authed_spotify_network(username)

            engine = PlaylistEngine(net)

            if isinstance(playlist, LastFMChartPlaylist) and user.lastfm_username is not None:
                engine.sources.append(ChartSource(spotnet=net, fmnet=database.get_authed_lastfm_network(user.username)))

            processors = [DeduplicateByID()]

            if not isinstance(playlist, LastFMChartPlaylist):
                if playlist.shuffle is True:
                    processors.append(Shuffle())
                else:
                    processors.append(SortReleaseDate(reverse=True))

            part_generator = PartGenerator(user=user)
            submit_parts = part_generator.get_recursive_parts(playlist.name)

            params = [
                PlaylistSource.Params(names=submit_parts)
            ]

            if playlist.include_recommendations:
                params.append(RecommendationSource.Params(recommendation_limit=playlist.recommendation_sample))

            if playlist.include_library_tracks:
                params.append(LibraryTrackSource.Params())

            if isinstance(playlist, LastFMChartPlaylist):
                params.append(ChartSource.Params(chart_range=playlist.chart_range, limit=playlist.chart_limit))

            if isinstance(playlist, RecentsPlaylist):
                boundary_date = datetime.datetime.now(datetime.timezone.utc) - \
                                datetime.timedelta(days=int(playlist.day_boundary))
                tracks = engine.get_recent_playlist(params=params,
                                                    processors=processors,
                                                    boundary_date=boundary_date,
                                                    add_this_month=playlist.add_this_month,
                                                    add_last_month=playlist.add_last_month)
            else:
                tracks = engine.make_playlist(params=params,
                                              processors=processors)

            engine.execute_playlist(tracks, Uri(playlist.uri))

            overwrite = playlist.description_overwrite
            suffix = playlist.description_suffix

            engine.change_description(sorted(submit_parts),
                                      uri=Uri(playlist.uri),
                                      overwrite=overwrite,
                                      suffix=suffix)

        else:
            logger.critical(f'playlist not found ({username}/{playlist_name})')
            return None

    else:
        logger.critical(f'{username} not found')