import logging
from datetime import datetime, timedelta

import pylast
import pytz
from django.conf import settings
from music.models import Track
from scrobbles.constants import MediaType

logger = logging.getLogger(__name__)

PYLAST_ERRORS = tuple(
    getattr(pylast, exc_name)
    for exc_name in (
        "ScrobblingError",
        "NetworkError",
        "MalformedResponseError",
        "WSError",
    )
    if hasattr(pylast, exc_name)
)


class LastFM:
    def __init__(self, user):
        try:
            self.client = pylast.LastFMNetwork(
                api_key=getattr(settings, "LASTFM_API_KEY"),
                api_secret=getattr(settings, "LASTFM_SECRET_KEY"),
                username=user.profile.lastfm_username,
                password_hash=pylast.md5(user.profile.lastfm_password),
            )
            self.user = self.client.get_user(user.profile.lastfm_username)
            self.vrobbler_user = user
        except PYLAST_ERRORS as e:
            logger.error(f"Error during Last.fm setup: {e}")

    def import_from_lastfm(self, last_processed=None):
        """Given a last processed time, import all scrobbles from LastFM since then"""
        from scrobbles.models import Scrobble

        new_scrobbles = []
        source = "Last.fm"
        lastfm_scrobbles = self.get_last_scrobbles(time_from=last_processed)

        for lfm_scrobble in lastfm_scrobbles:
            track = Track.find_or_create(
                title=lfm_scrobble.get("title"),
                artist_name=lfm_scrobble.get("artist"),
                album_name=lfm_scrobble.get("album"),
                enrich=True,
            )

            tz_timestamp = self.vrobbler_user.profile.get_timestamp_with_tz(
                lfm_scrobble.get("timestamp")
            )
            timestamp = lfm_scrobble.get("timestamp")
            stop_timestamp = timestamp + timedelta(
                seconds=track.run_time_seconds
            )
            new_scrobble = Scrobble(
                user=self.vrobbler_user,
                timestamp=timestamp,
                stop_timestamp=stop_timestamp,
                source=source,
                track=track,
                played_to_completion=True,
                in_progress=False,
                media_type=MediaType.TRACK,
                timezone=tz_timestamp.tzinfo.name,
            )
            # Vrobbler scrobbles on finish, LastFM scrobbles on start
            seconds_eariler = timestamp - timedelta(seconds=20)
            seconds_later = timestamp + timedelta(seconds=20)
            existing = Scrobble.objects.filter(
                created__gte=seconds_eariler,
                created__lte=seconds_later,
                track=track,
            ).first()
            if existing:
                logger.debug(f"Skipping existing scrobble {new_scrobble}")
                continue
            logger.debug(f"Queued scrobble {new_scrobble} for creation")
            new_scrobbles.append(new_scrobble)

        created = Scrobble.objects.bulk_create(new_scrobbles)
        # TODO Add a notification for users that their import is complete
        logger.info(
            f"Last.fm import fnished",
            extra={
                "scrobbles_created": len(created),
                "user_id": self.vrobbler_user,
                "lastfm_user": self.user,
            },
        )
        return created

    def get_last_scrobbles(self, time_from=None, time_to=None, check=False):
        """Given a user, Last.fm api key, and secret key, grab a list of scrobbled
        tracks"""
        lfm_params = {}
        scrobbles = []
        if time_from:
            lfm_params["time_from"] = int(time_from.timestamp())
        if time_to:
            lfm_params["time_to"] = int(time_to.timestamp())

        # if not time_from and not time_to:
        lfm_params["limit"] = None

        found_scrobbles = self.user.get_recent_tracks(**lfm_params)
        # TOOD spin this out into a celery task over certain threshold of found scrobbles?

        if check and found_scrobbles:
            return True

        for scrobble in found_scrobbles:
            logger.info(f"Processing {scrobble}")
            run_time = None
            mbid = None
            artist = None

            log_dict = {"scrobble": scrobble}
            try:
                run_time = int(scrobble.track.get_duration() / 1000)
                mbid = scrobble.track.get_mbid()
                artist = scrobble.track.get_artist().name
                log_dict["artist"] = artist
                log_dict["mbid"] = mbid
                log_dict["run_time"] = run_time
            except pylast.MalformedResponseError as e:
                logger.warning(e)
            except pylast.WSError as e:
                logger.info(
                    "LastFM barfed trying to get the track for {scrobble.track}",
                    extra=log_dict,
                )
            except pylast.NetworkError as e:
                logger.info(
                    "LastFM barfed trying to get the track for {scrobble.track}",
                    extra=log_dict,
                )

            if not artist:
                logger.info(
                    f"Silly LastFM, no artist found for scrobble",
                    extra=log_dict,
                )
                continue

            # TODO figure out if this will actually work
            # timestamp = datetime.fromtimestamp(int(scrobble.timestamp), UTC)
            timestamp = datetime.utcfromtimestamp(
                int(scrobble.timestamp)
            ).replace(tzinfo=pytz.utc)

            logger.info(
                f"Scrobble appended to list for bulk create", extra=log_dict
            )
            scrobbles.append(
                {
                    "artist": artist,
                    "album": scrobble.album,
                    "title": scrobble.track.title,
                    "mbid": mbid,
                    "run_time_seconds": run_time,
                    "timestamp": timestamp,
                }
            )
        return scrobbles
