import datetime

from django.utils import timezone as tz
from django.utils.decorators import method_decorator
from django.views.decorators.cache import cache_page
from django.views.generic import ListView
from rest_framework.generics import ListAPIView

from ..models import Diffusion, Log
from ..serializers import LogInfo, LogInfoSerializer
from .base import BaseAPIView, BaseView
from .mixins import AttachedToMixin, GetDateMixin

__all__ = ("LogListMixin", "LogListView", "LogListAPIView")


class LogListMixin(GetDateMixin):
    model = Log
    min_date = None
    max_date = None

    def get_date(self, param):
        date = super().get_date(param)
        if date is not None and not self.request.user.is_staff:
            return min(date, datetime.date.today())
        return date

    def filter_qs(self, query):
        if self.min_date:
            query = query.after(self.min_date)
        if self.max_date:
            query = query.before(self.max_date)
        if not self.min_date and not self.max_date and self.date:
            return query.date(self.date)
        return query

    def get_queryset(self):
        # only get logs for tracks: log for diffusion will be retrieved
        # by the diffusions' queryset.
        query = super().get_queryset().on_air().filter(track__isnull=False).filter(date__lte=tz.now())
        return self.filter_qs(query)

    def get_diffusions_queryset(self):
        query = Diffusion.objects.station(self.station).on_air().filter(start__lte=tz.now()).before()
        return self.filter_qs(query)

    def get_object_list(self, logs, full=False):
        """Return diffusions merged to the provided logs iterable.

        If `full`, sort items by date without merging.
        """
        diffs = self.get_diffusions_queryset()
        if self.request.user.is_staff and full:
            return sorted(list(logs) + list(diffs), key=lambda obj: obj.start)
        return Log.merge_diffusions(logs, diffs)


class LogListView(AttachedToMixin, BaseView, LogListMixin, ListView):
    """Return list of logs for the provided date (from `kwargs` or
    `request.GET`, defaults to today)."""

    redirect_date_url = "log-list"
    date_delta = tz.timedelta(days=7)

    def get_date(self, param):
        date = super().get_date(param)
        return datetime.date.today() if date is None else date

    def get(self, request, **kwargs):
        min_date = self.get_date("min_date")
        max_date = self.get_date("max_date")

        # ensure right values for min and max
        min_date, max_date = min(min_date, max_date), max(min_date, max_date)

        # limit logs list size using date delta
        if min_date and max_date:
            max_date = min(min_date + self.date_delta, max_date)
        elif min_date:
            max_date = min_date + self.date_delta
        elif max_date:
            min_date = max_date - self.date_delta

        self.min_date = min_date
        self.max_date = max_date
        return super().get(request, **kwargs)

    def get_context_data(self, **kwargs):
        today = datetime.date.today()
        # `super()...` must be called before updating kwargs, in order
        # to get `self.object_list`
        kwargs = super().get_context_data(**kwargs)
        kwargs.update(
            {
                "min_date": self.min_date or today,
                "max_date": self.max_date or today,
                "today": datetime.date.today(),
                "date": self.date,
                "dates": (today - datetime.timedelta(days=i) for i in range(0, 7)),
                "object_list": self.get_object_list(self.object_list),
            }
        )
        return kwargs


# Logs are accessible through API only with this list view
class LogListAPIView(LogListMixin, BaseAPIView, ListAPIView):
    """Return logs list, including diffusions. By default return logs of the
    last 30 minutes.

    Available GET parameters:
    - "date": return logs for a specified date (
    - "full": (staff user only) don't merge diffusion and logs
    """

    serializer_class = LogInfoSerializer
    queryset = Log.objects.all()

    @method_decorator(cache_page(5))
    def list(self, *args, **kwargs):
        return super().list(*args, **kwargs)

    def get_date(self, param):
        date = super().get_date(param)
        if date is None:
            self.min_date = tz.now() - tz.timedelta(minutes=30)
        return date

    def get_object_list(self, logs, *args, **kwargs):
        return [LogInfo(obj) for obj in super().get_object_list(logs, *args, **kwargs)]

    def get_serializer(self, queryset, *args, **kwargs):
        full = bool(self.request.GET.get("full"))
        return super().get_serializer(self.get_object_list(queryset, full), *args, **kwargs)
