import logging

from django.core.cache import cache
from django.db.models.signals import post_delete, post_save
from django.dispatch import receiver
from django.utils import timezone

from scrobbles.models import FavoriteMedia, Scrobble
from scrobbles.tasks import (
    add_favorite_to_mopidy_playlist,
    CHARTABLE_MEDIA_TYPES,
    remove_favorite_from_mopidy_playlist,
    reverse_geocode_geolocation,
    SCROBBLES_WITHOUT_CHARTS,
    update_charts_for_timestamp,
)
from scrobbles.utils import tokenize_title_to_tags

logger = logging.getLogger(__name__)


@receiver(post_save, sender=Scrobble)
def update_charts_on_scrobble_complete(sender, instance, **kwargs):
    if not instance.played_to_completion:
        return

    if instance.timestamp is None:
        return

    if instance.media_type.lower() in SCROBBLES_WITHOUT_CHARTS:
        return

    if instance.media_type.lower() not in CHARTABLE_MEDIA_TYPES:
        return

    _update_charts_for_timestamp(instance.user, instance.timestamp)


@receiver(post_delete, sender=Scrobble)
def update_charts_on_scrobble_delete(sender, instance, **kwargs):
    if instance.timestamp is None:
        return

    if instance.media_type.lower() in SCROBBLES_WITHOUT_CHARTS:
        return

    if instance.media_type.lower() not in CHARTABLE_MEDIA_TYPES:
        return

    _update_charts_for_timestamp(instance.user, instance.timestamp)


def _update_charts_for_timestamp(user, ts):
    if ts is None:
        return

    lock_key = f"chart_update_{user.id}"
    if not cache.add(lock_key, "locked", timeout=30):
        logger.info(f"Chart update already queued for user {user.id}, skipping")
        return

    if timezone.is_naive(ts):
        ts = timezone.make_aware(ts)

    year = ts.year
    month = ts.month
    week = ts.isocalendar()[1]
    day = ts.day

    update_charts_for_timestamp.delay(user.id, year, month, day, week)


@receiver(post_save, sender=Scrobble)
def add_tags_from_task_title(sender, instance, **kwargs):
    if instance.media_type != Scrobble.MediaType.TASK:
        return

    title = instance.logdata.title
    if not title:
        return

    new_tags = tokenize_title_to_tags(title)
    existing_tags = {t.name for t in instance.tags.all()}

    for tag in new_tags:
        if tag not in existing_tags:
            instance.tags.add(tag)


@receiver(post_save, sender=Scrobble)
def reverse_geocode_on_scrobble_creation(sender, instance, created, **kwargs):
    if not created:
        return
    if not instance.geo_location_id:
        logger.info(
            "Skipping reverse geocode: scrobble %s has no geo_location",
            instance.id,
        )
        return
    if instance.geo_location.postal_code:
        logger.info(
            "Skipping reverse geocode: geo_location %s already has postal_code %s",
            instance.geo_location_id,
            instance.geo_location.postal_code,
        )
        return

    logger.info(
        "Enqueuing reverse geocode for geo_location %s",
        instance.geo_location_id,
    )
    reverse_geocode_geolocation.delay(instance.geo_location_id)


@receiver(post_save, sender=FavoriteMedia)
def add_to_mopidy_playlist_on_favorite(sender, instance, created, **kwargs):
    if not created:
        return
    if instance.media_type != Scrobble.MediaType.TRACK:
        return
    if not instance.track:
        return

    add_favorite_to_mopidy_playlist.delay(instance.id)


@receiver(post_delete, sender=FavoriteMedia)
def remove_from_mopidy_playlist_on_unfavorite(sender, instance, **kwargs):
    if instance.media_type != Scrobble.MediaType.TRACK:
        return
    if not instance.track_id:
        return

    remove_favorite_from_mopidy_playlist.delay(
        user_id=instance.user_id,
        track_id=instance.track_id,
    )
