from flask import Blueprint, render_template, request, redirect, url_for, flash, make_response, jsonify, abort, Response
import hashlib
import gzip
import json
from PIL import Image
from app.models.groups import GroupIcon
from app.models.user_thumbnail import UserThumbnail
from app.models.asset_thumbnail import AssetThumbnail
from app.models.place_icon import PlaceIcon
from app.models.user import User
from app.models.asset import Asset
from app.routes.thumbnailer import TakeThumbnail
from app.util import s3helper
from app.extensions import redis_controller, csrf
from config import Config
from io import BytesIO

config = Config()
ImageRoute = Blueprint('image', __name__)

def HandleResolutionCheck(
    WidthParametersName : list[ str ] = [ 'width', 'x' ],
    HeightParametersName : list[ str ] = [ 'height', 'y' ],
    AllowedWidths : list[ int ] = [ 48, 180, 420, 60, 100, 150, 352, 200, 500 ],
    AllowedHeights : list[ int ] = [ 48, 180, 420, 60, 100, 150, 352, 200, 500 ],
    MustBeSquare : bool = True,
    CanRoundToNearest : bool = True
) -> ( int, int ):
    """
        Handles resolution checking for images. Aborts the request if the resolution is invalid.
        Must be called in a flask request context.

        :param WidthParametersName: The names of valid parameters for width.
        :param HeightParametersName: The names of valid parameters for height.
        :param AllowedWidths: The allowed widths.
        :param AllowedHeights: The allowed heights.
        :param MustBeSquare: Whether or not the image must be square.
        :param CanRoundToNearest: Whether or not the image can round to the nearest resolution.

        :return: ( width, height )
    """
    Width : int = None
    Height : int = None
    for WidthParameterName in WidthParametersName:
        if WidthParameterName in request.args:
            Width = request.args.get( key=WidthParameterName, default=None, type=int )
            break
    for HeightParameterName in HeightParametersName:
        if HeightParameterName in request.args:
            Height = request.args.get( key=HeightParameterName, default=None, type=int )
            break
    
    
    if Width is None or Height is None:
        abort( 400 )
    if ( Width not in AllowedWidths or Height not in AllowedHeights ) and not CanRoundToNearest:
        abort( 400 )
    if MustBeSquare and Width != Height:
        abort( 400 )
    
    
    if CanRoundToNearest:
        if Width not in AllowedWidths:
            Width = min( AllowedWidths, key=lambda x:abs(x-Width) )
        if Height not in AllowedHeights:
            Height = min( AllowedHeights, key=lambda x:abs(x-Height) )
    
    return ( Width, Height )

def HandleImageResize(
    ImageContentHash : str,
    TargetWidth : int,
    TargetHeight : int,
    CroppedHash : str,
    CacheControl : str = "max-age=120",
    SkipCacheCroppedImage : bool = False,
    ReturnAsJSON : bool = False
) -> Response:
    """
        Handles image resizing

        :param ImageContentHash: The content hash of the image.
        :param TargetWidth: The target width.
        :param TargetHeight: The target height.
        :param CroppedHash: The hash of the cropped image.

        :return: Flask Response
    """
    if s3helper.DoesKeyExist(CroppedHash) and not SkipCacheCroppedImage:
        if ReturnAsJSON:
            return jsonify({
                "Final": True,
                "Url": f"{config.CDN_URL}/{CroppedHash}"
            })
        ImageResponse = make_response(redirect(f"{config.CDN_URL}/{CroppedHash}"))
        ImageResponse.headers['Cache-Control'] = CacheControl
        return ImageResponse
    
    if not s3helper.DoesKeyExist(ImageContentHash):
        if ReturnAsJSON:
            return jsonify({
                "Final": False,
                "Url": "/static/img/placeholder.png"
            })
        return redirect("/static/img/placeholder.png")
    
    ImageContent = BytesIO( s3helper.GetFileFromS3(ImageContentHash) )
    ImageObj = Image.open(ImageContent)
    ImageObj = ImageObj.resize((int(TargetWidth),int(TargetHeight))).convert('RGBA')
    
    VirtualFile = BytesIO()
    ImageObj.save(VirtualFile, "PNG")
    VirtualFile.seek(0)
    s3helper.UploadBytesToS3(VirtualFile.getvalue(), CroppedHash, contentType="image/png")

    if ReturnAsJSON:
        return jsonify({
            "Final": True,
            "Url": f"{config.CDN_URL}/{CroppedHash}"
        })
    ImageResponse = make_response(redirect(f"{config.CDN_URL}/{CroppedHash}"))
    ImageResponse.headers['Cache-Control'] = CacheControl
    return ImageResponse

@ImageRoute.route("/avatar-thumbnail/image", methods=["GET"])
@ImageRoute.route('/Thumbs/Avatar.ashx', methods=['GET'])
@ImageRoute.route('/thumbs/avatar.ashx', methods=['GET'])
def avatar():
    userId = request.args.get('userId', default = None, type = int)
    username = request.args.get('username', default = None, type = str)

    if (userId is None and username is None):
        return redirect("/static/img/placeholder.png")
    
    TargetX, TargetY = HandleResolutionCheck(
        WidthParametersName = [ 'x', 'width' ],
        HeightParametersName = [ 'y', 'height' ],
        AllowedWidths = [ 48, 180, 420, 60, 100, 150, 352, 200, 500 ],
        AllowedHeights = [ 48, 180, 420, 60, 100, 150, 352, 200, 500 ],
        MustBeSquare = True,
        CanRoundToNearest = True
    )

    if username is not None and userId is None:
        UserObj : User = User.query.filter_by(username=username).first()
        if UserObj is None:
            return redirect("/static/img/placeholder.png")
        userId = UserObj.id

    ThumbnailObj : UserThumbnail = UserThumbnail.query.filter_by(userid=userId).first()
    if ThumbnailObj is None:
        return redirect("/static/img/placeholder.png")
    
    ContentHash = ThumbnailObj.full_contenthash
    CroppedHash = hashlib.sha512(f"{ContentHash}-{TargetX}-{TargetY}-v3".encode('utf-8')).hexdigest()
    
    return HandleImageResize(
        ImageContentHash = ContentHash,
        TargetWidth = TargetX,
        TargetHeight = TargetY,
        CroppedHash = CroppedHash,
        CacheControl = "max-age=120"
    )

@ImageRoute.route('/avatar-thumbnail/json', methods=['GET'])
def avatar_json():
    userId = request.args.get('userId', None, type=int)
    if userId is None:
        return jsonify({
            "Final": False,
            "Url": "/static/img/placeholder.png"
        })
    
    TargetWidth, TargetHeight = HandleResolutionCheck(
        WidthParametersName = [ 'width', 'x' ],
        HeightParametersName = [ 'height', 'y' ],
        AllowedWidths = [ 48, 180, 420, 60, 100, 150, 352, 200, 500 ],
        AllowedHeights = [ 48, 180, 420, 60, 100, 150, 352, 200, 500 ],
        MustBeSquare = True,
        CanRoundToNearest = True
    )
    
    thumbnail : UserThumbnail = UserThumbnail.query.filter_by(userid=userId).first()
    if thumbnail is None:
        return jsonify({
            "Final": False,
            "Url": "/static/img/placeholder.png"
        })
    ContentHash = thumbnail.full_contenthash
    CroppedHash = hashlib.sha512(f"{ContentHash}-{TargetWidth}-{TargetHeight}-v3".encode('utf-8')).hexdigest()
    
    return HandleImageResize(
        ImageContentHash = ContentHash,
        TargetWidth = TargetWidth,
        TargetHeight = TargetHeight,
        CroppedHash = CroppedHash,
        CacheControl = "max-age=120",
        ReturnAsJSON = True
    )

@ImageRoute.route('/headshot-thumbnail/image', methods=['GET'])
@ImageRoute.route('/Thumbs/Head.ashx', methods=['GET'])
def head():
    userId = request.args.get('userId', default = None, type = int)
    if userId is None:
        return redirect("/static/img/placeholder.png")
    
    TargetX, TargetY = HandleResolutionCheck(
        WidthParametersName = [ 'x', 'width' ],
        HeightParametersName = [ 'y', 'height' ],
        AllowedWidths = [ 48, 180, 420, 60, 100, 150, 352, 200, 500 ],
        AllowedHeights = [ 48, 180, 420, 60, 100, 150, 352, 200, 500 ],
        MustBeSquare = True,
        CanRoundToNearest = True
    )

    ThumbnailObj : UserThumbnail = UserThumbnail.query.filter_by(userid=userId).first()
    if ThumbnailObj is None:
        return redirect("/static/img/placeholder.png")
    
    ContentHash = ThumbnailObj.headshot_contenthash
    CroppedHash = hashlib.sha512(f"{ContentHash}-{TargetX}-{TargetY}-v3".encode('utf-8')).hexdigest()
    
    return HandleImageResize(
        ImageContentHash = ContentHash,
        TargetWidth = TargetX,
        TargetHeight = TargetY,
        CroppedHash = CroppedHash,
        CacheControl = "max-age=120"
    )

@ImageRoute.route('/asset-thumbnail/json', methods=['GET'])
def asset_json():
    assetId = request.args.get( 'assetId',  None, type=int )
    if assetId is None:
        return jsonify({
            "Final": False,
            "Url": "/static/img/placeholder.png"
        })
    
    TargetWidth, TargetHeight = HandleResolutionCheck(
        WidthParametersName = [ 'width', 'x' ],
        HeightParametersName = [ 'height', 'y' ],
        AllowedWidths = [48,180,420,60,100,150,352,396,480,512,576,700,768,640,360,1280,720],
        AllowedHeights = [48,180,420,60,100,150,352,396,480,512,576,700,768,640,360,1280,720],
        MustBeSquare = False,
        CanRoundToNearest = True
    )

    thumbnailObj : AssetThumbnail = AssetThumbnail.query.filter_by(asset_id=assetId).order_by(AssetThumbnail.asset_version_id.desc()).first()
    if thumbnailObj is None:
        return jsonify({
            "Final": False,
            "Url": "/static/img/placeholder.png"
        })
    if thumbnailObj.moderation_status != 0 or thumbnailObj.asset.moderation_status != 0:
        if thumbnailObj.moderation_status == 2 or thumbnailObj.asset.moderation_status == 2:
            return jsonify({
                "Final": True,
                "Url": "/static/img/ContentDeleted.png"
            })
        return jsonify({
            "Final": False,
            "Url": "/static/img/placeholder.png"
        })
    
    ContentHash = thumbnailObj.content_hash
    CroppedHash = hashlib.sha512(f"{ContentHash}-{TargetWidth}-{TargetHeight}-v3".encode('utf-8')).hexdigest()
    
    return HandleImageResize(
        ImageContentHash = ContentHash,
        TargetWidth = TargetWidth,
        TargetHeight = TargetHeight,
        CroppedHash = CroppedHash,
        CacheControl = "max-age=120",
        ReturnAsJSON = True
    )

@ImageRoute.route('/asset-thumbnail/image', methods=['GET'])
@ImageRoute.route('/thumbs/asset.ashx', methods=['GET'])
@ImageRoute.route('/Thumbs/Asset.ashx', methods=['GET'])
def asset():
    assetId = request.args.get('assetId', default = None, type = int) or request.args.get('assetid', default = None, type = int)
    if assetId is None:
        return redirect("/static/img/placeholder.png")
    
    TargetX, TargetY = HandleResolutionCheck(
        WidthParametersName = [ 'x', 'width' ],
        HeightParametersName = [ 'y', 'height' ],
        AllowedWidths = [48,180,420,60,100,150,352,396,480,512,576,700,768,640,360,1280,720],
        AllowedHeights = [48,180,420,60,100,150,352,396,480,512,576,700,768,640,36,1280,720],
        MustBeSquare = False,
        CanRoundToNearest = True
    )

    ThumbnailObj : AssetThumbnail = AssetThumbnail.query.filter_by(asset_id=assetId).order_by(AssetThumbnail.asset_version_id.desc()).first()
    if ThumbnailObj is None:
        return redirect("/static/img/placeholder.png")
    if ThumbnailObj.moderation_status != 0 or ThumbnailObj.asset.moderation_status != 0:
        if ThumbnailObj.moderation_status == 2 or ThumbnailObj.asset.moderation_status == 2:
            return redirect("/static/img/ContentDeleted.png")
        return redirect("/static/img/placeholder.png")
    ContentHash = ThumbnailObj.content_hash
    CroppedHash = hashlib.sha512(f"{ContentHash}-{TargetX}-{TargetY}-v3".encode('utf-8')).hexdigest()
    
    return HandleImageResize(
        ImageContentHash = ContentHash,
        TargetWidth = TargetX,
        TargetHeight = TargetY,
        CroppedHash = CroppedHash,
        CacheControl = "max-age=120"
    )

@ImageRoute.route('/Thumbs/GroupIcon.ashx', methods=['GET'])
def groupicon():
    groupid = request.args.get( key='groupid', default=None, type=int )
    if groupid is None:
        return redirect("/static/img/placeholder.png")
    
    TargetX, TargetY = HandleResolutionCheck(
        WidthParametersName = [ 'x', 'width' ],
        HeightParametersName = [ 'y', 'height' ],
        AllowedWidths = [48,180,420,60,100,150,352],
        AllowedHeights = [48,180,420,60,100,150,352],
        MustBeSquare = True,
        CanRoundToNearest = True
    )

    ThumbnailObj : GroupIcon = GroupIcon.query.filter_by(group_id=groupid).first()
    if ThumbnailObj is None:
        return redirect("/static/img/placeholder.png")
    if ThumbnailObj.moderation_status != 0:
        if ThumbnailObj.moderation_status == 2:
            return redirect("/static/img/ContentDeleted.png")
        return redirect("/static/img/placeholder.png")
    
    ContentHash = ThumbnailObj.content_hash
    CroppedHash = hashlib.sha512(f"{ContentHash}-{TargetX}-{TargetY}-v3".encode('utf-8')).hexdigest()
    
    return HandleImageResize(
        ImageContentHash = ContentHash,
        TargetWidth = TargetX,
        TargetHeight = TargetY,
        CroppedHash = CroppedHash,
        CacheControl = "max-age=120"
    )

@ImageRoute.route('/Thumbs/GameIcon.ashx', methods=['GET'])
@ImageRoute.route('/Thumbs/PlaceIcon.ashx', methods=['GET'])
def placeicon():
    assetId = request.args.get('assetId', default = None, type = int) or request.args.get('assetid', default = None, type = int)
    if assetId is None:
        return redirect("/static/img/placeholder.png")
    
    TargetX, TargetY = HandleResolutionCheck(
        WidthParametersName = [ 'x', 'width' ],
        HeightParametersName = [ 'y', 'height' ],
        AllowedWidths = [48,180,420,60,100,150,352,324,576],
        AllowedHeights = [48,180,420,60,100,150,352,324,576],
        MustBeSquare = False,
        CanRoundToNearest = True
    )

    PlaceIconObj : PlaceIcon = PlaceIcon.query.filter_by(placeid=assetId).first()
    if PlaceIconObj is None:
        return redirect("/static/img/placeholder.png")
    if PlaceIconObj.moderation_status != 0 or PlaceIconObj.asset.moderation_status != 0:
        if PlaceIconObj.moderation_status == 2 or PlaceIconObj.asset.moderation_status == 2:
            return redirect("/static/img/ContentDeleted.png")
        return redirect("/static/img/placeholder.png")

    ContentHash = PlaceIconObj.contenthash
    CroppedHash = hashlib.sha512(f"{ContentHash}-{TargetX}-{TargetY}-v3".encode('utf-8')).hexdigest()
    
    return HandleImageResize(
        ImageContentHash = ContentHash,
        TargetWidth = TargetX,
        TargetHeight = TargetY,
        CroppedHash = CroppedHash,
        CacheControl = "max-age=120"
    )

@ImageRoute.route('/Game/Tools/ThumbnailAsset.ashx', methods=['GET'])
def thumbnail_asset():
    ExpectedFormat : str = request.args.get( key='fmt', default='png', type=str )
    AssetId : int = request.args.get( key='aid', default=None, type=int )

    if AssetId is None:
        return redirect("/static/img/placeholder.png")
    if ExpectedFormat.lower() != "png":
        return redirect("/static/img/placeholder.png")

    AssetObj : Asset = Asset.query.filter_by(id=AssetId).first()
    if AssetObj is None:
        return redirect("/static/img/placeholder.png")
    
    TargetWidth, TargetHeight = HandleResolutionCheck(
        WidthParametersName = [ 'wd', 'width' ],
        HeightParametersName = [ 'ht', 'height' ],
        AllowedWidths = [48,180,420,60,100,150,352,75],
        AllowedHeights = [48,180,420,60,100,150,352,75],
        MustBeSquare = True,
        CanRoundToNearest = True
    )
    
    thumbnail : AssetThumbnail = AssetThumbnail.query.filter_by(asset_id=AssetId).order_by(AssetThumbnail.asset_version_id.desc()).first()
    if thumbnail is None:
        TakeThumbnail(AssetId)
        return redirect("/static/img/placeholder.png")
    if thumbnail.moderation_status != 0:
        if thumbnail.moderation_status == 2 or thumbnail.asset.moderation_status == 2:
            return redirect("/static/img/ContentDeleted.png")
        return redirect("/static/img/placeholder.png")
    
    ContentHash = thumbnail.content_hash
    CroppedHash = hashlib.sha512(f"{ContentHash}-{TargetWidth}-{TargetHeight}-v3".encode('utf-8')).hexdigest()
    
    return HandleImageResize(
        ImageContentHash = ContentHash,
        TargetWidth = TargetWidth,
        TargetHeight = TargetHeight,
        CroppedHash = CroppedHash,
        CacheControl = "max-age=120"
    )

import urllib.parse

@ImageRoute.route("/v1/batch", methods=["POST"])
@csrf.exempt
def BatchImageRequest():
    if request.headers.get("Content-Encoding") == "gzip":
        try:
            data = gzip.decompress(request.data)
        except Exception as e:
            return jsonify({"success": False, "message": "Invalid gzip data"}), 400
        try:
            JSONData = json.loads(data)
        except Exception as e:
            return jsonify({"success": False, "message": "Invalid JSON data"}), 400
    else:
        JSONData = request.json
    if JSONData is None:
        return jsonify({"success": False, "message": "Missing JSON data"}), 400

    # [{'requestId': 'type=GameIcon&id=1&w=128&h=128&filters=', 'targetId': 1, 'type': 'GameIcon', 'size': '128x128', 'isCircular': False}]

    if len(JSONData) > 15:
        return jsonify({"success": False, "message": "Too many requests"}), 400
    if len(JSONData) == 0:
        return jsonify({"data":[]}), 200
    
    ProcessedRequests = []
    for RequestObj in JSONData:
        if "requestId" not in RequestObj or "targetId" not in RequestObj or "type" not in RequestObj or "size" not in RequestObj:
            continue
        if RequestObj["type"] not in [ "Avatar", "AvatarHeadShot", "GameIcon", "GameThumbnail", "Asset", "GroupIcon"]:
            continue

        if "x" not in RequestObj["size"]:
            continue
        SplittedSize = RequestObj["size"].split("x")
        if len(SplittedSize) != 2:
            continue
        try:
            TargetWidth = int(SplittedSize[0])
            TargetHeight = int(SplittedSize[1])
        except:
            continue

        AllowedSizes = [48,180,420,60,100,150,352,396,480,512,576,700,768,640,36,1280,720]
        TargetWidth = min(AllowedSizes, key=lambda x:abs(x-TargetWidth))
        TargetHeight = min(AllowedSizes, key=lambda x:abs(x-TargetHeight))

        RequestType = RequestObj["type"]

        if RequestType == "Avatar":
            ThumbnailObj : UserThumbnail = UserThumbnail.query.filter_by(userid=RequestObj["targetId"]).first()
            if ThumbnailObj is None:
                continue
            ContentHash = ThumbnailObj.full_contenthash
        elif RequestType == "AvatarHeadShot":
            ThumbnailObj : UserThumbnail = UserThumbnail.query.filter_by(userid=RequestObj["targetId"]).first()
            if ThumbnailObj is None:
                continue
            ContentHash = ThumbnailObj.headshot_contenthash
        elif RequestType == "GameIcon":
            PlaceIconObj : PlaceIcon = PlaceIcon.query.filter_by(placeid=RequestObj["targetId"]).first()
            if PlaceIconObj is None:
                continue
            ContentHash = PlaceIconObj.contenthash
        elif RequestType == "GameThumbnail" or RequestType == "Asset":
            thumbnailObj : AssetThumbnail = AssetThumbnail.query.filter_by(asset_id=RequestObj["targetId"]).order_by(AssetThumbnail.asset_version_id.desc()).first()
            if thumbnailObj is None:
                continue
            if thumbnailObj.moderation_status != 0:
                continue
            ContentHash = thumbnailObj.content_hash
        elif RequestType == "GroupIcon":
            ThumbnailObj : GroupIcon = GroupIcon.query.filter_by(group_id=RequestObj["targetId"]).first()
            if ThumbnailObj is None:
                continue
            if ThumbnailObj.moderation_status != 0:
                continue
            ContentHash = ThumbnailObj.content_hash
        else:
            continue
        CroppedHash = hashlib.sha512(f"{ContentHash}-{TargetWidth}-{TargetHeight}-v3".encode('utf-8')).hexdigest()
        if not s3helper.DoesKeyExist(CroppedHash):
            if not s3helper.DoesKeyExist(ContentHash):
                continue
            ImageContent = BytesIO( s3helper.GetFileFromS3(ContentHash) )
            ImageObj = Image.open(ImageContent)
            ImageObj = ImageObj.resize((int(TargetWidth),int(TargetHeight))).convert('RGBA')
            
            VirtualFile = BytesIO()
            ImageObj.save(VirtualFile, "PNG")
            VirtualFile.seek(0)
            s3helper.UploadBytesToS3(VirtualFile.getvalue(), CroppedHash, contentType="image/png")
        ProcessedRequests.append({
            "requestId": RequestObj["requestId"],
            "targetId": RequestObj["targetId"],
            "state": "Completed",
            "imageUrl": f"{config.CDN_URL}/{CroppedHash}",
            "version": None
        })

    return jsonify({
        "data": ProcessedRequests
    })

@ImageRoute.route("/v1/users/avatar-headshot", methods=["GET"])
def multi_avatar_headshot():
    userIdsCSV = request.args.get('userIds', default = None, type = str)
    if userIdsCSV is None:
        return jsonify( { "errors": [ { "code": 4, "message": "The requested Ids are invalid, of an invalid type or missing." } ] } ), 400
    
    userIds = userIdsCSV.split(",")
    if len(userIds) > 100:
        return jsonify( { "errors": [ { "code": 1, "message": "There are too many requested Ids." } ] } ), 400
    
    requestedSize = request.args.get('size', default = "48x48", type = str)

    if "x" not in requestedSize:
        return jsonify( { "errors": [ { "code": 3, "message": "The requested size is invalid. Please see documentation for valid thumbnail size parameter name and format." } ] } ), 400
    
    SplittedSize = requestedSize.split("x")
    if len(SplittedSize) != 2:
        return jsonify( { "errors": [ { "code": 3, "message": "The requested size is invalid. Please see documentation for valid thumbnail size parameter name and format." } ] } ), 400
    
    try:
        TargetWidth = int(SplittedSize[0])
        TargetHeight = int(SplittedSize[1])

        AllowedSizes = [48,180,420,60,100,150,352,396,480,512,576,700,768,640,36,1280,720]
        TargetWidth = min(AllowedSizes, key=lambda x:abs(x-TargetWidth))
        TargetHeight = min(AllowedSizes, key=lambda x:abs(x-TargetHeight))
    except:
        return jsonify( { "errors": [ { "code": 3, "message": "The requested size is invalid. Please see documentation for valid thumbnail size parameter name and format." } ] } ), 400

    ProcessedRequests = []
    for userId in userIds:
        try:
            userId = int(userId)
        except:
            continue
        ThumbnailObj : UserThumbnail = UserThumbnail.query.filter_by(userid=userId).first()
        if ThumbnailObj is None:
            continue
        ContentHash = ThumbnailObj.headshot_contenthash
        CroppedHash = hashlib.sha512(f"{ContentHash}-{TargetWidth}-{TargetHeight}-v3".encode('utf-8')).hexdigest()
        if not s3helper.DoesKeyExist(CroppedHash):
            if not s3helper.DoesKeyExist(ContentHash):
                continue
            ImageContent = BytesIO( s3helper.GetFileFromS3(ContentHash) )
            ImageObj = Image.open(ImageContent)
            ImageObj = ImageObj.resize((int(TargetWidth),int(TargetHeight))).convert('RGBA')
            
            VirtualFile = BytesIO()
            ImageObj.save(VirtualFile, "PNG")
            VirtualFile.seek(0)
            s3helper.UploadBytesToS3(VirtualFile.getvalue(), CroppedHash, contentType="image/png")
        ProcessedRequests.append({
            "targetId": userId,
            "state": "Completed",
            #"imageUrl": f"{config.CDN_URL}/{CroppedHash}", # 2020 Does not allow things like cdn.syntax.eco to be used directly in the "Texture" property so we have to redirect them to an allowed route on the www. domain
            "imageUrl": f"{config.BaseURL}/headshot-thumbnail/image?userId={userId}&x={TargetWidth}&y={TargetHeight}",
            "version": "1"
        })

    return jsonify({
        "data": ProcessedRequests
    }), 200

@ImageRoute.route("/v1/games/icons", methods=["GET"])
def get_game_icons():
    universeIdsCSV = request.args.get('universeIds', default = None, type = str)
    if universeIdsCSV is None:
        return jsonify( { "errors": [ { "code": 4, "message": "The requested Ids are invalid, of an invalid type or missing." } ] } ), 400
    universeIdsList = universeIdsCSV.split(",")
    if len(universeIdsList) > 100:
        return jsonify( { "errors": [ { "code": 1, "message": "There are too many requested Ids." } ] } ), 400
    
    requestedSize = request.args.get('size', default = "50x50", type = str)
    if "x" not in requestedSize:
        return jsonify( { "errors": [ { "code": 3, "message": "The requested size is invalid. Please see documentation for valid thumbnail size parameter name and format." } ] } ), 400
    
    SplittedSize = requestedSize.split("x")
    if len(SplittedSize) != 2:
        return jsonify( { "errors": [ { "code": 3, "message": "The requested size is invalid. Please see documentation for valid thumbnail size parameter name and format." } ] } ), 400
    
    try:
        TargetWidth = int(SplittedSize[0])
        TargetHeight = int(SplittedSize[1])

        AllowedSizes = [50, 128, 150, 256, 420, 512]
        TargetWidth = min(AllowedSizes, key=lambda x:abs(x-TargetWidth))
        TargetHeight = min(AllowedSizes, key=lambda x:abs(x-TargetHeight))
    except:
        return jsonify( { "errors": [ { "code": 3, "message": "The requested size is invalid. Please see documentation for valid thumbnail size parameter name and format." } ] } ), 400
    
    ProcessedRequests = []
    for universeId in universeIdsList:
        try:
            universeId = int(universeId)
        except:
            continue
        PlaceIconObj : PlaceIcon = PlaceIcon.query.filter_by(placeid=universeId).first()
        if PlaceIconObj is None:
            continue
        ContentHash = PlaceIconObj.contenthash
        CroppedHash = hashlib.sha512(f"{ContentHash}-{TargetWidth}-{TargetHeight}-v3".encode('utf-8')).hexdigest()
        if not s3helper.DoesKeyExist(CroppedHash):
            if not s3helper.DoesKeyExist(ContentHash):
                continue
            ImageContent = BytesIO( s3helper.GetFileFromS3(ContentHash) )
            ImageObj = Image.open(ImageContent)
            ImageObj = ImageObj.resize((int(TargetWidth),int(TargetHeight))).convert('RGBA')
            
            VirtualFile = BytesIO()
            ImageObj.save(VirtualFile, "PNG")
            VirtualFile.seek(0)
            s3helper.UploadBytesToS3(VirtualFile.getvalue(), CroppedHash, contentType="image/png")
        ProcessedRequests.append({
            "targetId": universeId,
            "state": "Completed",
            #"imageUrl": f"{config.CDN_URL}/{CroppedHash}"
            "imageUrl": f"{config.BaseURL}/Thumbs/GameIcon.ashx?assetId={str(universeId)}&x={str(TargetWidth)}&y={str(TargetHeight)}"
        })

    return jsonify({
        "data": ProcessedRequests
    }), 200