import base64
import os

from flask_security import Security, UserMixin, RoleMixin, AnonymousUser
from flask_sqlalchemy import SQLAlchemy
from werkzeug.local import LocalProxy

from web_interface.security.privileges import PRIVILEGES
from web_interface.security.user_role_db_interface import UserRoleDbInterface


def add_flask_security_to_app(app, config):
    _add_configuration_to_app(app, config)

    db = SQLAlchemy(app)

    user_interface = create_user_interface(db)
    security = Security(app, user_interface)

    _add_apikey_handler(security, user_interface)
    return db, user_interface


def create_user_interface(db):
    roles_users = db.Table('roles_users', db.Column('user_id', db.Integer(), db.ForeignKey('user.id')), db.Column('role_id', db.Integer(), db.ForeignKey('role.id')))

    class Role(db.Model, RoleMixin):
        id = db.Column(db.Integer(), primary_key=True)  # pylint: disable=invalid-name
        name = db.Column(db.String(80), unique=True)
        description = db.Column(db.String(255))

    class User(db.Model, UserMixin):
        id = db.Column(db.Integer, primary_key=True)  # pylint: disable=invalid-name
        api_key = db.Column(db.String(255), default=_build_api_key, unique=True)
        email = db.Column(db.String(255), unique=True)
        password = db.Column(db.String(255))
        active = db.Column(db.Boolean())
        confirmed_at = db.Column(db.DateTime())
        roles = db.relationship('Role', secondary=roles_users, backref=db.backref('users', lazy='dynamic'))

    return UserRoleDbInterface(db, User, Role)


def _add_apikey_handler(security, user_datastore):
    @security.login_manager.request_loader
    def load_user_from_request(request):  # pylint: disable=unused-variable
        api_key = request.headers.get('Authorization')
        if api_key:
            user = user_datastore.find_user(api_key=api_key)
            if user:
                return user
        return None


def _add_configuration_to_app(app, config):
    app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
    app.config['SECURITY_PASSWORD_SALT'] = config.get('data_storage', 'password_salt').encode()
    app.config['SQLALCHEMY_DATABASE_URI'] = config.get('data_storage', 'user_database', fallback='sqlite:///')
    app.config['SECURITY_UNAUTHORIZED_VIEW'] = '/login'
    app.config['LOGIN_DISABLED'] = not config.getboolean('ExpertSettings', 'authentication')


def _build_api_key():
    raw_key = os.urandom(32)
    return base64.standard_b64encode(raw_key).decode()


def _auth_is_disabled(user):
    user_object = user._get_current_object() if isinstance(user, LocalProxy) else user
    return isinstance(user_object, AnonymousUser)


def user_has_privilege(user, privilege='delete'):
    return _auth_is_disabled(user) or any(user.has_role(role) for role in PRIVILEGES[privilege])