import flask
import logging
import re
import uuid

from sqlalchemy.orm.exc import NoResultFound

log = logging.getLogger(__name__)


def init_front_auth(app):
    """For each request to the front end Flask application, we process the current user, via headers"""
    app.before_request(set_current_user_from_x_forwarded)


def set_current_user_from_x_forwarded():
    """Check the headers X-Forwarded-User and X-Forwarded-Email from Oauth2 Proxy, and use them to load the user.
    If the user does not exist, create it.

    Important: If there is no header name, we load the default user from the configuration. Don't set it in Production.
    """
    import taiga2.controllers.models_controller as mc

    request = flask.request
    config = flask.current_app.config
    user = None

    # Use for development environment
    default_user_email = config.get("DEFAULT_USER_EMAIL", None)

    # Use for production environment
    user_name_header_name = request.headers.get("X-Forwarded-User", None)
    user_email_header_name = request.headers.get("X-Forwarded-Email", None)

    if user_email_header_name is not None:
        try:
            user = mc.get_user_by_email(user_email_header_name)
        except NoResultFound:
            # User does not exists so we can create it
            username = user_email_header_name.split("@")[0]
            user = mc.add_user(name=username, email=user_email_header_name)
            log.debug(
                "We just created the user {} with email {}".format(
                    username, user_email_header_name
                )
            )
            log.debug(
                "Check of the user name ({}) and email ({})".format(
                    user.name, user.email
                )
            )
        user_id = user.id
        log.debug(
            "Looked up header user_email %s to find username: %s",
            user_email_header_name,
            user_id,
        )
    elif user_name_header_name is not None:
        user = mc.get_user_by_name(user_name_header_name)
        log.debug(
            f"Looked up header user_name {user_name_header_name} to find user with id: {user.id}"
        )

    if user is None and default_user_email is not None:
        print(
            "We did not find the user from the headers, loading the default user by its email {}".format(
                default_user_email
            )
        )

        try:
            user = mc.get_user_by_email(default_user_email)
        except NoResultFound:
            user = mc.add_user(name=str(uuid.uuid4()), email=default_user_email)

    flask.g.current_user = user
    return None


def init_backend_auth(app):
    """For each request to the backend end Flask application, we process the current user, via Authorization header"""
    app.before_request(set_current_user_from_bearer_token)


def set_current_user_from_bearer_token():
    """Use the header Authorization to authenticate the user. If we don't find it, we create it.
    The token is a UUID generated by the first Flask app to receive a new user.

    Important: If no Authorization header is passed, we use the DEFAULT_USER_EMAIL from the configuration settings"""
    import taiga2.controllers.models_controller as mc

    request = flask.request
    config = flask.current_app.config
    user = None
    bearer_token = request.headers.get("Authorization", None)
    default_user_email = config.get("DEFAULT_USER_EMAIL", None)

    if user is None and bearer_token is not None:
        m = re.match("Bearer (\\S+)", bearer_token)
        if m is not None:
            token = m.group(1)
            user = bearer_token_lookup(token)
            if not user:
                # If we did not find the user, we return unauthorized
                flask.abort(401)
            log.debug("Got token %s which mapped to user %s", token, user.email)
        else:
            log.warning("Authorization header malformed: %s", bearer_token)
    else:
        # TODO: Should ask for returning a "Not authenticated" page/response number
        if default_user_email is not None:
            log.critical(
                "DEFAULT_USER_EMAIL is set in config, using that when accessing API"
            )
            try:
                user = mc.get_user_by_email(default_user_email)
            except NoResultFound:
                user = mc.add_user(name=str(uuid.uuid4()), email=default_user_email)
        else:
            log.critical(
                "A request without authentication has been received. Rejecting."
            )
            # raise Exception("No user passed")
            flask.abort(403)
    flask.g.current_user = user


def bearer_token_lookup(token):
    """Ask the controller to return the user given the token"""
    import taiga2.controllers.models_controller as mc

    user = mc.get_user_by_token(token)
    return user
