from datetime import timedelta

from django.conf import settings
from django.shortcuts import redirect
from django.urls import reverse_lazy
from django.utils.datetime_safe import datetime
from django.views.generic import ListView, DetailView

from AKModel.models import AKSlot, Room, AKTrack
from AKModel.metaviews.admin import FilterByEventSlugMixin


class PlanIndexView(FilterByEventSlugMixin, ListView):
    """
    Default plan view

    Shows two lists of current and upcoming AKs and a graphical full plan below
    """
    model = AKSlot
    template_name = "AKPlan/plan_index.html"
    context_object_name = "akslots"
    ordering = "start"

    def get_queryset(self):
        # Ignore slots not scheduled yet
        return super().get_queryset().filter(start__isnull=False).select_related('ak', 'room', 'ak__category')

    def get_context_data(self, *, object_list=None, **kwargs):
        context = super().get_context_data(object_list=object_list, **kwargs)

        context["event"] = self.event

        current_timestamp = datetime.now().astimezone(self.event.timezone)

        context["akslots_now"] = []
        context["akslots_next"] = []
        rooms = set()
        buildings = set()

        # Get list of current and next slots
        for akslot in context["akslots"]:
            # Construct a list of all rooms used by these slots on the fly
            if akslot.room is not None:
                rooms.add(akslot.room)
                # Store buildings for hierarchical view
                if akslot.room.location != '':
                    buildings.add(akslot.room.location)

            # Recent AKs: Started but not ended yet
            if akslot.start <= current_timestamp <= akslot.end:
                context["akslots_now"].append(akslot)
            # Next AKs: Not started yet, list will be filled in order until threshold is reached
            elif akslot.start > current_timestamp:
                if len(context["akslots_next"]) < settings.PLAN_MAX_NEXT_AKS:
                    context["akslots_next"].append(akslot)

        # Sort list of rooms by title
        context["rooms"] = sorted(rooms, key=lambda x: x.title)
        if settings.PLAN_SHOW_HIERARCHY:
            context["buildings"] = sorted(buildings)

        context["tracks"] = self.event.aktrack_set.all()

        return context


class PlanScreenView(PlanIndexView):
    """
    Plan view optimized for screens and projectors

    This again shows current and upcoming AKs as well as a graphical plan,
    but no navigation elements and trys to use the available space as best as possible
    such that no scrolling is needed.

    The view contains a frontend functionality for auto-reload.
    """
    template_name = "AKPlan/plan_wall.html"

    def get(self, request, *args, **kwargs):
        s = super().get(request, *args, **kwargs)
        # Don't show wall when event is not active -> redirect to normal schedule
        if not self.event.active or (self.event.plan_hidden and not request.user.is_staff):
            return redirect(reverse_lazy("plan:plan_overview", kwargs={"event_slug": self.event.slug}))
        return s

    def get_queryset(self):
        now = datetime.now().astimezone(self.event.timezone)
        # Wall during event: Adjust, show only parts in the future
        if self.event.start < now < self.event.end:
            # Determine interesting range (some hours ago until some hours in the future as specified in the settings)
            self.start = now - timedelta(hours=settings.PLAN_WALL_HOURS_RETROSPECT)
        else:
            self.start = self.event.start
        self.end = self.event.end

        # Restrict AK slots to relevant ones
        # This will automatically filter all rooms not needed for the selected range in the orginal get_context method
        akslots = super().get_queryset().filter(start__gt=self.start)

        # Find the earliest hour AKs start and end (handle 00:00 as 24:00)
        self.earliest_start_hour = 23
        self.latest_end_hour = 1
        for akslot in akslots.all():
            start_hour = akslot.start.astimezone(self.event.timezone).hour
            if start_hour < self.earliest_start_hour:
                # Use hour - 1 to improve visibility of date change
                self.earliest_start_hour = max(start_hour - 1, 0)
            end_hour = akslot.end.astimezone(self.event.timezone).hour
            # Special case: AK starts before but ends after midnight -- show until midnight
            if end_hour < start_hour:
                self.latest_end_hour = 24
            elif end_hour > self.latest_end_hour:
                # Always use hour + 1, since AK may end at :xy and not always at :00
                self.latest_end_hour = min(end_hour + 1, 24)
        return akslots

    def get_context_data(self, *, object_list=None, **kwargs):
        context = super().get_context_data(object_list=object_list, **kwargs)
        context["start"] = self.start
        context["end"] = self.event.end
        context["earliest_start_hour"] = self.earliest_start_hour
        context["latest_end_hour"] = self.latest_end_hour
        return context


class PlanRoomView(FilterByEventSlugMixin, DetailView):
    """
    Plan view for a single room
    """
    template_name = "AKPlan/plan_room.html"
    model = Room
    context_object_name = "room"

    def get_context_data(self, *, object_list=None, **kwargs):
        context = super().get_context_data(object_list=object_list, **kwargs)
        # Restrict AKSlot list to the given room
        # while joining AK, room and category information to reduce the amount of necessary SQL queries
        context["slots"] = AKSlot.objects.filter(room=context['room']).select_related('ak', 'ak__category', 'ak__track')
        return context


class PlanTrackView(FilterByEventSlugMixin, DetailView):
    """
    Plan view for a single track
    """
    template_name = "AKPlan/plan_track.html"
    model = AKTrack
    context_object_name = "track"

    def get_context_data(self, *, object_list=None, **kwargs):
        context = super().get_context_data(object_list=object_list, **kwargs)
        # Restrict AKSlot list to given track
        # while joining AK, room and category information to reduce the amount of necessary SQL queries
        context["slots"] = AKSlot.objects.\
            filter(event=self.event, ak__track=context['track']).\
            select_related('ak', 'room', 'ak__category')
        return context