from flask import Blueprint, render_template, request, redirect, url_for, flash, make_response, jsonify
import base64
import json
from app.extensions import db, redis_controller, limiter, csrf
from app.util import auth, websiteFeatures
from app.enums.AssetType import AssetType

from app.models.asset import Asset
from app.models.user_avatar_asset import UserAvatarAsset
from app.models.user_avatar import UserAvatar
from app.models.userassets import UserAsset
from app.models.user_thumbnail import UserThumbnail
from app.models.user import User

from app.routes.thumbnailer import TakeUserThumbnail
AllowedBodyColors = [361, 192, 217, 153, 359, 352, 5, 101, 1007, 1014, 38, 18, 125, 1030, 133, 106, 105, 1017, 24, 334, 226, 141, 1021, 28, 37, 310, 317, 119, 1011, 1012, 1010, 23, 305, 102, 45, 107, 1018, 1027, 1019, 1013, 11, 1024, 104, 1023, 321, 1015, 1031, 1006, 1026, 21, 1004, 1032, 1016, 330, 9, 1025, 364, 351, 1008, 29, 1022, 151, 135, 1020, 1028, 1009, 1029, 1003, 26, 199, 194, 1002, 208, 1, 1001]
ScalingLimits = {
    "height": {
      "min": 0.9,
      "max": 1.05,
      "increment": 0.01
    },
    "width": {
      "min": 0.7,
      "max": 1,
      "increment": 0.01
    },
    "head": {
      "min": 0.95,
      "max": 1,
      "increment": 0.01
    },
    "proportion": {
      "min": 0,
      "max": 1,
      "increment": 0.01
    },
    "bodyType": {
      "min": 0,
      "max": 1,
      "increment": 0.01
    }
}
AvatarRoute = Blueprint('avatar', __name__, template_folder='pages')

@AvatarRoute.route('/avatar', methods=['GET'])
@auth.authenticated_required
def avatar():
    AuthenticatedUser : User = auth.GetCurrentUser()
    return render_template('avatar/avatar.html', user=AuthenticatedUser)

@AvatarRoute.route("/avatar/getassets", methods=["GET"])
@auth.authenticated_required_api
def get_assets():
    ReqAssetType = request.args.get("type")
    if ReqAssetType is None:
        return jsonify({"success": False, "message": "Invalid request"}), 400
    try:
        ReqAssetType = int(ReqAssetType)
    except:
        return jsonify({"success": False, "message": "Invalid request"}), 400
    if ReqAssetType not in [2,8,11,12,17,18,19,27,28,29,30,31,32,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58]:
        return jsonify({"success": False, "message": "Invalid request"}), 400
    PageNumber = request.args.get("page") or 0
    try:
        PageNumber = int(PageNumber)
    except:
        return jsonify({"success": False, "message": "Invalid request"}), 400
    PageNumber += 1

    AuthenticatedUser : User = auth.GetCurrentUser()
    AssetsList : list[UserAsset] = UserAsset.query.filter_by(userid=AuthenticatedUser.id).join(Asset, Asset.id == UserAsset.assetid).filter(Asset.asset_type == AssetType(ReqAssetType)).distinct(Asset.id).paginate( page=PageNumber, per_page=12, error_out=False)    
    AssetList = []
    for AssetObj in AssetsList.items:
        AssetObj : Asset = Asset.query.filter_by(id=AssetObj.assetid).first()
        if AssetObj is None:
            continue
        AssetList.append({
            "id": AssetObj.id,
            "name": AssetObj.name,
            "moderation_status": AssetObj.moderation_status
        })

    return jsonify({
        "success": True,
        "assets": AssetList,
        "lastPage": not AssetsList.has_next
    })

@AvatarRoute.route('/avatar/getavatar', methods=['GET'])
@auth.authenticated_required_api
def get_avatar():
    AuthenticatedUser : User = auth.GetCurrentUser()
    userCurrentlyWearing : list[UserAvatarAsset] = UserAvatarAsset.query.filter_by(user_id=AuthenticatedUser.id).all()
    userCurrentlyWearingList : list[int] = []
    for asset in userCurrentlyWearing:
        AssetObj : Asset = Asset.query.filter_by(id=asset.asset_id).first()
        userCurrentlyWearingList.append({
            "id": AssetObj.id,
            "name": AssetObj.name,
            "type": AssetObj.asset_type.value,
            "moderation_status": AssetObj.moderation_status
        })
    avatar : UserAvatar = UserAvatar.query.filter_by(user_id=AuthenticatedUser.id).first()
    return jsonify({
        "success": True,
        "currentlyWearing": userCurrentlyWearingList,
        "bodyColors": [
            avatar.head_color_id,
            avatar.torso_color_id,
            avatar.left_arm_color_id,
            avatar.right_arm_color_id,
            avatar.left_leg_color_id,
            avatar.right_leg_color_id
        ],
        "rigType": "R15" if avatar.r15 else "R6",
        "scales": {
            "height": avatar.height_scale,
            "width": avatar.width_scale,
            "head": avatar.head_scale,
            "proportion": avatar.proportion_scale,
            "bodyType": avatar.body_type_scale
        }
    })

@AvatarRoute.route("/avatar/setavatar", methods=["POST"])
@auth.authenticated_required_api
@csrf.exempt
def set_wearing_assets():
    if not websiteFeatures.GetWebsiteFeature("AvatarUpdate"):
        return jsonify({"success": False, "message": "Avatar updating is currently disabled."}), 400

    newAvatarData = request.json
    if "bodyColors" not in newAvatarData or "assets" not in newAvatarData or "rigType" not in newAvatarData:
        return jsonify({"success": False, "message": "Invalid request"}), 400
    if len(newAvatarData["bodyColors"]) != 6:
        return jsonify({"success": False, "message": "Invalid request"}), 400

    AuthenticatedUser : User = auth.GetCurrentUser()
    if AuthenticatedUser is None:
        return redirect('/login')
    if redis_controller.get(f"avatar:lock:{AuthenticatedUser.id}") is not None:
        return jsonify({"success": False, "message": "You are changing your avatar too fast."}), 429
    redis_controller.set(f"avatar:lock:{AuthenticatedUser.id}", "1", ex=5)
    avatar : UserAvatar = UserAvatar.query.filter_by(user_id=AuthenticatedUser.id).first()

    if len(newAvatarData["assets"]) > 32:
        return jsonify({"success": False, "message": "Too many assets"}), 400
    AssetTypeCount = {}
    for asset in newAvatarData["assets"]:
        AssetObj : Asset = Asset.query.filter_by(id=asset).first()
        if AssetObj is None:
            return jsonify({"success": False, "message": "Invalid asset"}), 400
        if AssetObj.asset_type.value not in [2,8,11,12,17,18,19,27,28,29,30,31,32,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58]:
            return jsonify({"success": False, "message": "Invalid asset"}), 400
        if AssetTypeCount.get(AssetObj.asset_type.value) is None:
            AssetTypeCount[AssetObj.asset_type.value] = 1
        else:
            AssetTypeCount[AssetObj.asset_type.value] += 1
        if AssetTypeCount[AssetObj.asset_type.value] > 1 and AssetObj.asset_type != AssetType.Hat and AssetObj.asset_type != AssetType.HairAccessory and AssetObj.asset_type != AssetType.FaceAccessory:
            return jsonify({"success": False, "message": "Invalid configuration"}), 400
        elif AssetTypeCount[AssetObj.asset_type.value] > 5 and AssetObj.asset_type == AssetType.Hat:
            return jsonify({"success": False, "message": "Invalid configuration"}), 400
        
        if AssetObj.moderation_status != 0:
            newAvatarData["assets"].remove(asset)
        
        UserAssetObj : UserAsset = UserAsset.query.filter_by(userid=AuthenticatedUser.id, assetid=asset).first()
        if UserAssetObj is None:
            return jsonify({"success": False, "message": "Invalid asset"}), 400
    for Color in newAvatarData["bodyColors"]:
        if Color not in AllowedBodyColors:
            return jsonify({"success": False, "message": "Invalid body color"}), 400
    
    UserAvatarAsset.query.filter_by(user_id=AuthenticatedUser.id).delete()
    db.session.commit()
    for asset in newAvatarData["assets"]:
        UserAvatarAssetObj = UserAvatarAsset(
            user_id=AuthenticatedUser.id,
            asset_id=asset
        )
        db.session.add(UserAvatarAssetObj)
    avatar.head_color_id = newAvatarData["bodyColors"][0]
    avatar.torso_color_id = newAvatarData["bodyColors"][1]
    avatar.left_arm_color_id = newAvatarData["bodyColors"][2]
    avatar.right_arm_color_id = newAvatarData["bodyColors"][3]
    avatar.left_leg_color_id = newAvatarData["bodyColors"][4]
    avatar.right_leg_color_id = newAvatarData["bodyColors"][5]
    avatar.r15 = newAvatarData["rigType"] == "R15"

    if "scales" in newAvatarData:
        if "height" not in newAvatarData["scales"] or "width" not in newAvatarData["scales"] or "head" not in newAvatarData["scales"] or "proportion" not in newAvatarData["scales"]:
            return jsonify({"success": False, "message": "Invalid request"}), 400
        if newAvatarData["scales"]["height"] < ScalingLimits["height"]["min"] or newAvatarData["scales"]["height"] > ScalingLimits["height"]["max"]:
            return jsonify({"success": False, "message": "Invalid height scale"}), 400
        if newAvatarData["scales"]["width"] < ScalingLimits["width"]["min"] or newAvatarData["scales"]["width"] > ScalingLimits["width"]["max"]:
            return jsonify({"success": False, "message": "Invalid width scale"}), 400
        if newAvatarData["scales"]["head"] < ScalingLimits["head"]["min"] or newAvatarData["scales"]["head"] > ScalingLimits["head"]["max"]:
            return jsonify({"success": False, "message": "Invalid head scale"}), 400
        if newAvatarData["scales"]["proportion"] < ScalingLimits["proportion"]["min"] or newAvatarData["scales"]["proportion"] > ScalingLimits["proportion"]["max"]:
            return jsonify({"success": False, "message": "Invalid proportion scale"}), 400
        
        avatar.height_scale = round(newAvatarData["scales"]["height"], 2)
        avatar.width_scale = round(newAvatarData["scales"]["width"], 2)
        avatar.head_scale = round(newAvatarData["scales"]["head"], 2)
        avatar.proportion_scale = round(newAvatarData["scales"]["proportion"], 2)

    db.session.commit()

    UserThumbnail.query.filter_by(userid=AuthenticatedUser.id).delete()
    db.session.commit()
    TakeUserThumbnail(AuthenticatedUser.id, True, False)
     # handle 3d within
    TakeUserThumbnail(AuthenticatedUser.id, bypassCooldown=True, bypassCache=True, Is3D=True)
    return jsonify({"success": True}), 200

@AvatarRoute.route("/avatar/forceredraw", methods=["POST"])
@auth.authenticated_required_api
@csrf.exempt
def force_redraw():
    AuthenticatedUser : User = auth.GetCurrentUser()
    if redis_controller.get(f"avatar:lock:{AuthenticatedUser.id}") is not None:
        return jsonify({"success": False, "message": "A redraw has recently been started please try again later."}), 429
    redis_controller.set(f"avatar:lock:{AuthenticatedUser.id}", "1", ex=5)
    UserThumbnail.query.filter_by(userid=AuthenticatedUser.id).delete()
    db.session.commit()
    TakeUserThumbnail(AuthenticatedUser.id, True, True)
    # handle 3d within
    TakeUserThumbnail(AuthenticatedUser.id, bypassCooldown=True, bypassCache=True, Is3D=True)
    return jsonify({"success": True}), 200



@AvatarRoute.route("/avatar/3d", methods=["GET"])
@auth.authenticated_required_api
def get_3d_avatar():
    AuthenticatedUser: User = auth.GetCurrentUser()
    
    target_user_id = request.args.get("userId", None)
    
    if target_user_id:
        target_id = target_user_id
    else:
        target_id = AuthenticatedUser.id
    
    UserThumbnailObj: UserThumbnail = UserThumbnail.query.filter_by(userid=target_id).first()
    
    if UserThumbnailObj and UserThumbnailObj.full_3dcontenthash:
        return jsonify({
            "targetId": target_id,
            "state": "Completed",
            "imageUrl": UserThumbnailObj.full_3dcontenthash,
            "version": "TN3"
        })
    else:
        TakeUserThumbnail(target_id, bypassCooldown=True, bypassCache=False, Is3D=True)
        
        return jsonify({
            "targetId": target_id,
            "state": "Pending",
            "imageUrl": "",
            "version": "TN3"
        })


@AvatarRoute.route("/avatar/isthumbnailready", methods=["GET"])
@auth.authenticated_required_api
@csrf.exempt
def is_thumbnail_ready():
    AuthenticateUser : User = auth.GetCurrentUser()
    UserThumbnailObj : UserThumbnail = UserThumbnail.query.filter_by(userid=AuthenticateUser.id).first()
    if UserThumbnailObj is None:
        return jsonify({"success": True, "ready": False}), 200
    else:
        if UserThumbnailObj.full_contenthash is None:
            return jsonify({"success": True, "ready": False}), 200
        return jsonify({"success": True, "ready": True}), 200