from django.core.exceptions import FieldDoesNotExist, ImproperlyConfigured
from django.core.paginator import Paginator
from django.db.models import QuerySet


class RestrictedViewMixin:
    """
    This mixin overrides two helpers in order to pass the user object to the filters.
    get_queryset alters the objects returned for list views.
    get_form_kwargs passes the request object to the form class. Remaining permissions
    checks are in forms.py
    """

    allow_empty = True
    queryset = None
    model = None
    paginate_by = None
    paginate_orphans = 0
    context_object_name = None
    paginator_class = Paginator
    page_kwarg = "page"
    ordering = None

    def set_extra_args(self, user):
        """
        This function is overriden to filter the objects by the requesting user.
        """
        self.extra_permission_args = {}

    def get_queryset(self, **kwargs):
        """
        This function is overriden to filter the objects by the requesting user.
        """
        self.set_extra_args(self.request.user)
        if self.queryset is not None:
            queryset = self.queryset
            if isinstance(queryset, QuerySet):
                # queryset = queryset.all()
                queryset = queryset.filter(
                    user=self.request.user, **self.extra_permission_args
                )
        elif self.model is not None:
            queryset = self.model._default_manager.filter(
                user=self.request.user, **self.extra_permission_args
            )
        else:
            raise ImproperlyConfigured(
                "%(cls)s is missing a QuerySet. Define "
                "%(cls)s.model, %(cls)s.queryset, or override "
                "%(cls)s.get_queryset()." % {"cls": self.__class__.__name__}
            )
        if hasattr(self, "get_ordering"):
            ordering = self.get_ordering()
            if ordering:
                if isinstance(ordering, str):
                    ordering = (ordering,)
                queryset = queryset.order_by(*ordering)

        return queryset

    def get_form_kwargs(self):
        """Passes the request object to the form class.
        This is necessary to only display members that belong to a given user"""

        kwargs = super().get_form_kwargs()
        kwargs["request"] = self.request
        return kwargs


class RestrictedFormMixin:
    """
    This mixin is used to restrict the queryset of a form to the current user.
    The request object is passed from the view.
    Fieldargs is used to pass additional arguments to the queryset filter.
    """

    fieldargs = {}

    # TODO: implement set_extra_args to check more permissions here
    # for completeness, however as views open forms, the permissions
    # are already checked there, so it may not be necessary.
    def __init__(self, *args, **kwargs):
        # self.fieldargs = {}
        self.request = kwargs.pop("request")
        super().__init__(*args, **kwargs)
        for field in self.fields:
            # Check it's not something like a CharField which has no queryset
            if not hasattr(self.fields[field], "queryset"):
                continue

            model = self.fields[field].queryset.model
            # Check if the model has a user field
            try:
                model._meta.get_field("user")
                # Add the user to the queryset filters
                self.fields[field].queryset = model.objects.filter(
                    user=self.request.user, **self.fieldargs.get(field, {})
                )
            except FieldDoesNotExist:
                pass
