from flask import Blueprint, render_template, redirect, request, flash
from app.util import auth, websiteFeatures
from app.util.discord import GenerateAuthorizationURL, ExchangeCodeForToken, GetUserInfoFromToken
from app.models.user import User
from app.models.linked_discord import LinkedDiscord
from app.extensions import db
from datetime import datetime
from config import Config
import hashlib

config = Config()
link_discord = Blueprint('link_discord', __name__, template_folder='pages')


def GenerateStateHash(Cookie: str) -> str:
    salted_cookie = Cookie + config.FLASK_SESSION_KEY
    return hashlib.md5(salted_cookie.encode('utf-8')).hexdigest()


@link_discord.route('/link-discord', methods=['GET'])
@auth.authenticated_required
def link_discord_page():
    CurrentUser = auth.GetCurrentUser()
    LinkedDiscordObj = LinkedDiscord.query.filter_by(user_id=CurrentUser.id).first()
    if LinkedDiscordObj is not None:
        return redirect('/home')

    return render_template('link_discord.html')


@link_discord.route('/link-discord/start', methods=['GET'])
@auth.authenticated_required
def start_discord_link():
    if not websiteFeatures.GetWebsiteFeature("UsernameChange"):
        flash("Discord Linking is temporarily disabled", "danger")
        return redirect("/link-discord")

    CurrentUser = auth.GetCurrentUser()
    LinkedDiscordObj = LinkedDiscord.query.filter_by(user_id=CurrentUser.id).first()
    if LinkedDiscordObj is not None:
        return redirect('/home')

    state = GenerateStateHash(request.cookies[".ROBLOSECURITY"])
    AuthorizationURL = GenerateAuthorizationURL(state)
    return redirect(AuthorizationURL)


@link_discord.route('/link-discord/callback', methods=['GET'])
@auth.authenticated_required
def discord_link_callback():
    if not websiteFeatures.GetWebsiteFeature("UsernameChange"):
        flash("Discord Linking is temporarily disabled", "danger")
        return redirect("/link-discord")

    CurrentUser = auth.GetCurrentUser()
    LinkedDiscordObj = LinkedDiscord.query.filter_by(user_id=CurrentUser.id).first()
    if LinkedDiscordObj is not None:
        flash("You already have a linked discord account", "warning")
        return redirect('/home')

    expectedState = GenerateStateHash(request.cookies[".ROBLOSECURITY"])
    givenState = request.args.get(key="state", default=None, type=str)

    if givenState is None:
        flash("Invalid state", "danger")
        return redirect("/link-discord")

    if givenState != expectedState:
        flash("State mismatch - potential CSRF attack", "danger")
        return redirect("/link-discord")

    code = request.args.get(key="code", default=None, type=str)
    if code is None:
        flash("No authorization code received", "danger")
        return redirect("/link-discord")

    try:
        AccessToken = ExchangeCodeForToken(code)
        DiscordUserInfo = GetUserInfoFromToken(AccessToken)
    except Exception as e:
        flash(f"Failed to authenticate with Discord: {str(e)}", "danger")
        return redirect("/link-discord")

    ExistingDiscordLink = LinkedDiscord.query.filter_by(discord_id=DiscordUserInfo.UserId).first()
    if ExistingDiscordLink is not None:
        flash("This Discord account is already linked to another account", "danger")
        return redirect("/link-discord")

    NewLinkedDiscord = LinkedDiscord(
        user_id=CurrentUser.id,
        discord_id=DiscordUserInfo.UserId,
        discord_username=DiscordUserInfo.Username,
        discord_discriminator=DiscordUserInfo.Discriminator,
        discord_avatar=DiscordUserInfo.AvatarHash,
        discord_access_token=AccessToken,
        discord_refresh_token=None,
        discord_expiry=None,
        last_updated=datetime.utcnow(),
        linked_on=datetime.utcnow()
    )

    db.session.add(NewLinkedDiscord)
    db.session.commit()

    flash("Discord account linked successfully!", "success")
    return redirect('/home')
