from urllib.parse import urlsplit
from urllib.parse import urlunsplit

from authlib.common.errors import AuthlibBaseError
from authlib.integrations.flask_client import OAuth
from authlib.oidc.discovery import get_well_known_url
from flask import Flask
from flask import current_app
from flask import flash
from flask import redirect
from flask import render_template
from flask import session
from flask import url_for

oauth = OAuth()


def setup_routes(app):
    @app.route("/")
    @app.route("/tos")
    @app.route("/policy")
    def index():
        return render_template(
            "index.html", user=session.get("user"), name=app.config["NAME"]
        )

    @app.route("/register")
    def register():
        return oauth.canaille.authorize_redirect(
            url_for("register_callback", _external=True), prompt="create"
        )

    @app.route("/register_callback")
    def register_callback():
        try:
            token = oauth.canaille.authorize_access_token()
            session["user"] = token.get("userinfo")
            session["id_token"] = token["id_token"]
            flash("You account has been successfully created.", "success")
        except AuthlibBaseError as exc:
            flash(f"An error happened during registration: {exc.description}", "error")

        return redirect(url_for("index"))

    @app.route("/login")
    def login():
        return oauth.canaille.authorize_redirect(
            url_for("login_callback", _external=True)
        )

    @app.route("/login_callback")
    def login_callback():
        try:
            token = oauth.canaille.authorize_access_token()
            session["user"] = token.get("userinfo")
            session["id_token"] = token["id_token"]
            flash("You have been successfully logged in.", "success")
        except AuthlibBaseError as exc:
            flash(f"An error happened during login: {exc.description}", "error")

        return redirect(url_for("index"))

    @app.route("/logout")
    def logout():
        oauth.canaille.load_server_metadata()
        end_session_endpoint = oauth.canaille.server_metadata.get(
            "end_session_endpoint"
        )
        end_session_url = set_parameter_in_url_query(
            end_session_endpoint,
            client_id=current_app.config["OAUTH_CLIENT_ID"],
            id_token_hint=session["id_token"],
            post_logout_redirect_uri=url_for("logout_callback", _external=True),
        )
        return redirect(end_session_url)

    @app.route("/logout_callback")
    def logout_callback():
        try:
            del session["user"]
        except KeyError:
            pass

        flash("You have been successfully logged out", "success")
        return redirect(url_for("index"))


def setup_oauth(app):
    oauth.init_app(app)
    oauth.register(
        name="canaille",
        client_id=app.config["OAUTH_CLIENT_ID"],
        client_secret=app.config["OAUTH_CLIENT_SECRET"],
        server_metadata_url=get_well_known_url(
            app.config["OAUTH_AUTH_SERVER"], external=True
        ),
        client_kwargs={"scope": "openid profile email phone address groups"},
    )


def create_app():
    app = Flask(__name__)
    app.config.from_envvar("CONFIG")
    app.static_folder = "../../canaille/static"

    setup_routes(app)
    setup_oauth(app)
    return app


def set_parameter_in_url_query(url, **kwargs):
    split = list(urlsplit(url))

    parameters = "&".join(f"{key}={value}" for key, value in kwargs.items())

    if split[3]:
        split[3] = f"{split[3]}&{parameters}"
    else:
        split[3] = parameters

    return urlunsplit(split)
