import calendar as _calendar
from datetime import date, timedelta, datetime

from django.contrib import messages
from django.contrib.auth.decorators import login_required, permission_required
from django.contrib.auth.mixins import LoginRequiredMixin, PermissionRequiredMixin
from django.contrib.messages.views import SuccessMessageMixin
from django.core.cache import cache
from django.db.models import Count, F, Subquery, OuterRef
from django.db.models import Q, Min, Max, Avg, StdDev
from django.db.models.functions import TruncMonth, ExtractDay
from django.http import HttpResponseRedirect
from django.shortcuts import render
from django.urls import reverse_lazy
from django.utils.translation import gettext_lazy as _
from django.views.generic import CreateView, UpdateView

from . import week_view
from .utils import Median
from ..forms import UserProfileForm
from ..models import (
    RecurrentImport,
    Event,
    StaticContent,
    BatchImportation,
    UserProfile,
)


def home(request, cat=None):
    return week_view(request, home=True, cat=cat)


def thank_you(request):
    return render(request, "agenda_culturel/thank_you.html")


def mentions_legales(request):
    context = {
        "title": "Mentions légales",
        "static_content": "mentions_legales",
        "url_path": reverse_lazy("mentions_legales"),
    }
    return render(request, "agenda_culturel/page-single.html", context)


def about(request):
    rimports = (
        RecurrentImport.objects.filter(~Q(recurrence=RecurrentImport.RECURRENCE.NEVER))
        .order_by("name__unaccent")
        .all()
    )
    context = {
        "title": "À propos",
        "static_content": "about",
        "url_path": reverse_lazy("about"),
        "rimports": rimports,
    }
    return render(request, "agenda_culturel/page-rimports-list.html", context)


def moderation_rules(request):
    context = {
        "title": _("Moderation rules"),
        "static_content": "moderation_rules",
        "url_path": reverse_lazy("moderation_rules"),
    }
    return render(request, "agenda_culturel/page-single.html", context)


def import_requirements(request):
    context = {
        "title": _("Import requirements"),
        "static_content": "import_requirements",
        "url_path": reverse_lazy("import_requirements"),
    }
    return render(request, "agenda_culturel/page-single.html", context)


def statistics(request, pk=None):
    if pk is not None:
        rimport = RecurrentImport.objects.filter(pk=pk)
        source = rimport.values("source").first()["source"]
        qs = Event.objects.filter(import_sources__contains=[source])
    else:
        rimport = None
        qs = Event.objects

    stats = {}
    stats_months = {}
    first = {}
    last = {}

    ev_published = qs.filter(
        Q(status=Event.STATUS.PUBLISHED)
        & (
            Q(other_versions__isnull=True)
            | Q(other_versions__representative=F("pk"))
            | Q(other_versions__representative__isnull=True)
        )
    )

    for v in ["start_day", "created_date__date"]:
        after = 24
        last[v] = (
            date.today()
            if v == "created_date__date"
            else date.today() + timedelta(weeks=after)
        )
        last[v] = last[v].replace(
            day=_calendar.monthrange(last[v].year, last[v].month)[1]
        )

        r = 8 * 30
        if v == "start_day":
            r += after * 7
        first[v] = (last[v] - timedelta(days=r)).replace(day=1)

        ev_days = ev_published.annotate(day=F(v)).filter(
            Q(day__lte=last[v]) & Q(day__gte=first[v])
        )

        stats[v] = ev_days.values("day").annotate(total=Count("day")).order_by("day")

        stats_months[v] = (
            ev_days.annotate(month=TruncMonth("day"))
            .values("month")
            .annotate(total=Count("month"))
            .order_by("month")
        )

    nb_by_city = (
        ev_published.annotate(city=F("exact_location__city"))
        .filter(city__isnull=False)
        .values("city")
        .annotate(total=Count("city"))
        .order_by("-total")
    )

    limit = datetime.now() + timedelta(days=-30)

    stat_qs = qs.filter(start_day__gte=F("created_date")).annotate(
        foresight=ExtractDay(F("start_day") - F("created_date"))
    )

    statsa = stat_qs.filter().aggregate(
        minimum=Min("foresight"),
        maximum=Max("foresight"),
        mean=Avg("foresight"),
        median=Median("foresight"),
        stdev=StdDev("foresight"),
    )

    statsm = stat_qs.filter(created_date__gte=limit).aggregate(
        minimum=Min("foresight"),
        maximum=Max("foresight"),
        mean=Avg("foresight"),
        median=Median("foresight"),
        stdev=StdDev("foresight"),
    )

    stats_foresight = [
        [
            _(x),
            round(statsa[x], 2) if statsa[x] is not None else "-",
            round(statsm[x], 2) if statsm[x] is not None else "-",
        ]
        for x in statsa
    ]

    context = {
        "stats_by_startday": stats["start_day"],
        "stats_by_creation": stats["created_date__date"],
        "stats_months_by_startday": stats_months["start_day"],
        "stats_months_by_creation": stats_months["created_date__date"],
        "first_by_startday": first["start_day"],
        "last_by_startday": last["start_day"],
        "first_by_creation": first["created_date__date"],
        "last_by_creation": last["created_date__date"],
        "nb_by_city": nb_by_city,
        "stats_foresight": stats_foresight,
        "object": rimport.first() if rimport else None,
    }

    if pk is None:
        return render(request, "agenda_culturel/statistics.html", context)
    else:
        return render(request, "agenda_culturel/rimport-statistics.html", context)


@login_required(login_url="/accounts/login/")
@permission_required("agenda_culturel.view_event")
def activite(request):
    now = date.today()

    days = [now]
    while len(days) < 7 or days[-1].weekday() != 0:
        days.append(days[-1] + timedelta(days=-1))

    weeks = [days[-1]]
    for w in range(0, 8):
        weeks.append(weeks[-1] + timedelta(days=-7))

    daily_modifications = Event.get_count_modifications([(d, 1) for d in days])
    weekly_modifications = Event.get_count_modifications([(w, 7) for w in weeks])

    return render(
        request,
        "agenda_culturel/page-activity.html",
        {
            "daily_modifications": daily_modifications,
            "weekly_modifications": weekly_modifications,
        },
    )


class StaticContentCreateView(LoginRequiredMixin, CreateView):
    model = StaticContent
    fields = ["text"]
    permission_required = "agenda_culturel.add_staticcontent"

    def form_valid(self, form):
        form.instance.name = self.request.GET["name"]
        form.instance.url_path = self.request.GET["url_path"]
        return super().form_valid(form)


class StaticContentUpdateView(
    SuccessMessageMixin,
    PermissionRequiredMixin,
    LoginRequiredMixin,
    UpdateView,
):
    model = StaticContent
    permission_required = "agenda_culturel.change_staticcontent"
    fields = ["text"]
    success_message = _("The static content has been successfully updated.")


def clear_cache(request):
    if request.method == "POST":
        cache.clear()
        messages.success(request, _("Cache successfully cleared."))
        return HttpResponseRedirect(reverse_lazy("administration"))
    else:
        return render(
            request,
            "agenda_culturel/clear_cache.html",
        )


@login_required(login_url="/accounts/login/")
@permission_required("agenda_culturel.view_event")
def administration(request):
    nb_mod_days = 21
    nb_classes = 4
    today = date.today()

    # get information about recent modifications
    days = [today]
    for i in range(0, 2):
        days.append(days[-1] + timedelta(days=-1))
    daily_modifications = Event.get_count_modifications([(d, 1) for d in days])

    # get last created events
    events = (
        Event.objects.all()
        .order_by("-created_date")
        .select_related("exact_location", "category")[:5]
    )

    # get last batch imports
    rel_event = Event.objects.filter(
        import_sources__contains=[OuterRef("url_source")]
    ).values("pk")[:1]
    batch_imports = (
        BatchImportation.objects.all()
        .select_related("recurrentImport")
        .annotate(event_id=Subquery(rel_event))
        .order_by("-created_date")[:5]
    )

    # get info about batch information
    newest = (
        BatchImportation.objects.filter(recurrentImport=OuterRef("pk"))
        .order_by("-created_date")
        .select_related("recurrentImport")
    )
    imported_events = RecurrentImport.objects.annotate(
        last_run_status=Subquery(newest.values("status")[:1])
    )

    nb_failed = imported_events.filter(
        last_run_status=BatchImportation.STATUS.FAILED
    ).count()
    nb_canceled = imported_events.filter(
        last_run_status=BatchImportation.STATUS.CANCELED
    ).count()
    nb_running = imported_events.filter(
        last_run_status=BatchImportation.STATUS.RUNNING
    ).count()
    nb_all = imported_events.count()

    # get some info about imported (or not) events
    srcs = RecurrentImport.objects.all().values_list("source")
    in_future = Event.objects.filter(Q(start_day__gte=today))
    nb_in_rimport = in_future.filter(Q(import_sources__overlap=srcs)).count()
    nb_in_orphan_import = in_future.filter(
        (
            Q(import_sources__isnull=False)
            & (Q(modified_date__isnull=True) | Q(modified_date__lte=F("imported_date")))
        )
        & ~Q(import_sources__overlap=srcs)
    ).count()

    # get all non moderated events
    nb_not_moderated = Event.get_nb_not_moderated(today, nb_mod_days, nb_classes)

    return render(
        request,
        "agenda_culturel/administration.html",
        {
            "daily_modifications": daily_modifications,
            "events": events,
            "batch_imports": batch_imports,
            "nb_failed": nb_failed,
            "nb_canceled": nb_canceled,
            "nb_running": nb_running,
            "nb_all": nb_all,
            "nb_not_moderated": nb_not_moderated,
            "nb_in_rimport": nb_in_rimport,
            "nb_in_orphan_import": nb_in_orphan_import,
        },
    )


class UserProfileUpdateView(
    SuccessMessageMixin,
    LoginRequiredMixin,
    UpdateView,
):
    model = UserProfile
    success_message = _("Your user profile has been successfully modified.")
    success_url = reverse_lazy("administration")
    form_class = UserProfileForm

    def get_object(self):
        return self.request.user.userprofile
