from functools import wraps
from flask import request, abort
from flask_login import current_user
from flask_login.config import EXEMPT_METHODS
from app import app


def priv_required(*perms):
    """
    Requires the user to be an authenticated member with privileges [perms].
    Calls :attr:`LoginManager.unauthorized` if the user is not authenticated,
    and a 403 if some of the privileges are missing.

    Example:
        @app.route('/admin')
        @priv_required('forum.access.admin')
        def admin_board():
            pass

    It can be convenient to globally turn off authentication when unit testing.
    Setting the `LOGIN_DISABLED` configuration variable to `True` will silence
    this decorator.
    """
    def decorated_view(func):
        @wraps(func)
        def wrapped(*args, **kwargs):
            if request.method in EXEMPT_METHODS:
                return func(*args, **kwargs)
            elif not current_user.is_authenticated:
                return app.login_manager.unauthorized()
            else:
                for p in perms:
                    if not current_user.priv(p):
                        # TODO: Add error message and privilege name
                        abort(403)
            return func(*args, **kwargs)
        return wrapped
    return decorated_view


def guest_only(func):
    """
    Opposite decorator of @login_required
    """
    @wraps(func)
    def wrapped(*args, **kwargs):
        if current_user.is_authenticated:
            abort(404)
        return func(*args, **kwargs)
    return wrapped
