from datetime import datetime

from django.contrib import messages
from django.contrib.auth.mixins import PermissionRequiredMixin
from django.contrib.messages.views import SuccessMessageMixin
from django.db.models import F, Q
from django.http import HttpResponseRedirect
from django.shortcuts import get_object_or_404
from django.urls import reverse_lazy
from django.utils.translation import gettext_lazy as _
from django.views.generic import ListView, UpdateView, CreateView, DeleteView
from django.contrib.gis.measure import D
from django.utils.safestring import mark_safe

from .utils import get_event_qs
from ..forms import PlaceForm, EventAddPlaceForm
from ..models import Place, Event
from ..utils import PlaceGuesser


class PlaceListView(ListView):
    model = Place
    ordering = ["name__unaccent"]


class PlaceListAdminView(PermissionRequiredMixin, ListView):
    model = Place
    paginate_by = 10
    permission_required = "agenda_culturel.add_place"
    ordering = ["name__unaccent"]
    template_name = "agenda_culturel/place_list_admin.html"


class PlaceDetailView(ListView):
    model = Place
    template_name = "agenda_culturel/place_detail.html"
    paginate_by = 10

    def get_queryset(self):
        self.place = get_object_or_404(Place, pk=self.kwargs["pk"])
        return (
            get_event_qs(self.request)
            .filter(exact_location=self.place)
            .filter(
                Q(other_versions__isnull=True)
                | Q(other_versions__representative=F("pk"))
                | Q(other_versions__representative__isnull=True)
            )
            .filter(start_day__gte=datetime.now())
            .order_by("start_day")
        )

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context["object"] = self.place
        context["place_list"] = Place.objects.filter(
            ~Q(pk=self.place.pk)
            & Q(location__distance_lte=(self.place.location, D(m=4000)))
        ).only("location", "name", "pk")
        return context


class PlaceDetailViewPast(PlaceDetailView):
    def get_queryset(self):
        self.place = get_object_or_404(Place, pk=self.kwargs["pk"])
        self.past = True
        return (
            get_event_qs(self.request)
            .filter(exact_location=self.place)
            .filter(
                Q(other_versions__isnull=True)
                | Q(other_versions__representative=F("pk"))
                | Q(other_versions__representative__isnull=True)
            )
            .filter(start_day__lte=datetime.now())
            .order_by("-start_day")
        )

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context["past"] = self.past
        return context


class UpdatePlaces:
    def form_valid(self, form):
        result = super().form_valid(form)
        p = form.instance

        if not hasattr(self, "nb_applied"):
            self.nb_applied = 0

        # if required, find all matching events
        if form.apply():
            self.nb_applied += p.associate_matching_events()

        if self.nb_applied > 1:
            messages.success(
                self.request,
                _("{} events have been updated.").format(self.nb_applied),
            )
        elif self.nb_applied == 1:
            messages.success(self.request, _("1 event has been updated."))
        else:
            messages.info(self.request, _("No events have been modified."))
        return result


class PlaceUpdateView(
    UpdatePlaces, PermissionRequiredMixin, SuccessMessageMixin, UpdateView
):
    model = Place
    permission_required = "agenda_culturel.change_place"
    success_message = _("The place has been successfully updated.")
    form_class = PlaceForm


class PlaceCreateView(
    UpdatePlaces, PermissionRequiredMixin, SuccessMessageMixin, CreateView
):
    model = Place
    permission_required = "agenda_culturel.add_place"
    success_message = _("The place has been successfully created.")
    form_class = PlaceForm

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context["place_list"] = Place.objects.all().only("location", "name", "pk")
        return context


class PlaceDeleteView(PermissionRequiredMixin, DeleteView):
    model = Place
    permission_required = "agenda_culturel.delete_place"
    success_url = reverse_lazy("view_places_admin")


class UnknownPlacesListView(PermissionRequiredMixin, ListView):
    model = Event
    permission_required = "agenda_culturel.add_place"
    paginate_by = 10
    ordering = ["-pk"]
    template_name = "agenda_culturel/place_unknown_list.html"
    queryset = Event.get_qs_events_with_unkwnon_place()


def fix_unknown_places(request):
    # get all places
    places = Place.objects.all()
    # get all events without exact location
    u_events = Event.get_qs_events_with_unkwnon_place()

    to_be_updated = []
    # try to find matches
    for ue in u_events:
        for p in places:
            if p.match(ue):
                ue.exact_location = p
                to_be_updated.append(ue)
                continue
    # update events with a location
    Event.objects.bulk_update(to_be_updated, fields=["exact_location"])

    # create a success message
    nb = len(to_be_updated)
    if nb > 1:
        messages.success(request, _("{} events have been updated.").format(nb))
    elif nb == 1:
        messages.success(request, _("1 event has been updated."))
    else:
        messages.info(request, _("No events have been modified."))

    # come back to the list of places
    return HttpResponseRedirect(reverse_lazy("view_unknown_places"))


class UnknownPlaceAddView(PermissionRequiredMixin, SuccessMessageMixin, UpdateView):
    model = Event
    permission_required = (
        "agenda_culturel.change_place",
        "agenda_culturel.change_event",
    )
    form_class = EventAddPlaceForm
    template_name = "agenda_culturel/event_set_place_form.html"

    def form_valid(self, form):
        self.modified_event = form.cleaned_data.get("place")
        self.add_alias = form.cleaned_data.get("add_alias")
        result = super().form_valid(form)

        if form.cleaned_data.get("place"):
            messages.success(
                self.request,
                mark_safe(
                    _(
                        'The selected place has been assigned to the event <a href="{}">{}</a>.'
                    ).format(self.object.get_absolute_url(), str(self.object))
                ),
            )
            if form.cleaned_data.get("add_alias"):
                messages.success(
                    self.request,
                    _("A new alias has been added to the selected place."),
                )

                nb_applied = form.cleaned_data.get("place").associate_matching_events()

                if nb_applied > 1:
                    messages.success(
                        self.request,
                        _("{} events have been updated.").format(nb_applied),
                    )
                elif nb_applied == 1:
                    messages.success(self.request, _("1 event has been updated."))
                else:
                    messages.info(self.request, _("No events have been modified."))

        return result

    def get_success_url(self):
        if self.modified_event:
            return reverse_lazy("view_unknown_places")
        else:
            param = "?add=1" if self.add_alias else ""
            return reverse_lazy("add_place_from_event", args=[self.object.pk]) + param


class PlaceFromEventCreateView(PlaceCreateView):
    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context["event"] = self.event
        return context

    def get_initial(self, *args, **kwargs):
        initial = super().get_initial(**kwargs)
        self.event = get_object_or_404(Event, pk=self.kwargs["pk"])
        if self.event.location and "add" in self.request.GET:
            initial["aliases"] = [self.event.location]
            guesser = PlaceGuesser()
            name, address, postcode, city = guesser.guess_address_elements(
                self.event.location
            )
            initial["name"] = name
            initial["address"] = address
            initial["postcode"] = postcode
            initial["city"] = city
            initial["location"] = ""

        return initial

    def form_valid(self, form):
        result = super().form_valid(form)
        self.event.exact_location = form.instance
        self.event.save(update_fields=["exact_location"])
        return result

    def get_success_url(self):
        return self.event.get_absolute_url()
