# Decorating URL includes, <https://djangosnippets.org/snippets/2532/>

from functools import wraps

from django.conf import settings
from django.contrib.auth.decorators import user_passes_test
from django.core.exceptions import PermissionDenied
from django.core.urlresolvers import RegexURLPattern, RegexURLResolver
from django.views.debug import technical_404_response
from django.http import Http404

class DecoratedURLPattern(RegexURLPattern):
    def resolve(self, *args, **kwargs):
        result = super(DecoratedURLPattern, self).resolve(*args, **kwargs)
        if result:
            result.func = self._decorate_with(result.func)
        return result

class DecoratedRegexURLResolver(RegexURLResolver):
    def resolve(self, *args, **kwargs):
        result = super(DecoratedRegexURLResolver, self).resolve(*args, **kwargs)
        if result:
            result.func = self._decorate_with(result.func)
        return result

def decorated_includes(func, includes, *args, **kwargs):
    urlconf_module, app_name, namespace = includes

    for item in urlconf_module:
        if isinstance(item, RegexURLPattern):
            item.__class__ = DecoratedURLPattern
            item._decorate_with = func

        elif isinstance(item, RegexURLResolver):
            item.__class__ = DecoratedRegexURLResolver
            item._decorate_with = func

    return urlconf_module, app_name, namespace


# below, a set of decorators to generate urls.py like this:
#      urlpatterns = required(
#          app_enabled('applabel'),
#          patterns( ... urls for applabel ... )
#      )

def unless(test, message):
    '''Decorator returning a 404 status code if some condition is not met'''
    def decorator(func):
        @wraps(func)
        def f(request, *args, **kwargs):
            if not test():
                return technical_404_response(request, Http404(message))
            return func(request, *args, **kwargs)
        return f
    return decorator

def app_enabled(app_label):
    '''for enabling a view based on PASSERELLE_APP_<APP_LABEL>_ENABLED flag'''
    def test():
        return getattr(settings, 'PASSERELLE_APP_%s_ENABLED' % app_label.upper(), False)
    return unless(test, 'please enable %s' % app_label)

def setting_enabled(name):
    '''for enabling a view based on a setting'''
    def test():
        return getattr(settings, name, False)
    return unless(test, 'please enable %s' % name)

# code bellow is borrowed from https://djangosnippets.org/snippets/2607/
# or https://gist.github.com/sjzabel/1378003
def required(wrapping_functions,patterns_rslt):
    '''
    Used to require 1..n decorators in any view returned by a url tree

    Usage:
      urlpatterns = required(func,patterns(...))
      urlpatterns = required((func,func,func),patterns(...))

    Note:
      Use functools.partial to pass keyword params to the required
      decorators. If you need to pass args you will have to write a
      wrapper function.

    Example:
      from functools import partial

      urlpatterns = required(
          partial(login_required,login_url='/accounts/login/'),
          patterns(...)
      )
    '''
    if not hasattr(wrapping_functions, '__iter__'):
        wrapping_functions = (wrapping_functions, )

    return [
        _wrap_instance__resolve(wrapping_functions, instance)
        for instance in patterns_rslt
    ]

def _wrap_instance__resolve(wrapping_functions, instance):
    def _wrap_func_in_returned_resolver_match(*args, **kwargs):
        rslt = resolve(*args, **kwargs)
        if not hasattr(rslt, 'func'):
            return rslt
        f = getattr(rslt, 'func')
        for _f in reversed(wrapping_functions):
            # @decorate the function from inner to outter
            f = _f(f)
        setattr(rslt, 'func', f)
        return rslt
    if not hasattr(instance, 'resolve'):
        return instance
    resolve = getattr(instance, 'resolve')
    setattr(instance, 'resolve', _wrap_func_in_returned_resolver_match)
    return instance

def manager_required(function=None, login_url=None):
    def check_manager(user):
        if user and user.is_staff:
            return True
        if user and not user.is_anonymous():
            raise PermissionDenied()
        # As the last resort, show the login form
        return False
    actual_decorator = user_passes_test(check_manager, login_url=login_url)
    if function:
        return actual_decorator(function)
    return actual_decorator
