from django.contrib.auth.backends import ModelBackend
from django.contrib.auth.models import User
from django.db.models import Model
from django.http import HttpRequest

from guardian.backends import ObjectPermissionBackend
from guardian.shortcuts import get_objects_for_user
from rules import predicate

from ..models import Group
from .core_helpers import get_site_preferences
from .core_helpers import has_person as has_person_helper


def permission_validator(request: HttpRequest, perm: str) -> bool:
    """Checks whether the request user has a permission."""
    if request.user:
        return request.user.has_perm(perm)
    return False


def check_global_permission(user: User, perm: str) -> bool:
    """Checks whether a user has a global permission."""
    return ModelBackend().has_perm(user, perm)


def check_object_permission(user: User, perm: str, obj: Model) -> bool:
    """Checks whether a user has a permission on a object."""
    return ObjectPermissionBackend().has_perm(user, perm, obj)


def has_global_perm(perm: str):
    """Builds predicate which checks whether a user has a global permission."""
    name = f"has_global_perm:{perm}"

    @predicate(name)
    def fn(user: User) -> bool:
        return check_global_permission(user, perm)

    return fn


def has_object_perm(perm: str):
    """Builds predicate which checks whether a user has a permission on a object."""
    name = f"has_global_perm:{perm}"

    @predicate(name)
    def fn(user: User, obj: Model) -> bool:
        if not obj:
            return False
        return check_object_permission(user, perm, obj)

    return fn


def has_any_object(perm: str, klass):
    """
    Build predicate which checks whether a user has access to objects with the provided permission.
    """
    name = f"has_any_object:{perm}"

    @predicate(name)
    def fn(user: User) -> bool:
        objs = get_objects_for_user(user, perm, klass)
        return len(objs) > 0

    return fn


def is_site_preference_set(section: str, pref: str):
    """Check the boolean value of a given site preference."""
    name = f"check_site_preference:{section}__{pref}"

    @predicate(name)
    def fn() -> bool:
        if isinstance(get_site_preferences()[f"{section}__{pref}"], bool):
            return get_site_preferences()[f"{section}__{pref}"]
        return False

    return fn


@predicate
def has_person(user: User) -> bool:
    """Predicate which checks whether a user has a linked person."""
    return has_person_helper(user)


@predicate
def is_current_person(user: User, obj: Model) -> bool:
    """Predicate which checks if the provided object is the person linked to the user object."""
    return user.person == obj


@predicate
def is_group_owner(user: User, group: Group) -> bool:
    """Predicate which checks if the user is a owner of the provided group."""
    return group.owners.filter(owners=user.person).exists()


@predicate
def is_notification_recipient(user: User, obj: Model) -> bool:
    """
    Predicate which checks whether the recipient of the
    notification a user wants to mark read is this user.
    """
    return user == obj.recipient.user