from functools import wraps
from typing import Literal, Dict, Union, List

from drf_spectacular.utils import extend_schema

from .utils import has_perms as has_permissions


def _get_perms_desc(perms, operator):
    perms_text = ", ".join(perms)
    desc = f"**Perms(operator={operator}):** `[{perms_text}]`"
    return desc


def has_perm(*permission: str, operator: Literal["OR", "AND"] = "OR"):
    """
    Usage:
        has_perm('PERM1')
        has_perm('PERM1', 'PERM2')

    :param permission: One or many permissions.
    :param operator: Any of (OR, AND)
    """

    def decorator(view_func):
        view_func._perms = permission
        description = _get_perms_desc(permission, operator)

        view_func = extend_schema(description=description)(view_func)

        @wraps(view_func)
        def _wrapped_view(self, request, *args, **kwargs):
            if not has_permissions(user_id=request.user.id, perms=list(permission), operator=operator, request=request):
                self.permission_denied(request)
            return view_func(self, request, *args, **kwargs)

        return _wrapped_view

    return decorator


def has_class_perm(permissions_map: Dict[str, Union[str, List[str]]], operator: Literal["OR", "AND"] = "OR"):
    """
    Usage:
        @has_class_perm({
            'list': ['perm1', 'perm2'],
            'create': 'perm3',
            'update': ['perm4']
        })
    """

    def _make_wrapper(method, perms):
        @wraps(method)
        def wrapped(self, request, *args, **kwargs):
            return method(self, request, *args, **kwargs)

        wrapped._perms = perms
        return wrapped

    def decorator(cls):
        for action, perms in permissions_map.items():
            if isinstance(perms, str):
                perms = [perms]

            if hasattr(cls, action):
                method = getattr(cls, action)
                wrapped = _make_wrapper(method, perms)
                description = _get_perms_desc(perms, operator)
                decorated_method = extend_schema(description=description)(wrapped)
                setattr(cls, action, decorated_method)

        original_initial = cls.initial

        @wraps(original_initial)
        def new_initial(self, request, *args, **kwargs):
            action = getattr(self, 'action', None) or request.method.lower()

            if action in permissions_map:
                required_perms = permissions_map[action]

                if isinstance(required_perms, str):
                    required_perms = [required_perms]

                if not has_permissions(user_id=request.user.id, perms=required_perms, operator=operator,
                                       request=request):
                    self.permission_denied(request)

            return original_initial(self, request, *args, **kwargs)

        cls.initial = new_initial
        return cls

    return decorator
