#    Copyright (C) 2019  Alban Gruin
#
#    celcatsanitizer is free software: you can redistribute it and/or modify
#    it under the terms of the GNU Affero General Public License as published
#    by the Free Software Foundation, either version 3 of the License, or
#    (at your option) any later version.
#
#    celcatsanitizer is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU Affero General Public License for more details.
#
#    You should have received a copy of the GNU Affero General Public License
#    along with celcatsanitizer.  If not, see <http://www.gnu.org/licenses/>.

import datetime

from django.db.models import Count
from django.db.models.functions import ExtractWeek, ExtractYear
from django.utils import timezone

from rest_framework import viewsets
from rest_framework.decorators import action, detail_route
from rest_framework.response import Response

from ..forms import QSJPSForm
from ..models import Course, Group, Room, Source, Timetable, Year
from ..utils import get_current_or_next_week, get_week

from .serializers import CourseSerializer, GroupSerializer, RoomSerializer, \
    SourceSerializer, TimetableSerializer, YearSerializer


class YearViewSet(viewsets.ReadOnlyModelViewSet):
    queryset = Year.objects.all().order_by("name")
    serializer_class = YearSerializer

    @detail_route(methods=["get"], url_path="timetables")
    def timetable_list(self, request, pk):
        year = self.get_object()
        timetables = Timetable.objects.filter(year=year).distinct() \
                                                        .order_by("name")
        timetables_json = TimetableSerializer(
            self.paginate_queryset(timetables), many=True)
        return self.get_paginated_response(timetables_json.data)


class SourceViewSet(viewsets.ReadOnlyModelViewSet):
    queryset = Source.objects.all().order_by("pk")
    serializer_class = SourceSerializer

    @detail_route(methods=["get"], url_path="timetables")
    def timetable_list(self, request, pk):
        source = self.get_object()
        timetables = Timetable.objects.filter(source=source).distinct() \
                                                            .order_by("name")
        timetables_json = TimetableSerializer(
            self.paginate_queryset(timetables), many=True)
        return self.get_paginated_response(timetables_json.data)


class TimetableViewSet(viewsets.ReadOnlyModelViewSet):
    queryset = Timetable.objects.all().select_related("source") \
                                      .order_by("year", "name")
    serializer_class = TimetableSerializer

    @detail_route(methods=["get"], url_path="groups")
    def group_list(self, request, pk):
        timetable = self.get_object()
        groups = Group.objects.filter(source=timetable.source).distinct() \
                                                              .order_by("name")
        groups_json = GroupSerializer(self.paginate_queryset(groups),
                                      many=True)
        return self.get_paginated_response(groups_json.data)


class CourseListGroupSet(viewsets.ReadOnlyModelViewSet):
    @detail_route(methods=["get"], url_path="courses")
    def course_list(self, request, pk):
        obj = self.get_object()
        courses = Course.objects.get_courses(obj).prefetch_related("groups")
        courses_json = CourseSerializer(self.paginate_queryset(courses),
                                        many=True)
        return self.get_paginated_response(courses_json.data)

    @detail_route(methods=["get"], url_path="courses/weeks/current")
    def current_week(self, request, pk):
        obj = self.get_object()
        start, end = get_week(*get_current_or_next_week())

        courses = Course.objects.get_courses(obj,
                                             begin__gte=start, end__lt=end) \
                                .prefetch_related("groups")
        courses_json = CourseSerializer(self.paginate_queryset(courses),
                                        many=True)
        return self.get_paginated_response(courses_json.data)

    @detail_route(methods=["get"],
                  url_path="courses/weeks/(?P<year>\d+)/(?P<week>\d+)")
    def other_week(self, request, pk, year, week):
        obj = self.get_object()

        errors = {}
        if not year.isdigit():
            errors["year"] = "Rentrez une année valide"
        if not week.isdigit() or not 0 < int(week) <= 53:
            errors["week"] = "Rentrez une semaine valide"
        if errors:
            return Response(errors, status=400)

        start, end = get_week(int(year), int(week))

        courses = Course.objects.get_courses(obj,
                                             begin__gte=start, end__lt=end) \
                                .prefetch_related("groups")
        courses_json = CourseSerializer(self.paginate_queryset(courses),
                                        many=True)
        return self.get_paginated_response(courses_json.data)


class GroupViewSet(CourseListGroupSet):
    queryset = Group.objects.all().order_by("name")
    serializer_class = GroupSerializer

    @detail_route(methods=["get"], url_path="courses/weeks")
    def weeks(self, request, pk):
        group = self.get_object()
        groups = Group.objects.get_parents(group)

        courses = Course.objects.filter(groups__in=groups) \
                                .order_by("year", "week") \
                                .annotate(year=ExtractYear("begin"),
                                          week=ExtractWeek("begin")) \
                                .values("year", "week") \
                                .annotate(c=Count("*"))

        weeks = [get_week(course["year"], course["week"])[0]
                 for course in courses]

        return Response(weeks)


class RoomViewSet(CourseListGroupSet):
    queryset = Room.objects.all().order_by("name")
    serializer_class = RoomSerializer

    @action(
        methods=["get"],
        detail=False,
        url_path="qsjps/(?P<day>[0-9\-]+)/(?P<begin>[0-9:]+)/(?P<end>[0-9:]+)")
    def qsjps(self, request, day, begin, end):
        form = QSJPSForm({"day": day, "begin": begin, "end": end})
        if not form.is_valid():
            return Response(form.errors, status=400)

        day = form.cleaned_data["day"]
        begin_hour = form.cleaned_data["begin"]
        end_hour = form.cleaned_data["end"]

        begin = timezone.make_aware(datetime.datetime.combine(day, begin_hour))
        end = timezone.make_aware(datetime.datetime.combine(day, end_hour))

        rooms = Room.objects.qsjps(begin, end)
        rooms_json = RoomSerializer(rooms, many=True)
        return Response(rooms_json.data)


class CourseViewSet(viewsets.ReadOnlyModelViewSet):
    queryset = Course.objects.all().prefetch_related("groups", "rooms")
    serializer_class = CourseSerializer