from warnings import warn

from django.contrib.auth.models import Group
from django.shortcuts import get_object_or_404, redirect
from django.urls import re_path, reverse
from django.utils.functional import cached_property
from django.utils.translation import gettext
from django.utils.translation import gettext_lazy as _

from wagtail import hooks
from wagtail.admin.ui.tables import TitleColumn
from wagtail.admin.utils import set_query_params
from wagtail.admin.views import generic
from wagtail.admin.viewsets.model import ModelViewSet
from wagtail.admin.widgets.button import HeaderButton
from wagtail.users.forms import GroupForm, GroupPagePermissionFormSet
from wagtail.utils.deprecation import RemovedInWagtail70Warning

_permission_panel_classes = None


def get_permission_panel_classes():
    global _permission_panel_classes
    if _permission_panel_classes is None:
        _permission_panel_classes = [GroupPagePermissionFormSet]
        for fn in hooks.get_hooks("register_group_permission_panel"):
            _permission_panel_classes.append(fn())

    return _permission_panel_classes


class PermissionPanelFormsMixin:
    def get_permission_panel_form_kwargs(self, cls):
        kwargs = {}

        if self.request.method in ("POST", "PUT"):
            kwargs.update(
                {
                    "data": self.request.POST,
                    "files": self.request.FILES,
                }
            )

        if hasattr(self, "object"):
            kwargs.update({"instance": self.object})

        return kwargs

    def get_permission_panel_forms(self):
        return [
            cls(**self.get_permission_panel_form_kwargs(cls))
            for cls in get_permission_panel_classes()
        ]

    def process_form(self):
        form = self.get_form()
        permission_panels = self.get_permission_panel_forms()
        if form.is_valid() and all(panel.is_valid() for panel in permission_panels):
            response = self.form_valid(form)

            for panel in permission_panels:
                panel.save()

            return response
        else:
            return self.form_invalid(form)

    def get_context_data(self, **kwargs):
        if "permission_panels" not in kwargs:
            kwargs["permission_panels"] = self.get_permission_panel_forms()

        context = super().get_context_data(**kwargs)

        # Add js/css media from the formsets to the existing media
        for panel in context["permission_panels"]:
            context["media"] += panel.media

        return context


class IndexView(generic.IndexView):
    page_title = _("Groups")
    add_item_label = _("Add a group")
    search_box_placeholder = _("Search groups")
    search_fields = ["name"]
    context_object_name = "groups"
    paginate_by = 20

    columns = [
        TitleColumn(
            "name",
            label=_("Name"),
            sort_key="name",
            url_name="wagtailusers_groups:edit",
        ),
    ]


class CreateView(PermissionPanelFormsMixin, generic.CreateView):
    page_title = _("Add group")
    success_message = _("Group '%(object)s' created.")

    def post(self, request, *args, **kwargs):
        """
        Handle POST requests: instantiate a form instance with the passed
        POST variables and then check if it's valid.
        """
        # Create an object now so that the permission panel forms have something to link them against
        self.object = Group()
        return self.process_form()


class EditView(PermissionPanelFormsMixin, generic.EditView):
    success_message = _("Group '%(object)s' updated.")
    error_message = _("The group could not be saved due to errors.")
    context_object_name = "group"

    @cached_property
    def header_buttons(self):
        return [
            HeaderButton(
                gettext("View users in this group"),
                url=set_query_params(
                    reverse("wagtailusers_users:index"),
                    {"group": self.object.pk},
                ),
                icon_name="user",
            )
        ]

    def post(self, request, *args, **kwargs):
        """
        Handle POST requests: instantiate a form instance with the passed
        POST variables and then check if it's valid.
        """
        self.object = self.get_object()
        return self.process_form()


class DeleteView(generic.DeleteView):
    success_message = _("Group '%(object)s' deleted.")
    page_title = _("Delete group")
    confirmation_message = _("Are you sure you want to delete this group?")


class GroupViewSet(ModelViewSet):
    icon = "group"
    model = Group
    ordering = ["name"]
    add_to_reference_index = False
    menu_name = "groups"
    menu_label = _("Groups")
    menu_order = 601
    add_to_settings_menu = True

    index_view_class = IndexView
    add_view_class = CreateView
    edit_view_class = EditView
    delete_view_class = DeleteView

    # Copying requires prepopulating the formsets with copies of the existing
    # permissions, which is not currently supported.
    copy_view_enabled = False

    template_prefix = "wagtailusers/groups/"

    @property
    def users_view(self):
        def view(request, pk):
            legacy_url = reverse(self.get_url_name("users"), args=(pk,))
            new_url = set_query_params(
                reverse("wagtailusers_users:index"),
                {"group": get_object_or_404(Group, pk=pk).pk},
            )

            warn(
                f"Accessing the list of users in a group via {legacy_url} is "
                f"deprecated, use {new_url} instead.",
                RemovedInWagtail70Warning,
            )
            return redirect(new_url)

        return view

    def get_common_view_kwargs(self, **kwargs):
        return super().get_common_view_kwargs(
            **{
                "usage_url_name": None,
                **kwargs,
            }
        )

    def get_form_class(self, for_update=False):
        return GroupForm

    def get_urlpatterns(self):
        return super().get_urlpatterns() + [
            re_path(r"(\d+)/users/$", self.users_view, name="users"),
        ]
