import logging
from rules import predicate
from typing import Sequence, TYPE_CHECKING

if TYPE_CHECKING:
    from sandbox.latchstring.models import User as UserType
    from .models.models import BaseUUIDPKModel as ModelRecord


logger = logging.getLogger(__name__)


# -------------------------------------------------------------------------
def is_group_member(groups: str | Sequence[str]) -> bool:
    """
    Check if user is member of at least one of the groups specified.
    Also checking for is_superuser because that check should always be True
    """
    if isinstance(groups, str):
        groups = [groups]

    assert len(groups) > 0, 'You must provide at least one group name'

    if len(groups) > 3:
        g = list(groups[:3]) + ['...']
    else:
        g = groups  # type: ignore[assignment]

    name = f"is_group_member:{','.join(g)}"

    @predicate(name)  # type: ignore[no-untyped-call]
    def fn(user: 'UserType') -> bool:
        if not user.is_authenticated:
            return False

        if user.is_superuser:
            return True

        return bool(set(groups).intersection(user.groups_cache))

    return fn  # noqa


# -------------------------------------------------------------------------
@predicate
def qualified_to_view(user: 'UserType', obj: 'ModelRecord') -> bool:
    """
    The most basic permission.
    If users can't view, then they can't do anything else
    """
    if is_related_user(user, obj):
        return True

    # If any object is not assigned to a company, then any user can view it.
    # The object might not have a company attribute, or the company attribute
    # not assigned, so it's available to ALL_COMPANIES
    if obj and not obj.assigned_to_company:
        return True

    return False


# -------------------------------------------------------------------------
@predicate
def qualified_to_change(user: 'UserType', obj: 'ModelRecord') -> bool:
    """
    User may change/edit/update objects.
    """
    if not is_related_user(user, obj):
        return False

    return True


# -------------------------------------------------------------------------
@predicate
def qualified_to_delete(user: 'UserType', obj: 'ModelRecord') -> bool:
    """
    User may delete / deactivate objects.
    """
    if not is_related_user(user, obj):
        return False

    return True


# -------------------------------------------------------------------------
@predicate
def is_related_user(user: 'UserType', obj: 'ModelRecord') -> bool:
    """
    Users must _always_ be related to the object
    on which they're performing operations

    :param user: Auth model's User object
    :param obj: Usually a database object
    """
    if not obj:
        logger.error('No object received. Unable to check relationship for %s', user)
        return False

    return obj.is_related(user)


# -------------------------------------------------------------------------
__all__ = (
    'is_group_member',
    'is_related_user',
    'qualified_to_change',
    'qualified_to_view',
    'qualified_to_delete',
)
