from datetime import date

from django.contrib import messages
from django.contrib.auth.decorators import login_required, permission_required
from django.contrib.auth.mixins import (
    LoginRequiredMixin,
    PermissionRequiredMixin,
    UserPassesTestMixin,
)
from django.contrib.messages.views import SuccessMessageMixin
from django.core.paginator import EmptyPage, PageNotAnInteger
from django.http import (
    HttpResponseForbidden,
    HttpResponseRedirect,
)
from django.shortcuts import get_object_or_404, render
from django.urls import reverse, reverse_lazy
from django.utils.html import escape
from django.utils.safestring import mark_safe
from django.utils.translation import gettext_lazy as _
from django.views.generic import DetailView
from django.views.generic.edit import (
    CreateView,
    DeleteView,
    ModelFormMixin,
    UpdateView,
)
from django.http import Http404
from urllib.parse import quote

from .utils import PaginatorFilter
from ..celery import (
    import_events_from_url,
    import_events_from_urls,
)
from ..filters import (
    EventFilterAdmin,
    SimpleSearchEventFilter,
)
from ..forms import (
    EventForm,
    EventFormWithContact,
    MessageEventForm,
    SimpleContactForm,
    URLSubmissionFormSet,
    URLSubmissionFormWithContact,
    URLSubmissionSimpleForm,
)
from ..import_tasks.extractor import Extractor
from ..models import (
    DuplicatedEvents,
    Event,
    Message,
)
from .utils import get_event_qs


def update_from_source(request, pk):
    event = get_object_or_404(Event, pk=pk)

    url = event.get_updateable_uuid()
    if url is None:
        messages.warning(
            request,
            _(
                "The event cannot be updated because the import process is not available for the referenced sources."
            ),
        )
    else:
        import_events_from_url.delay(
            url,
            None,
            None,
            True,
            user_id=request.user.pk if request.user else None,
        )
        messages.success(
            request,
            _("The event update has been queued and will be completed shortly."),
        )

    return HttpResponseRedirect(event.get_absolute_url())


class EventUpdateView(
    SuccessMessageMixin,
    PermissionRequiredMixin,
    LoginRequiredMixin,
    UpdateView,
):
    model = Event
    permission_required = "agenda_culturel.change_event"
    form_class = EventForm

    def get_form_kwargs(self):
        kwargs = super().get_form_kwargs()
        kwargs["is_authenticated"] = self.request.user.is_authenticated
        kwargs["is_moderation_expert"] = (
            self.request.user.userprofile.is_moderation_expert
        )
        kwargs["is_cloning"] = self.is_cloning
        kwargs["is_edit_from_moderation"] = self.is_edit_force()
        kwargs["is_simple_cloning"] = self.is_simple_cloning
        return kwargs

    def get_success_message(self, cleaned_data):
        txt = (
            _(" A message has been sent to the person who proposed the event.")
            if hasattr(self, "with_msg") and self.with_msg
            else ""
        )
        return mark_safe(_("The event has been successfully modified.") + txt)

    def is_edit_force(self):
        return "edit-force" in self.request.path.split("/")

    def get_object(self, queryset=None):
        event = super().get_object(queryset)
        if event.status == Event.STATUS.DRAFT:
            event.status = Event.STATUS.PUBLISHED
        if self.is_edit_force():
            event.free_modification_lock(self.request.user)
        return event

    def form_valid(self, form):
        form.instance.set_processing_user(self.request.user)
        self.with_message = form.instance.notify_if_required(self.request)
        return super().form_valid(form)

    def get_initial(self):
        self.is_cloning = "clone" in self.request.path.split("/")
        if self.is_cloning:
            messages.info(
                self.request,
                _(
                    "Changes will be visible on a local copy of the event. The version identical to the imported source will be hidden."
                ),
            )
        self.is_simple_cloning = "simple-clone" in self.request.path.split("/")
        result = super().get_initial()

        if self.is_cloning and "other_versions" not in result:
            obj = self.get_object()
            # if no DuplicatedEvents is associated, create one
            obj.other_versions = DuplicatedEvents.objects.create()
            obj.other_versions.save()
            # save them without updating modified date
            obj.set_no_modification_date_changed()
            obj.save()
            result["other_versions"] = obj.other_versions
            result["status"] = Event.STATUS.PUBLISHED
            result["cloning"] = True

        if self.is_simple_cloning:
            result["other_versions"] = None
            result["simple_cloning"] = True

        if self.is_cloning or self.is_simple_cloning:
            obj = self.get_object()
            if obj.local_image:
                result["old_local_image"] = obj.local_image.name

        return result

    def get_success_url(self):
        if "save_and_next" in self.request.POST:
            return reverse_lazy("moderate_event_next", args=[self.object.pk])
        else:
            return self.object.get_absolute_url()


class EventDeleteView(
    SuccessMessageMixin,
    PermissionRequiredMixin,
    LoginRequiredMixin,
    DeleteView,
):
    model = Event
    permission_required = "agenda_culturel.delete_event"
    success_url = reverse_lazy("recent")
    success_message = _("The event has been successfully deleted.")


class EventDetailView(UserPassesTestMixin, DetailView, ModelFormMixin):
    model = Event
    form_class = MessageEventForm
    template_name = "agenda_culturel/page-event.html"
    queryset = (
        Event.objects.select_related("exact_location")
        .select_related("category")
        .select_related("other_versions")
        .select_related("other_versions__representative")
        .prefetch_related("message_set")
    )

    def test_func(self):
        return (
            self.request.user.is_authenticated
            or self.get_object().status == Event.STATUS.PUBLISHED
        )

    def get_object(self):
        o = super().get_object()
        o.download_missing_image()
        if "year" in self.kwargs:
            y = self.kwargs["year"]
            m = self.kwargs["month"]
            d = self.kwargs["day"]
            obj = o.get_recurrence_at_date(y, m, d)
            obj.set_current_date(date(y, m, d))
            return obj
        else:
            return o

    def get(self, request, *args, **kwargs):
        try:
            self.object = self.get_object()
        except Http404 as e:
            if "year" in self.kwargs:
                y = self.kwargs["year"]
                m = self.kwargs["month"]
                d = self.kwargs["day"]
                day = date(y, m, d)
                s = self.kwargs["extra"].replace("-", " ")
                qs = get_event_qs(request)
                request.GET._mutable = True
                request.GET["q"] = s
                filter = SimpleSearchEventFilter(
                    request.GET,
                    queryset=qs,
                    request=request,
                )
                context = {
                    "alternative_events": Event.alternative_events_if_not_found(day, s),
                    "filter": filter,
                }
                return render(
                    request, "page-event-not-found.html", status=404, context=context
                )
            else:
                raise e
        context = self.get_context_data(object=self.object)
        return self.render_to_response(context)

    def get_success_url(self):
        return self.get_object().get_absolute_url() + "#chronology"

    def post(self, request, *args, **kwargs):
        if not request.user.is_authenticated:
            return HttpResponseForbidden()
        form = self.get_form()
        if form.is_valid():
            return self.form_valid(form)
        else:
            return self.form_invalid(form)

    def form_valid(self, form):
        message = form.save(commit=False)
        message.user = self.request.user
        message.related_event = self.get_object()
        message.subject = _("Comment")
        message.spam = False
        message.closed = True
        message.save()

        return super().form_valid(form)


@login_required(login_url="/accounts/login/")
@permission_required("agenda_culturel.change_event")
def change_status_event(request, pk, status):
    event = get_object_or_404(Event, pk=pk)

    if request.method == "POST":
        event.status = Event.STATUS(status)
        fields = ["status", "moderated_date", "moderated_by_user"]
        event.set_in_moderation_process()
        event.update_modification_dates()
        event.save(update_fields=fields)
        with_msg = event.notify_if_required(request)
        if with_msg:
            messages.success(
                request,
                _(
                    "The status has been successfully modified and a message has been sent to the person who proposed the event."
                ),
            )
        else:
            messages.success(request, _("The status has been successfully modified."))

        return HttpResponseRedirect(event.get_absolute_url())

    else:
        cancel_url = request.META.get("HTTP_REFERER", "")
        if cancel_url == "":
            cancel_url = reverse_lazy("home")
        return render(
            request,
            "agenda_culturel/event_confirm_change_status.html",
            {"status": status, "event": event, "cancel_url": cancel_url},
        )


def import_event_proxy(request):
    if request.method == "POST":
        form = URLSubmissionSimpleForm(request.POST, request.FILES)
        if form.is_valid():
            url = form.cleaned_data["url"]
            if Extractor.is_known_url_default_extractors(url):
                c_url = Extractor.clean_url_default_extractors(url)

                ex = Event.is_know_url_get_visible_event(
                    c_url, request.user.is_authenticated
                )
                if ex is True:
                    messages.info(
                        request,
                        _(
                            "{} has not been submitted since it"
                            "s already known and currently into moderation process."
                        ).format(c_url),
                    )
                    return HttpResponseRedirect(reverse_lazy("home"))
                elif isinstance(ex, Event):
                    messages.info(
                        request,
                        mark_safe(
                            _(
                                "{} has not been submitted since it"
                                "s already known: {}."
                            ).format(
                                c_url,
                                '<a href="'
                                + ex.get_absolute_url()
                                + '">'
                                + escape(ex.title)
                                + "</a>",
                            )
                        ),
                    )
                    return HttpResponseRedirect(ex.get_absolute_url())
                else:
                    messages.success(
                        request,
                        _(
                            "This type of address is known to the calendar, so an automatic import is proposed."
                        ),
                    )
                    return HttpResponseRedirect(
                        reverse_lazy("add_event_url") + "?url=" + quote(c_url)
                    )
            else:
                manual = reverse_lazy("add_event_details") + "?url=" + quote(url)
                if request.user.is_authenticated:
                    messages.info(
                        request,
                        mark_safe(
                            _(
                                'This type of address is unknown to the agenda. As a logged-in user, you can still try to import the event, which may work if the page contains a google calendar link, for example. When in doubt, opt for <a href="{}">manual entry</a>.'
                            ).format(manual)
                        ),
                    )
                    return HttpResponseRedirect(
                        reverse_lazy("add_event_url") + "?url=" + quote(url)
                    )
                else:
                    messages.info(
                        request,
                        _(
                            "This type of address is unknown to the calendar, so we suggest that you enter the information for the event you wish to import, in addition to the link you've provided."
                        ),
                    )
                    return HttpResponseRedirect(manual)
    else:
        form = URLSubmissionSimpleForm()
    return render(request, "agenda_culturel/event_import.html", {"form": form})


class EventCreateView(SuccessMessageMixin, CreateView):
    model = Event
    form_class = EventFormWithContact

    def get_form_kwargs(self):
        kwargs = super().get_form_kwargs()
        kwargs["is_authenticated"] = self.request.user.is_authenticated
        return kwargs

    def get_initial(self):
        initial = super().get_initial()
        initial["reference_urls"] = [self.request.GET.get("url", "")]
        return initial

    def get_success_url(self):
        if self.request.user.is_authenticated:
            if "save_and_next" in self.request.POST:
                return reverse_lazy("moderate_event_next", args=[self.object.pk])
            else:
                return self.object.get_absolute_url()
        else:
            return reverse_lazy("home")

    def get_success_message(self, cleaned_data):
        if self.request.user.is_authenticated:
            return mark_safe(
                _('The event was created: <a href="{}">{}</a>.').format(
                    self.object.get_absolute_url(), self.object.title
                )
            )
        else:
            return _(
                "The event has been submitted and will be published as soon as it has been validated by the moderation team."
            )

    def form_valid(self, form):
        if form.cleaned_data["simple_cloning"]:
            form.instance.set_skip_duplicate_check()

        if form.cleaned_data["cloning"]:
            form.instance.set_in_moderation_process()

        if form.cleaned_data.get("email") or form.cleaned_data.get("comments"):
            has_comments = form.cleaned_data.get("comments") not in ["", None]
            form.instance.add_message(
                Message(
                    subject=_("during the creation process"),
                    message=form.cleaned_data.get("comments"),
                    email=form.cleaned_data.get("email"),
                    closed=False,
                    message_type=(
                        Message.TYPE.FROM_CONTRIBUTOR
                        if has_comments
                        else Message.TYPE.FROM_CONTRIBUTOR_NO_MSG
                    ),
                )
            )

        form.instance.import_sources = None
        form.instance.set_processing_user(self.request.user)

        result = super().form_valid(form)

        if form.cleaned_data["cloning"]:
            with_msg = form.instance.notify_if_required(self.request)
            if with_msg:
                messages.success(
                    self.request,
                    _(
                        "A message has been sent to the person who proposed the initial event."
                    ),
                )

        return result


# A class to evaluate the URL according to the existing events and the authentification
# level of the user
class URLEventEvaluation:
    def __init__(self, form, is_authenticated):
        self.form = form
        self.is_authenticated = is_authenticated

        self.cat = None
        self.tags = []
        self.existing = None
        self.url = form.cleaned_data.get("url")
        self.event = None
        if self.url is not None:
            self.url = Extractor.clean_url_default_extractors(self.url)
            print(self.url)
            # we check if the url is known
            self.existing = Event.is_know_url_get_visible_event(
                self.url, is_authenticated
            )
            if self.existing is False:
                self.cat = form.cleaned_data.get("category")
                if self.cat is not None:
                    self.cat = self.cat.name
                self.tags = form.cleaned_data.get("tags")

    def exists(self):
        return self.url is not None

    def is_new(self):
        return self.exists() and self.existing is False

    def is_event_visible(self):
        return isinstance(self.existing, Event)

    def get_event(self):
        if isinstance(self.existing, Event):
            return self.existing
        else:
            return None

    def get_link(self):
        e = self.get_event()
        if e is None:
            return ""
        else:
            return '<a href="' + e.get_absolute_url() + '">' + escape(e.title) + "</a>"

    def to_list(self):
        if self.is_new():
            return (self.url, self.cat, self.tags)


def import_from_urls(request):
    if request.method == "POST":
        formset = URLSubmissionFormSet(request.POST, request.FILES)

        if not request.user.is_authenticated:
            contactform = SimpleContactForm(request.POST)
        if formset.is_valid() and (
            request.user.is_authenticated or contactform.is_valid()
        ):
            # evaluate all the forms
            ucat = [
                URLEventEvaluation(form, request.user.is_authenticated)
                for form in formset.forms
            ]

            # for each not new, add a message
            for uc in ucat:
                if uc.exists() and not uc.is_new():
                    if uc.is_event_visible():
                        messages.info(
                            request,
                            mark_safe(
                                _(
                                    "{} has not been submitted since it"
                                    "s already known: {}."
                                ).format(uc.url, uc.get_link())
                            ),
                        )
                    else:
                        messages.info(
                            request,
                            _(
                                "{} has not been submitted since it"
                                "s already known and currently into moderation process."
                            ).format(uc.url),
                        )

            # keep only new ones
            ucat = [uc.to_list() for uc in ucat if uc.is_new()]

            # finally process them or go back to home page
            if len(ucat) > 0:
                messages.info(
                    request,
                    _("Integrating {} url(s) into our import process.").format(
                        len(ucat)
                    ),
                )
                email = None
                comments = None
                if not request.user.is_authenticated:
                    email = contactform.cleaned_data["email"]
                    comments = contactform.cleaned_data["comments"]
                import_events_from_urls.delay(
                    ucat,
                    user_id=request.user.pk if request.user else None,
                    email=email,
                    comments=comments,
                )
                return HttpResponseRedirect(reverse("thank_you"))
            else:
                return HttpResponseRedirect(reverse("home"))

    else:
        formset = URLSubmissionFormSet()
        if not request.user.is_authenticated:
            contactform = SimpleContactForm()

    context = {"formset": formset}
    if not request.user.is_authenticated:
        context["contactform"] = contactform
    return render(request, "agenda_culturel/import_set.html", context=context)


def import_from_url(request):

    # if the form has been sent
    if request.method == "POST":
        form = URLSubmissionFormWithContact(
            request.POST, is_authenticated=request.user.is_authenticated
        )

        # if the form is valid
        if form.is_valid():
            uc = URLEventEvaluation(form, request.user.is_authenticated)
            print(uc.exists(), uc.is_new(), uc.event)
            if uc.exists() and not uc.is_new():
                if uc.is_event_visible():
                    messages.info(
                        request,
                        mark_safe(
                            _(
                                "{} has not been submitted since its already known: {}."
                            ).format(uc.url, uc.get_link())
                        ),
                    )
                    return HttpResponseRedirect(uc.get_event().get_absolute_url())
                else:
                    messages.info(
                        request,
                        _(
                            "{} has not been submitted since it"
                            "s already known and currently into moderation process."
                        ).format(uc.url),
                    )
                    return HttpResponseRedirect(reverse("home"))

            else:
                messages.info(
                    request,
                    _("Integrating {} into our import process.").format(uc.url),
                )
                import_events_from_url.delay(
                    uc.url,
                    uc.cat,
                    uc.tags,
                    user_id=request.user.pk if request.user else None,
                    email=form.cleaned_data.get("email"),
                    comments=form.cleaned_data.get("comments"),
                )
                return HttpResponseRedirect(reverse("thank_you"))
    else:
        url = request.GET.get("url", "") if request.method == "GET" else ""
        initial = {"url": url}
        form = URLSubmissionFormWithContact(
            is_authenticated=request.user.is_authenticated, initial=initial
        )

    return render(
        request,
        "agenda_culturel/import.html",
        context={"form": form},
    )


@login_required(login_url="/accounts/login/")
@permission_required("agenda_culturel.view_event")
def recent(request):
    filter = EventFilterAdmin(
        request.GET, queryset=Event.objects.all().order_by("-created_date")
    )
    paginator = PaginatorFilter(filter, 10, request)
    page = request.GET.get("page")

    try:
        response = paginator.page(page)
    except PageNotAnInteger:
        response = paginator.page(1)
    except EmptyPage:
        response = paginator.page(paginator.num_pages)

    return render(
        request,
        "agenda_culturel/recent.html",
        {"filter": filter, "paginator_filter": response},
    )
