from functools import wraps
from six import string_types
from typing import List, Union

from staff.lib.decorators import responding_json
from staff.lib.exceptions import ErrorWithStatusCode

from staff.person.models import Staff

from staff.person_profile.permissions.properties import Properties
from staff.person_profile.permissions.registry import BlockRegistry
from staff.person_profile.permissions.base import BaseRegistry


@responding_json
def view403(*args, **kw):
    return {}, 403


@responding_json
def view404(*args, **kw):
    return {}, 404


exception_views = {403: view403, 404: view404}


def observer_has_perm(perm_name):

    def decorator(view):

        @wraps(view)
        def wrapper(request, login, *args, **kwargs):
            request.permissions_ctl = get_block_registry(
                observer=request.user.get_profile(),
                login_or_logins=login,
                service_readonly=request.service_is_readonly,
            )
            try:
                has_perm = request.permissions_ctl.is_available(perm_name, login)
            except ErrorWithStatusCode as e:
                exception_view = exception_views.get(e.status_code)
                if exception_view:
                    return exception_view(request, login, *args, **kwargs)
                raise

            if has_perm:
                return view(request, login, *args, **kwargs)
            else:
                return view403(request, login)

        return wrapper

    return decorator


def attach_permissions_ctl(view):
    @wraps(view)
    def wrapper(request, login, *args, **kwargs):
        if getattr(request, 'permissions_ctl', None) is None:
            request.permissions_ctl = get_block_registry(
                observer=request.user.get_profile(),
                login_or_logins=login,
                service_readonly=request.service_is_readonly,
            )
        return view(request, login, *args, **kwargs)
    return wrapper


def get_block_registry(
    observer: Staff,
    login_or_logins: Union[str, List[str]],
    service_readonly: bool = False,
) -> BaseRegistry:
    target_logins = [login_or_logins] if isinstance(login_or_logins, string_types) else login_or_logins
    properties = Properties(
        target_logins=target_logins,
        observer=observer,
        readonly=service_readonly,
    )
    return BlockRegistry(properties)
