from collections import OrderedDict
from datetime import date, datetime, timedelta
from typing import Optional

from django.contrib.auth.decorators import login_required
from django.db.models import Count, Max, Min
from django.http import HttpRequest, HttpResponse
from django.shortcuts import get_object_or_404, redirect, render
from django.urls import reverse
from django.utils.translation import ugettext as _

from django_tables2 import RequestConfig

from aleksis.core.decorators import admin_required
from aleksis.core.models import Person, Group
from aleksis.core.util import messages

from .forms import LessonSubstitutionForm
from .models import LessonPeriod, LessonSubstitution, TimePeriod, Room
from .tables import LessonsTable, SubstitutionsTable
from .util import CalendarWeek


@login_required
def all(request: HttpRequest) -> HttpResponse:
    context = {}

    teachers = Person.objects.annotate(lessons_count=Count("lessons_as_teacher")).filter(lessons_count__gt=0)
    classes = Group.objects.annotate(lessons_count=Count("lessons")).filter(lessons_count__gt=0, parent_groups=None)
    rooms = Room.objects.annotate(lessons_count=Count("lesson_periods")).filter(lessons_count__gt=0)

    context['teachers'] = teachers
    context['classes'] = classes
    context['rooms'] = rooms

    return render(request, 'chronos/quicklaunch.html', context)


@login_required
def timetable(
    request: HttpRequest, _type: str, pk: int, year: Optional[int] = None, week: Optional[int] = None
) -> HttpResponse:
    context = {}

    if year and week:
        wanted_week = CalendarWeek(year=year, week=week)
    else:
        wanted_week = CalendarWeek()

    lesson_periods = LessonPeriod.objects.in_week(wanted_week)

    # Incrementally filter lesson periods by GET parameters
    # if (
    #     request.GET.get("group", None)
    #     or request.GET.get("teacher", None)
    #     or request.GET.get("room", None)
    # ):

    lesson_periods = lesson_periods.filter_from_type(_type, pk)
    # else:
    #     # Redirect to a selected view if no filter provided
    #     if request.user.person:
    #         if request.user.person.primary_group:
    #             return redirect(
    #                 reverse("timetable") + "?group=%d" % request.user.person.primary_group.pk
    #             )
    #         elif lesson_periods.filter(lesson__teachers=request.user.person).exists():
    #             return redirect(reverse("timetable") + "?teacher=%d" % request.user.person.pk)

    # Regroup lesson periods per weekday
    per_period = {}
    for lesson_period in lesson_periods:
        print(lesson_period.period)
        added = False
        if lesson_period.period.period in per_period :
            if lesson_period.period.weekday in per_period[lesson_period.period.period]:
                print("HEY HEY")
                print(per_period[lesson_period.period.period][lesson_period.period.weekday])
                per_period[lesson_period.period.period][lesson_period.period.weekday].append(lesson_period)
                added =True

        if not added:
            per_period.setdefault(lesson_period.period.period, {})[
                lesson_period.period.weekday
            ] = [lesson_period]

    print(per_period)
    # Determine overall first and last day and period
    min_max = TimePeriod.objects.aggregate(
        Min("period"), Max("period"), Min("weekday"), Max("weekday")
    )

    period_min = min_max.get("period__min", 1)
    period_max = min_max.get("period__max", 7)

    weekday_min = min_max.get("weekday__min", 0)
    weekday_max = min_max.get("weekday__max", 6)

    # Fill in empty lessons
    for period_num in range(period_min, period_max + 1):
        print(period_num)
        # Fill in empty weekdays
        if period_num not in per_period.keys():
            per_period[period_num] = {}

        # Fill in empty lessons on this workday
        for weekday_num in range(weekday_min, weekday_max + 1):
            if weekday_num not in per_period[period_num].keys():
                per_period[period_num][weekday_num] = []

        # Order this weekday by periods
        per_period[period_num] = OrderedDict(sorted(per_period[period_num].items()))

    print(lesson_periods)
    context["lesson_periods"] = OrderedDict(sorted(per_period.items()))
    context["periods"] = TimePeriod.get_times_dict()
    context["weekdays"] = dict(TimePeriod.WEEKDAY_CHOICES[weekday_min:weekday_max + 1])
    context["weekdays_short"] = dict(TimePeriod.WEEKDAY_CHOICES_SHORT[weekday_min:weekday_max + 1])
    context["week"] = wanted_week
    context["type"] = _type
    context["pk"] = pk

    week_prev = wanted_week - 1
    week_next = wanted_week + 1
    context["url_prev"] = "%s?%s" % (
        reverse("timetable_by_week", args=[_type, pk, week_prev.year, week_prev.week]),
        request.GET.urlencode(),
    )
    context["url_next"] = "%s?%s" % (
        reverse("timetable_by_week", args=[_type, pk, week_next.year, week_next.week]),
        request.GET.urlencode(),
    )

    return render(request, "chronos/plan.html", context)


@login_required
def lessons_day(request: HttpRequest, when: Optional[str] = None) -> HttpResponse:
    context = {}

    if when:
        day = datetime.strptime(when, "%Y-%m-%d").date()
    else:
        day = date.today()

    # Get lessons
    lesson_periods = LessonPeriod.objects.on_day(day)

    # Build table
    lessons_table = LessonsTable(lesson_periods.all())
    RequestConfig(request).configure(lessons_table)

    context["current_head"] = _("Lessons %s") % (day)
    context["lessons_table"] = lessons_table
    context["day"] = day
    context["lesson_periods"] = lesson_periods

    day_prev = day - timedelta(days=1)
    day_next = day + timedelta(days=1)
    context["url_prev"] = reverse("lessons_day_by_date", args=[day_prev.strftime("%Y-%m-%d")])
    context["url_next"] = reverse("lessons_day_by_date", args=[day_next.strftime("%Y-%m-%d")])

    return render(request, "chronos/lessons_day.html", context)


@admin_required
def edit_substitution(request: HttpRequest, id_: int, week: int) -> HttpResponse:
    context = {}

    lesson_period = get_object_or_404(LessonPeriod, pk=id_)
    wanted_week = lesson_period.lesson.get_calendar_week(week)

    lesson_substitution = LessonSubstitution.objects.filter(
        week=wanted_week.week, lesson_period=lesson_period
    ).first()
    if lesson_substitution:
        edit_substitution_form = LessonSubstitutionForm(
            request.POST or None, instance=lesson_substitution
        )
    else:
        edit_substitution_form = LessonSubstitutionForm(
            request.POST or None,
            initial={"week": wanted_week.week, "lesson_period": lesson_period},
        )

    context["substitution"] = lesson_substitution

    if request.method == "POST":
        if edit_substitution_form.is_valid():
            edit_substitution_form.save(commit=True)

            messages.success(request, _("The substitution has been saved."))
            return redirect(
                "lessons_day_by_date",
                when=wanted_week[lesson_period.period.weekday - 1].strftime("%Y-%m-%d"),
            )

    context["edit_substitution_form"] = edit_substitution_form

    return render(request, "chronos/edit_substitution.html", context)


@admin_required
def delete_substitution(request: HttpRequest, id_: int, week: int) -> HttpResponse:
    context = {}

    lesson_period = get_object_or_404(LessonPeriod, pk=id_)
    wanted_week = lesson_period.lesson.get_calendar_week(week)

    LessonSubstitution.objects.filter(week=wanted_week.week, lesson_period=lesson_period).delete()

    messages.success(request, _("The substitution has been deleted."))
    return redirect(
        "lessons_day_by_date",
        when=wanted_week[lesson_period.period.weekday - 1].strftime("%Y-%m-%d"),
    )


def substitutions(
    request: HttpRequest, year: Optional[int] = None, week: Optional[int] = None
) -> HttpResponse:
    context = {}

    if week:
        wanted_week = CalendarWeek(year=year, week=week)
    else:
        wanted_week = CalendarWeek()

    substitutions = LessonSubstitution.objects.filter(week=wanted_week.week)

    # Prepare table
    substitutions_table = SubstitutionsTable(substitutions)
    RequestConfig(request).configure(substitutions_table)

    context["current_head"] = str(wanted_week)
    context["substitutions_table"] = substitutions_table
    week_prev = wanted_week - 1
    week_next = wanted_week + 1
    context["url_prev"] = "%s" % (
        reverse("substitutions_by_week", args=[week_prev.year, week_prev.week])
    )
    context["url_next"] = "%s" % (
        reverse("substitutions_by_week", args=[week_next.year, week_next.week])
    )

    return render(request, "chronos/substitutions.html", context)