from flask import Blueprint, render_template, redirect, url_for, request, flash, jsonify, session, abort
from config import Config
from app.extensions import db
from app.util import auth
from app.models.user import User
from app.models.user_email import UserEmail

import hashlib
import hmac
import base64
import urllib.parse
import time
import logging

config = Config()
discourse_sso = Blueprint("discourse_sso", __name__, url_prefix="/discourse")

@discourse_sso.before_request
def VerifyUserAgent():
    if not config.DISCOURSE_SSO_ENABLED:
        return abort(404)

def VerifyPayloadSignature( Payload : str, Signature : str ) -> bool:
    """
        Verifies the payload signature
    """
    return hmac.compare_digest(
        Signature,
        hmac.new(
            key = config.DISCOURSE_SECRET_KEY.encode("utf-8"),
            msg = Payload.encode("utf-8"),
            digestmod = hashlib.sha256
        ).hexdigest()
    )

def SignPayload( Payload : str ) -> str:
    """
        Signs the payload and returns the signature
    """
    return hmac.new(
        key = config.DISCOURSE_SECRET_KEY.encode("utf-8"),
        msg = Payload.encode("utf-8"),
        digestmod = hashlib.sha256
    ).hexdigest()

@discourse_sso.route("/", methods=["GET"])
@auth.authenticated_required
def DiscourseSSOIndex():
    return render_template("discourse/leaving-syntax.html", baseurl=config.DISCOURSE_FORUM_BASEURL)

@discourse_sso.route("/sso", methods=["GET", "POST"])
@auth.authenticated_required
def DiscourseSSO():
    SSOPayload : str = request.args.get("sso", default=None, type=str)
    PayloadSignature : str = request.args.get("sig", default=None, type=str)

    if SSOPayload is None or PayloadSignature is None:
        return abort(404)
    isValidSignature = VerifyPayloadSignature(SSOPayload, PayloadSignature)
    if not isValidSignature:
        return abort(404)
    Payload = urllib.parse.parse_qs(base64.b64decode(SSOPayload).decode("utf-8"))
    if "nonce" not in Payload:
        return abort(404)
    nonce = Payload["nonce"][0]
    if nonce is None:
        return abort(404)
    
    AuthenticatedUser : User = auth.GetCurrentUser()
    UserEmailObj : UserEmail = UserEmail.query.filter_by(user_id=AuthenticatedUser.id, verified=True).first()
    if UserEmailObj is None:
        flash("You must have a verified email address to access the forums", "error")
        return redirect(url_for("settings.settings_update_email"))

    if request.method == "GET":
        return render_template("discourse/sso-confirm.html", baseurl=config.DISCOURSE_FORUM_BASEURL)
    elif request.method == "POST":
        payload = {
            "nonce": nonce,
            "email": f"SyntaxUser{AuthenticatedUser.id}@forum.nexium.fit",
            "external_id": AuthenticatedUser.id,
            "username": AuthenticatedUser.username,
            "name": AuthenticatedUser.username,
            "avatar_url": f"{config.BaseURL}/Thumbs/Head.ashx?x=420&y=420&userId={AuthenticatedUser.id}&rnd={time.time()}",
            "avatar_force_update": "true",
            "require_activation": "false",
            "suppress_welcome_message": "true"
        }
        
        payload = urllib.parse.urlencode(payload)
        payload = base64.b64encode(payload.encode("utf-8")).decode("utf-8")
        payloadSignature = SignPayload(payload)

        return redirect(f"{config.DISCOURSE_FORUM_BASEURL}/session/sso_login?sso={payload}&sig={payloadSignature}")        
    else:
        return abort(404)