from mailu import db, models
from mailu.ui import forms

import flask
import flask_login
import functools


def permissions_wrapper(handler):
    """ Decorator that produces a decorator for checking permissions.
    """
    def callback(function, args, kwargs, dargs, dkwargs):
        authorized = handler(args, kwargs, *dargs, **dkwargs)
        if not authorized:
            flask.abort(403)
        elif type(authorized) is int:
            flask.abort(authorized)
        else:
            return function(*args, **kwargs)
    # If the handler has no argument, declare a
    # simple decorator, otherwise declare a nested decorator
    # There are at least two mandatory arguments
    if handler.__code__.co_argcount > 2:
        def decorator(*dargs, **dkwargs):
            def inner(function):
                @functools.wraps(function)
                def wrapper(*args, **kwargs):
                    return callback(function, args, kwargs, dargs, dkwargs)
                wrapper._audit_permissions = handler, dargs
                return flask_login.login_required(wrapper)
            return inner
    else:
        def decorator(function):
            @functools.wraps(function)
            def wrapper(*args, **kwargs):
                return callback(function, args, kwargs, (), {})
            wrapper._audit_permissions = handler, []
            return flask_login.login_required(wrapper)
    return decorator


@permissions_wrapper
def global_admin(args, kwargs):
    """ The view is only available to global administrators.
    """
    return flask_login.current_user.global_admin


@permissions_wrapper
def domain_admin(args, kwargs, model, key):
    """ The view is only available to specific domain admins.
    Global admins will still be able to access the resource.

    A model and key must be provided. The model will be queries
    based on the query parameter named after the key. The model may
    either be Domain or an Email subclass (or any class with a
    ``domain`` attribute which stores a related Domain instance).
    """
    obj = model.query.get(kwargs[key])
    if obj:
        domain = obj if type(obj) is models.Domain else obj.domain
        return domain in flask_login.current_user.get_managed_domains()


@permissions_wrapper
def owner(args, kwargs, model, key):
    """ The view is only available to the resource owner or manager.

    A model and key must be provided. The model will be queries
    based on the query parameter named after the key. The model may
    either be User or any model with a ``user`` attribute storing
    a user instance (like Fetch).

    If the query parameter is empty and the model is User, then
    the resource being accessed is supposed to be the current
    logged in user and access is obviously authorized.
    """
    if kwargs[key] is None and model == models.User:
        return True
    obj = model.query.get(kwargs[key])
    if obj:
        user = obj if type(obj) is models.User else obj.user
        return (
            user.email == flask_login.current_user.email
            or user.domain in flask_login.current_user.get_managed_domains()
        )


@permissions_wrapper
def authenticated(args, kwargs):
    """ The view is only available to logged in users.
    """
    return True



def confirmation_required(action):
    """ View decorator that asks for a confirmation first.
    """
    def inner(function):
        @functools.wraps(function)
        def wrapper(*args, **kwargs):
            form = forms.ConfirmationForm()
            if form.validate_on_submit():
                return function(*args, **kwargs)
            return flask.render_template(
                "confirm.html", action=action.format(*args, **kwargs),
                form=form
            )
        return wrapper
    return inner
