from django.contrib.auth import get_user_model
from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin
from django.http import HttpResponse, HttpResponseRedirect
from django.urls import reverse, reverse_lazy
from django.views.generic import DetailView, ListView, RedirectView, TemplateView
from django.views.generic.detail import SingleObjectMixin
from django.views.generic.edit import BaseFormView

from wiki_groups.forms import CreateGroupForm, SelectGroupForm, SelectUserForm
from wiki_groups.models import WikiGroup

User = get_user_model()


def dot_graph(request):
    response = HttpResponse(content_type="text/vnd.graphviz")
    response["Content-Disposition"] = 'attachment; filename="groups.dot"'

    response.write("digraph {\n")
    nodes = WikiGroup.objects.values_list("id", "django_group__name")
    edges = WikiGroup.includes_groups.through.objects.values_list(
        "to_wikigroup", "from_wikigroup"
    )
    for id, name in nodes:
        response.write('  {} ["label"="{}"];\n'.format(id, name))
    for u, v in edges:
        response.write("  {} -> {};\n".format(u, v))
    response.write("}\n")

    return response


graph = TemplateView.as_view(template_name="wiki_groups/graph.html")


class WikiGroupMixin(SingleObjectMixin, UserPassesTestMixin):
    """
    Restricts the view to a manager of the wikigroup, and selects automatically
    the required WikiGroup with its Django group
    """

    model = WikiGroup

    def get_queryset(self):
        return super().get_queryset().select_related("django_group")

    def test_func(self):
        self.object = self.get_object()
        return self.request.user.is_staff or self.object.is_manager(self.request.user)


class StaffMixin(UserPassesTestMixin):
    """Restricts the view to a staff member"""

    def test_func(self):
        return self.request.user.is_staff


class ManagedGroupsView(LoginRequiredMixin, ListView):
    model = WikiGroup
    template_name = "wiki_groups/managed.html"

    def get_queryset(self):
        if self.request.user.is_staff:
            return WikiGroup.objects.select_related("django_group")
        return self.request.user.managed_groups.select_related("django_group")

    def get_context_data(self, **kwargs):
        ctx = super().get_context_data(**kwargs)
        wikigroups = set()

        for group in ctx["wikigroup_list"]:
            wikigroups |= group.get_all_groups()

        ctx["wikigroup_list"] = wikigroups
        ctx["object_list"] = wikigroups

        return ctx


class WikiGroupView(WikiGroupMixin, DetailView):
    template_name = "wiki_groups/admin.html"

    def get_context_data(self, **kwargs):
        kwargs.update({"errors": self.request.session.pop("wiki_form_errors", None)})
        return super().get_context_data(**kwargs)


class RemoveUserView(WikiGroupMixin, RedirectView):
    def get_redirect_url(self, *args, **kwargs):
        return reverse("wiki_groups:admin-group", args=[kwargs["pk"]])

    def get(self, request, *args, **kwargs):
        user = User.objects.filter(pk=kwargs["user_pk"]).first()

        if user is not None:
            self.object.users.remove(user)

        return super().get(request, *args, **kwargs)


class RemoveManagerView(WikiGroupMixin, RedirectView):
    def get_redirect_url(self, *args, **kwargs):
        return reverse("wiki_groups:admin-group", args=[kwargs["pk"]])

    def get(self, request, *args, **kwargs):
        user = User.objects.filter(pk=kwargs["user_pk"]).first()

        if user is not None:
            self.object.users.remove(user)

        self.object.managers.remove(user)

        return super().get(request, *args, **kwargs)


class RemoveGroupView(WikiGroupMixin, RedirectView):
    def get_redirect_url(self, *args, **kwargs):
        return reverse("wiki_groups:admin-group", args=[kwargs["pk"]])

    def get(self, request, *args, **kwargs):
        group = WikiGroup.objects.filter(pk=kwargs["group_pk"]).first()

        if group is not None:
            self.object.includes_groups.remove(group)

        return super().get(request, *args, **kwargs)


class AddUserView(WikiGroupMixin, BaseFormView):
    form_class = SelectUserForm

    def get_success_url(self):
        return reverse("wiki_groups:admin-group", args=[self.object.pk])

    def form_valid(self, form):
        self.object.users.add(form.cleaned_data["user"])

        return super().form_valid(form)

    def form_invalid(self, form):
        self.request.session["wiki_form_errors"] = {
            "user": {"value": form["user"].value(), "msg": form.errors["user"]}
        }
        return HttpResponseRedirect(self.get_success_url())


class AddManagerView(WikiGroupMixin, BaseFormView):
    form_class = SelectUserForm

    def get_success_url(self):
        return reverse("wiki_groups:admin-group", args=[self.object.pk])

    def form_valid(self, form):
        self.object.managers.add(form.cleaned_data["user"])

        return super().form_valid(form)

    def form_invalid(self, form):
        self.request.session["wiki_form_errors"] = {
            "manager": {"value": form["user"].value(), "msg": form.errors["user"]}
        }
        return HttpResponseRedirect(self.get_success_url())


class AddGroupView(WikiGroupMixin, BaseFormView):
    form_class = SelectGroupForm

    def get_success_url(self):
        return reverse("wiki_groups:admin-group", args=[self.object.pk])

    def form_valid(self, form):
        subgroup = form.cleaned_data["group"]
        group = self.object

        if group.group_in_cycle(list(group.includes_groups.all()) + [subgroup]):
            form.add_error("group", "Ajout impossible sous peine de créer un cycle")
            return self.form_invalid(form)

        group.includes_groups.add(subgroup)
        return super().form_valid(form)

    def form_invalid(self, form):
        self.request.session["wiki_form_errors"] = {
            "group_add": {"value": form["group"].value(), "msg": form.errors["group"]}
        }
        return HttpResponseRedirect(self.get_success_url())


class CreateGroupView(WikiGroupMixin, BaseFormView):
    form_class = CreateGroupForm

    def get_success_url(self):
        return reverse("wiki_groups:admin-group", args=[self.object.pk])

    def form_valid(self, form):
        new_group = form.cleaned_data["group"]

        self.object.includes_groups.add(new_group)
        new_group.managers.add(self.request.user)

        return super().form_valid(form)

    def form_invalid(self, form):
        self.request.session["wiki_form_errors"] = {
            "group_create": {
                "value": form["group"].value(),
                "msg": form.errors["group"],
            }
        }
        return HttpResponseRedirect(self.get_success_url())


class DeleteGroupView(SingleObjectMixin, StaffMixin, RedirectView):
    model = WikiGroup
    url = reverse_lazy("wiki_groups:managed-groups")

    def get(self, request, *args, **kwargs):
        group = self.get_object()
        # On enlève les membres pour répercuter les changements
        group.users.clear()

        # On utilise la propagation de la suppression django_group -> wiki_group
        group.django_group.delete()

        return super().get(request, *args, **kwargs)