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
from django.core.paginator import PageNotAnInteger, EmptyPage
from django.http import HttpResponseRedirect
from django.shortcuts import get_object_or_404, render
from django.urls import reverse_lazy
from django.utils.translation import gettext_lazy as _
from django.views.generic import DetailView, UpdateView

from ..calendar import CalendarDay
from ..filters import DuplicatedEventsFilter
from ..forms import MergeDuplicates, FixDuplicates, SelectEventInList
from ..models import DuplicatedEvents, Event
from .utils import PaginatorFilter


class DuplicatedEventsDetailView(LoginRequiredMixin, DetailView):
    model = DuplicatedEvents
    template_name = "agenda_culturel/duplicate.html"


@login_required(login_url="/accounts/login/")
@permission_required(
    ["agenda_culturel.change_event", "agenda_culturel.change_duplicatedevents"]
)
def update_duplicate_event(request, pk, epk):
    edup = get_object_or_404(DuplicatedEvents, pk=pk)
    event = get_object_or_404(Event, pk=epk)

    form = MergeDuplicates(duplicates=edup, event=event)

    if request.method == "POST":
        form = MergeDuplicates(request.POST, duplicates=edup)
        if form.is_valid():
            for f in edup.get_items_comparison():
                if not f["similar"]:
                    selected = form.get_selected_events(f["key"])
                    if selected is not None:
                        if isinstance(selected, list):
                            values = [
                                x
                                for x in [getattr(s, f["key"]) for s in selected]
                                if x is not None
                            ]
                            if len(values) != 0:
                                if isinstance(values[0], str):
                                    setattr(event, f["key"], "\n".join(values))
                                elif (
                                    values[0].__class__.__name__ == "ManyRelatedManager"
                                ):
                                    rels = set()
                                    for v in values:
                                        rels.update(v.all())
                                    getattr(event, f["key"]).set(rels)
                                else:
                                    setattr(event, f["key"], sum(values, []))
                        else:
                            setattr(
                                event,
                                f["key"],
                                getattr(selected, f["key"]),
                            )
                            if f["key"] == "image":
                                setattr(
                                    event,
                                    "local_image",
                                    getattr(selected, "local_image"),
                                )

            event.other_versions.fix(event)
            event.save()

            messages.info(request, _("Update successfully completed."))
            return HttpResponseRedirect(event.get_absolute_url())

    return render(
        request,
        "agenda_culturel/update_duplicate.html",
        context={
            "form": form,
            "object": edup,
            "event_id": edup.get_event_index(event),
        },
    )


@login_required(login_url="/accounts/login/")
@permission_required(
    ["agenda_culturel.change_event", "agenda_culturel.change_duplicatedevents"]
)
def merge_duplicate(request, pk):
    edup = get_object_or_404(DuplicatedEvents, pk=pk)
    form = MergeDuplicates(duplicates=edup)

    if request.method == "POST":
        form = MergeDuplicates(request.POST, duplicates=edup)
        if form.is_valid():
            events = edup.get_duplicated()

            # build fields for the new event
            new_event_data = {}
            for f in edup.get_items_comparison():
                if f["similar"]:
                    new_event_data[f["key"]] = getattr(events[0], f["key"])
                else:
                    selected = form.get_selected_events(f["key"])
                    if selected is None:
                        new_event_data[f["key"]] = None
                    elif isinstance(selected, list):
                        values = [
                            x
                            for x in [getattr(s, f["key"]) for s in selected]
                            if x is not None
                        ]
                        if len(values) == 0:
                            new_event_data[f["key"]] = None
                        else:
                            if isinstance(values[0], str):
                                new_event_data[f["key"]] = "\n".join(values)
                            elif values[0].__class__.__name__ == "ManyRelatedManager":
                                new_event_data[f["key"]] = values[0]
                                rels = set()
                                for v in values:
                                    rels.update(v.all())
                                new_event_data[f["key"]].set(rels)
                            else:
                                new_event_data[f["key"]] = sum(values, [])
                    else:
                        new_event_data[f["key"]] = getattr(selected, f["key"])
                        if f["key"] == "image" and "local_image" not in new_event_data:
                            new_event_data["local_image"] = getattr(
                                selected, "local_image"
                            )

            organisers = new_event_data.pop("organisers", None)
            # create a new event that merge the selected events
            new_event = Event(**new_event_data)
            new_event.status = Event.STATUS.PUBLISHED
            new_event.other_versions = edup
            new_event.save()
            if organisers is not None:
                new_event.organisers.set(organisers.all())
            edup.fix(new_event)

            messages.info(
                request,
                _("Creation of a merged event has been successfully completed."),
            )
            return HttpResponseRedirect(new_event.get_absolute_url())

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


@login_required(login_url="/accounts/login/")
@permission_required(
    ["agenda_culturel.change_event", "agenda_culturel.change_duplicatedevents"]
)
def fix_duplicate(request, pk):
    edup = get_object_or_404(DuplicatedEvents.objects.select_related(), pk=pk)

    if request.method == "POST":
        form = FixDuplicates(request.POST, edup=edup)

        if form.is_valid():
            if form.is_action_no_duplicates():
                # all events are different
                events = edup.get_duplicated()

                # get redirection date
                if len(events) == 0:
                    date = None
                else:
                    s_events = [e for e in events if not e.has_recurrences()]
                    if len(s_events) != 0:
                        s_event = s_events[0]
                    else:
                        s_event = events[0]
                    date = s_event.start_day

                messages.success(request, _("Events have been marked as unduplicated."))
                # delete the duplicated event (other_versions will be set to None on all events)
                edup.delete()
                if date is None:
                    return HttpResponseRedirect(reverse_lazy("home"))
                else:
                    return HttpResponseRedirect(
                        reverse_lazy("day_view", args=[date.year, date.month, date.day])
                    )

            elif form.is_action_select():
                # one element has been selected to be the representative
                selected = form.get_selected_event(edup)
                if selected is None:
                    messages.error(
                        request,
                        _(
                            "The selected item is no longer included in the list of duplicates. Someone else has probably modified the list in the meantime."
                        ),
                    )
                else:
                    edup.fix(selected)
                    messages.success(
                        request,
                        _("The selected event has been set as representative"),
                    )
                return HttpResponseRedirect(edup.get_absolute_url())
            elif form.is_action_remove():
                # one element is removed from the set
                event = form.get_selected_event(edup)
                if event is None:
                    messages.error(
                        request,
                        _(
                            "The selected item is no longer included in the list of duplicates. Someone else has probably modified the list in the meantime."
                        ),
                    )
                    return HttpResponseRedirect(edup.get_absolute_url())
                else:
                    event.other_versions = None
                    if edup.representative == event:
                        edup.representative = None
                    event.set_no_modification_date_changed()
                    event.save()
                    edup.save()
                    edup.events = [e for e in edup.events if e.pk != event.pk]
                    messages.success(
                        request,
                        _(
                            "The event has been withdrawn from the group and made independent."
                        ),
                    )
                    if edup.nb_duplicated() == 1:
                        return HttpResponseRedirect(edup.get_absolute_url())
                    else:
                        form = FixDuplicates(edup=edup)
            elif form.is_action_update():
                # otherwise, an event will be updated using other elements
                event = form.get_selected_event(edup)
                if event is None:
                    messages.error(
                        request,
                        _(
                            "The selected item is no longer included in the list of duplicates. Someone else has probably modified the list in the meantime."
                        ),
                    )
                    return HttpResponseRedirect(edup.get_absolute_url())
                else:
                    return HttpResponseRedirect(
                        reverse_lazy("update_event", args=[edup.pk, event.pk])
                    )
            else:
                # otherwise, a new event will be created using a merging process
                return HttpResponseRedirect(
                    reverse_lazy("merge_duplicate", args=[edup.pk])
                )
    else:
        form = FixDuplicates(edup=edup)

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


class DuplicatedEventsUpdateView(
    LoginRequiredMixin, UpdateView
):  # Todo à supprimer, pas d’utilisation ?
    model = DuplicatedEvents
    fields = ()
    template_name = "agenda_culturel/fix_duplicate.html"


@login_required(login_url="/accounts/login/")
@permission_required("agenda_culturel.view_duplicatedevents")
def duplicates(request):
    nb_removed = DuplicatedEvents.remove_singletons()
    if nb_removed > 0:
        messages.success(
            request,
            _("Cleaning up duplicates: {} item(s) fixed.").format(nb_removed),
        )

    filter = DuplicatedEventsFilter(
        request.GET, queryset=DuplicatedEvents.objects.all().order_by("-pk")
    )
    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/duplicates.html",
        {
            "filter": filter,
            "paginator_filter": response,
            "paginator": paginator,
        },
    )


def set_duplicate(request, year, month, day, pk):
    event = get_object_or_404(Event, pk=pk)
    cday = CalendarDay(date(year, month, day))
    others = [
        e
        for e in cday.get_events()
        if e != event
        and (event.other_versions is None or event.other_versions != e.other_versions)
        and e.status != Event.STATUS.TRASH
    ]

    form = SelectEventInList(events=others)

    if request.method == "POST":
        form = SelectEventInList(request.POST, events=others)
        if form.is_valid():
            selected = [o for o in others if o.pk == int(form.cleaned_data["event"])]
            event.set_other_versions(selected)
            # save them without updating modified date
            event.set_no_modification_date_changed()
            event.save()
            if request.user.is_authenticated:
                messages.success(request, _("The event was successfully duplicated."))
                return HttpResponseRedirect(
                    reverse_lazy("view_duplicate", args=[event.other_versions.pk])
                )
            else:
                messages.info(
                    request,
                    _(
                        "The event has been successfully flagged as a duplicate. The moderation team will deal with your suggestion shortly."
                    ),
                )
                return HttpResponseRedirect(event.get_absolute_url())

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