from flask import Blueprint, render_template, request, redirect, url_for, flash, make_response, jsonify, abort, Response
from app.extensions import csrf, redis_controller, get_remote_address
from app.models.user import User
from app.models.place import Place
from app.models.asset import Asset
from app.models.asset_version import AssetVersion
from app.models.gameservers import GameServer
from app.models.universe import Universe
from app.models.groups import Group, GroupRole, GroupRolePermission
from app.enums.PlaceRigChoice import PlaceRigChoice
from app.enums.PlaceYear import PlaceYear
from app.util.assetversion import GetLatestAssetVersion
from app.util.textfilter import FilterText
from app.util import auth
from app.services import economy, groups
from app.routes.asset import GenerateTempAuthToken
from config import Config
from app.util.filterconfig import ReplaceWith
import logging
import datetime
import requests

config = Config()

ClientInfo = Blueprint('clientinfo', __name__, url_prefix='/')

@ClientInfo.route('/GetAllowedSecurityVersions/', methods=['GET'])
def GetAllowedSecurityVersions():
    return jsonify({
        "data": ["0.271.1pcplayer", "0.205.0pcplayer", "0.360.0pcplayer", "0.450.0pcplayer", "0.463.0pcplayer"]
    })

@ClientInfo.route('/apisite/clientsettings/Setting/QuietGet/EmscriptenAppSettings/', methods=['GET', 'OPTIONS'])
def EmscriptenAppSettings():
    return jsonify({"DFFlagUseStarterPlayerCharacterScripts":"True","DFFlagUseStarterPlayerHumanoid":"True","DFFlagUseStarterPlayerCharacter":"True","FFlagEnableSetCoreTopbarEnabled":"True","FFlagPlayerDropDownEnabled":"True","DFFlagFilteringEnabledDialogFix":"True","DFFlagGoodbyeChoiceActiveProperty":"True","FFlagUseCanManageApiToDetermineConsoleAccess":"False","FFlagConsoleCodeExecutionEnabled":"True","FFlagLuaBasedBubbleChat":"True","FFlagUseInGameTopBar":"True","FFlagSetCoreMoveChat":"True","FFlagSetCoreDisableChatBar":"True","FFlagEnableLuaFollowers":"False","DFFlagUserServerFollowers":"True","FFlagUseUserListMenu":"True"})

@ClientInfo.route("/GetAllowedMD5Hashes/", methods=['GET'])
def GetAllowedMD5Hashes():
    apiKey = request.args.get("apiKey", default=None, type=str)
    if apiKey == "dac86da7-a4bc-4bff-8ca4-8b54e1ac925b": # 2021
        return jsonify({
            "data": [
                "aa",# 2020
                "a",
                "89ae5307ca8bcd4059fa8b61399b6dba" # 2021
            ]
        }), 200
    
    return jsonify({
        "data": [
            # 2016
            "7c4caa74baf2100a2e2b2ba34eb9d69b",
            "aaa",
            # 2018,
            "cb625aa02e932bdeb11ac84001ed5475"
            ]
    })

@ClientInfo.route('/game/players/<userid>/', methods=['GET'])
def GetPlayerInfo(userid):
    return jsonify({
        "ChatFilter": "whitelist"
    })
    
@ClientInfo.route("/Game/ChatFilter.ashx", methods=["POST"])
@csrf.exempt
def ChatFilterAshx():
    try:
        RequestedFilterText = request.data.decode("utf-8")
    except:
        RequestedFilterText = str(request.data)

    try:
        FilterText(
            Text = RequestedFilterText,
            ThrowException = True
        )
        return "True"
    except:
        return "False"

@ClientInfo.route("/moderation/filtertext/", methods=['POST'])
@csrf.exempt
def FilterTextAPI():
    userId = request.form.get('userId', '')
    RequestedFilterText = request.form.get("text", default="", type=str)
    FilteredText = FilterText(Text=RequestedFilterText, ThrowException=False, userId=userId)
    return jsonify({
        "data": {
            "white": FilteredText,
            "black": ""
        }
    })

@ClientInfo.route("/v2/moderation/textfilter", methods=['GET', 'POST'])
@ClientInfo.route("/moderation/v2/filtertext", methods=['GET', 'POST'])
@ClientInfo.route("/moderation/v2/filtertext/", methods=['GET', 'POST'])
@csrf.exempt
def FilterTextV2API():
    if request.method == 'GET':
        RequestedFilterText = request.args.get("text", default="", type=str)
        userId = request.args.get("userId", default="", type=str)
    else:
        RequestedFilterText = request.form.get("text", default="", type=str)
        userId = request.form.get("userId", default="", type=str)
    FilteredText = FilterText(Text=RequestedFilterText, ThrowException=False, userId=userId)
    return jsonify({"success": True, "message": "", "data": {"AgeUnder13": FilteredText, "Age13OrOver": FilteredText}})

@ClientInfo.route("/users/<userid>/canmanage/<placeid>", methods=['GET'])
def CanManage(userid, placeid):
    userid = int(userid)

    if userid in config.CAN_MANAGE:
        return jsonify({
            "CanManage": True,
            "Success": True
        })
    AssetObj : Asset = Asset.query.filter_by(id=placeid).first()
    if AssetObj is None:
        return jsonify({
            "CanManage": False,
            "Success": True
        })
    if AssetObj.creator_type == 0 and AssetObj.creator_id == int(userid):
        return jsonify({
            "CanManage": True,
            "Success": True
        })
    elif AssetObj.creator_type == 1:
        GroupObj : Group = Group.query.filter_by(id=AssetObj.creator_id).first()
        UserGroupRole : GroupRole = groups.GetUserRolesetInGroup(userid, GroupObj)
        if UserGroupRole is not None:
            UserRolePermissions : GroupRolePermission = groups.GetRolesetPermission( UserGroupRole )
            if UserRolePermissions.manage_group_games:
                return jsonify({
                    "CanManage": True,
                    "Success": True
                })
    return jsonify({
        "CanManage": False,
        "Success": True
    })

@ClientInfo.route("/v1/user/<userid>/is-admin-developer-console-enabled", methods=['GET'])
def IsAdminDeveloperConsoleEnabled(userid):
    is_admin = userid in {"1", "4", "94"}
    return jsonify({
        "isAdminDeveloperConsoleEnabled": is_admin
    })
    
@ClientInfo.route('/game/report-stats', methods=['POST'])
@csrf.exempt
def reportgamestats():
    # TODO: Implement this
    return jsonify({"status": "success"}),200

@ClientInfo.route("/userblock/getblockedusers", methods=['GET'])
def GetBlockedUsers():
    # TODO: Implement this
    return jsonify({
        "success": True,
        "userList": [],
        "total": 0
    })

@ClientInfo.route("/user/multi-following-exists", methods=['POST'])
@csrf.exempt
def MultiFollowingExists():
    # TODO: Implement this
    return jsonify({
        "followings": [],
    })

from app.util.signscript import signUTF8
@ClientInfo.route("/game/visit.ashx", methods=['GET'])
def Visit():
    IsPlaySolo = request.args.get('IsPlaySolo', default=1, type=int)
    UserID = request.args.get('UserID', default=5973, type=int) # default == guest
    PlaceID = request.args.get('PlaceID', default=0, type=int)
    UniverseId = request.args.get('UniverseId', default=0, type=int)

    with open("./app/files/Visit.lua", "r") as f:
        VisitLua = f.read()
    VisitLua = VisitLua.format(
        PlaceId = str(PlaceID),
        UniverseId = str(UniverseId),
        UserId = str(UserID)
    )
    VisitLua = signUTF8(VisitLua)
    Resposne = make_response(VisitLua)
    Resposne.headers['Content-Type'] = 'text/plain'
    return Resposne

@ClientInfo.route("/Error/Dmp.ashx", methods=['POST'])
@csrf.exempt
def DmpAshx():
        return ""
@ClientInfo.route("/Error/Grid.ashx", methods=['POST'])
@csrf.exempt
def ErrorGrid():
    return "OK"

@ClientInfo.route("/game/load-place-info", methods=['GET', 'POST'])
@csrf.exempt
def load_place_info():
    placeId = request.headers.get("Roblox-Place-Id", default=1, type=int)
    AssetObj : Asset = Asset.query.filter_by(id=placeId).first()
    if AssetObj is None:
        return jsonify({
            "success": False,
            "message": "Place not found",
        }), 404
    if AssetObj.asset_type.value != 9:
        return jsonify({
            "success": False,
            "message": "Place not found",
        }), 404
    LatestAssetVersion : AssetVersion = GetLatestAssetVersion(AssetObj)
    return jsonify({
        "CreatorId": AssetObj.creator_id,
        "CreatorType": "User" if AssetObj.creator_type == 0 else "Group",
        "PlaceVersion": LatestAssetVersion.version,
        "GameId": placeId,
        "IsRobloxPlace": True if AssetObj.creator_id == 1 and AssetObj.creator_type == 0 else False
    })

@ClientInfo.route("/v1/player-policies-client", methods=["POST", "GET"])
def playerpolicies():
    return jsonify({
        "isSubjectToChinaPolicies":False,
        "arePaidRandomItemsRestricted":False,
        "isPaidItemTradingAllowed":True,
        "allowedExternalLinkReferences":[
            "Discord",
            "YouTube",
            "Twitch",
            "Facebook"
        ]
    })

EnumTogameAvatarType = {
    PlaceRigChoice.UserChoice: "PlayerChoice",
    PlaceRigChoice.ForceR6: "MorphToR6",
    PlaceRigChoice.ForceR15: "MorphToR15"
}

@ClientInfo.route("/v1.1/game-start-info/")
def game_start_info():
    universeId = request.args.get("universeId", default=None, type=int)
    if universeId is None:
        return jsonify({
            "message": "Invalid request",
            "success": False
        }), 400
    UniverseObj : Universe = Universe.query.filter_by(id = universeId).first()
    if UniverseObj is None:
        return jsonify({
            "message": "Place not found",
            "success": False
        }), 404

    return jsonify({
        "gameAvatarType": "PlayerChoice", #EnumTogameAvatarType[UniverseObj.place_rig_choice], We let avatar-fetch handle this since we allow for place specific avatar types
        "allowCustomAnimations":"True",
        "universeAvatarCollisionType":"OuterBox",
        "universeAvatarBodyType":"Standard",
        "jointPositioningType":"ArtistIntent",
        "message":"",
        "universeAvatarMinScales":{
            "height":0.9,
            "width":0.7,
            "head":0.95,
            "depth":0.0,
            "proportion":0.0,
            "bodyType":0.0
        },
        "universeAvatarMaxScales":{
            "height":1.05,
            "width":1.0,
            "head":1.0,
            "depth":0.0,
            "proportion":1.0,
            "bodyType":1.0
        },
        "universeAvatarAssetOverrides":[],
        "moderationStatus":None
    })

@ClientInfo.route("/v1/name-description/games/<int:placeid>", methods=["GET"])
def name_description(placeid):
    PlaceObj : Place = Place.query.filter_by(placeid=placeid).first()
    if PlaceObj is None:
        return jsonify({
            "message": "Place not found",
            "success": False
        }), 404
    AssetObj : Asset = PlaceObj.assetObj
    return jsonify({
        "data":[
            {
                "name":AssetObj.name,
                "description":AssetObj.description,
                "languageCode":"en"
            }
        ]
    })

@ClientInfo.route("/v1/locales", methods=["GET"])
def client_locales():
    return jsonify({"data":[{"locale":{"id":1,"locale":"en_us","name":"English(US)","nativeName":"English","language":{"id":41,"name":"English","nativeName":"English","languageCode":"en"}},"isEnabledForFullExperience":True,"isEnabledForSignupAndLogin":True,"isEnabledForInGameUgc":True},{"locale":{"id":2,"locale":"es_es","name":"Spanish(Spain)","nativeName":"Español","language":{"id":148,"name":"Spanish","nativeName":"Español","languageCode":"es"}},"isEnabledForFullExperience":True,"isEnabledForSignupAndLogin":True,"isEnabledForInGameUgc":True},{"locale":{"id":3,"locale":"fr_fr","name":"French","nativeName":"Français","language":{"id":48,"name":"French","nativeName":"Français","languageCode":"fr"}},"isEnabledForFullExperience":True,"isEnabledForSignupAndLogin":True,"isEnabledForInGameUgc":True},{"locale":{"id":4,"locale":"id_id","name":"Indonesian","nativeName":"Bahasa Indonesia","language":{"id":64,"name":"Indonesian","nativeName":"Bahasa Indonesia","languageCode":"id"}},"isEnabledForFullExperience":False,"isEnabledForSignupAndLogin":False,"isEnabledForInGameUgc":True},{"locale":{"id":5,"locale":"it_it","name":"Italian","nativeName":"Italiano","language":{"id":71,"name":"Italian","nativeName":"Italiano","languageCode":"it"}},"isEnabledForFullExperience":False,"isEnabledForSignupAndLogin":False,"isEnabledForInGameUgc":True},{"locale":{"id":6,"locale":"ja_jp","name":"Japanese","nativeName":"日本語","language":{"id":73,"name":"Japanese","nativeName":"日本語 (にほんご),","languageCode":"ja"}},"isEnabledForFullExperience":True,"isEnabledForSignupAndLogin":True,"isEnabledForInGameUgc":True},{"locale":{"id":7,"locale":"ko_kr","name":"Korean","nativeName":"한국어","language":{"id":86,"name":"Korean","nativeName":"한국어","languageCode":"ko"}},"isEnabledForFullExperience":True,"isEnabledForSignupAndLogin":True,"isEnabledForInGameUgc":True},{"locale":{"id":8,"locale":"ru_ru","name":"Russian","nativeName":"Русский","language":{"id":133,"name":"Russian","nativeName":"Русский","languageCode":"ru"}},"isEnabledForFullExperience":False,"isEnabledForSignupAndLogin":False,"isEnabledForInGameUgc":True},{"locale":{"id":9,"locale":"th_th","name":"Thai","nativeName":"ภาษาไทย","language":{"id":156,"name":"Thai","nativeName":"ไทย","languageCode":"th"}},"isEnabledForFullExperience":False,"isEnabledForSignupAndLogin":False,"isEnabledForInGameUgc":True},{"locale":{"id":10,"locale":"tr_tr","name":"Turkish","nativeName":"Türkçe","language":{"id":163,"name":"Turkish","nativeName":"Türkçe","languageCode":"tr"}},"isEnabledForFullExperience":False,"isEnabledForSignupAndLogin":False,"isEnabledForInGameUgc":True},{"locale":{"id":11,"locale":"vi_vn","name":"Vietnamese","nativeName":"Tiếng Việt","language":{"id":173,"name":"Vietnamese","nativeName":"Tiếng Việt","languageCode":"vi"}},"isEnabledForFullExperience":False,"isEnabledForSignupAndLogin":False,"isEnabledForInGameUgc":True},{"locale":{"id":12,"locale":"pt_br","name":"Portuguese (Brazil)","nativeName":"Português (Brasil)","language":{"id":128,"name":"Portuguese","nativeName":"Português","languageCode":"pt"}},"isEnabledForFullExperience":True,"isEnabledForSignupAndLogin":True,"isEnabledForInGameUgc":True},{"locale":{"id":13,"locale":"de_de","name":"German","nativeName":"Deutsch","language":{"id":52,"name":"German","nativeName":"Deutsch","languageCode":"de"}},"isEnabledForFullExperience":True,"isEnabledForSignupAndLogin":True,"isEnabledForInGameUgc":True},{"locale":{"id":14,"locale":"zh_cn","name":"Chinese (Simplified)","nativeName":"中文(简体)","language":{"id":30,"name":"Chinese (Simplified)","nativeName":"简体中文","languageCode":"zh-hans"}},"isEnabledForFullExperience":True,"isEnabledForSignupAndLogin":True,"isEnabledForInGameUgc":True},{"locale":{"id":15,"locale":"zh_tw","name":"Chinese (Traditional)","nativeName":"中文(繁體)","language":{"id":189,"name":"Chinese (Traditional)","nativeName":"繁體中文","languageCode":"zh-hant"}},"isEnabledForFullExperience":True,"isEnabledForSignupAndLogin":True,"isEnabledForInGameUgc":True},{"locale":{"id":16,"locale":"bg_bg","name":"Bulgarian","nativeName":"Български","language":{"id":24,"name":"Bulgarian","nativeName":"български език","languageCode":"bg"}},"isEnabledForFullExperience":False,"isEnabledForSignupAndLogin":False,"isEnabledForInGameUgc":True},{"locale":{"id":17,"locale":"bn_bd","name":"Bengali","nativeName":"বাংলা","language":{"id":19,"name":"Bengali","nativeName":"বাংলা","languageCode":"bn"}},"isEnabledForFullExperience":False,"isEnabledForSignupAndLogin":False,"isEnabledForInGameUgc":True},{"locale":{"id":18,"locale":"cs_cz","name":"Czech","nativeName":"Čeština","language":{"id":36,"name":"Czech","nativeName":"čeština","languageCode":"cs"}},"isEnabledForFullExperience":False,"isEnabledForSignupAndLogin":False,"isEnabledForInGameUgc":True},{"locale":{"id":19,"locale":"da_dk","name":"Danish","nativeName":"Dansk","language":{"id":37,"name":"Danish","nativeName":"dansk","languageCode":"da"}},"isEnabledForFullExperience":False,"isEnabledForSignupAndLogin":False,"isEnabledForInGameUgc":True},{"locale":{"id":20,"locale":"el_gr","name":"Greek","nativeName":"Ελληνικά","language":{"id":53,"name":"Greek","nativeName":"ελληνικά","languageCode":"el"}},"isEnabledForFullExperience":False,"isEnabledForSignupAndLogin":False,"isEnabledForInGameUgc":True},{"locale":{"id":21,"locale":"et_ee","name":"Estonian","nativeName":"Eesti","language":{"id":43,"name":"Estonian","nativeName":"eesti, eesti keel","languageCode":"et"}},"isEnabledForFullExperience":False,"isEnabledForSignupAndLogin":False,"isEnabledForInGameUgc":True},{"locale":{"id":22,"locale":"fi_fi","name":"Finnish","nativeName":"Suomi","language":{"id":47,"name":"Finnish","nativeName":"suomi","languageCode":"fi"}},"isEnabledForFullExperience":False,"isEnabledForSignupAndLogin":False,"isEnabledForInGameUgc":True},{"locale":{"id":23,"locale":"hi_in","name":"Hindi","nativeName":"हिन्दी","language":{"id":60,"name":"Hindi","nativeName":"हिन्दी, हिंदी","languageCode":"hi"}},"isEnabledForFullExperience":False,"isEnabledForSignupAndLogin":False,"isEnabledForInGameUgc":True},{"locale":{"id":24,"locale":"hr_hr","name":"Croatian","nativeName":"Hrvatski","language":{"id":35,"name":"Croatian","nativeName":"hrvatski jezik","languageCode":"hr"}},"isEnabledForFullExperience":False,"isEnabledForSignupAndLogin":False,"isEnabledForInGameUgc":True},{"locale":{"id":25,"locale":"hu_hu","name":"Hungarian","nativeName":"Magyar","language":{"id":62,"name":"Hungarian","nativeName":"magyar","languageCode":"hu"}},"isEnabledForFullExperience":False,"isEnabledForSignupAndLogin":False,"isEnabledForInGameUgc":True},{"locale":{"id":26,"locale":"ka_ge","name":"Georgian","nativeName":"ქართული","language":{"id":51,"name":"Georgian","nativeName":"ქართული","languageCode":"ka"}},"isEnabledForFullExperience":False,"isEnabledForSignupAndLogin":False,"isEnabledForInGameUgc":True},{"locale":{"id":27,"locale":"kk_kz","name":"Kazakh","nativeName":"Қазақ Тілі","language":{"id":79,"name":"Kazakh","nativeName":"қазақ тілі","languageCode":"kk"}},"isEnabledForFullExperience":False,"isEnabledForSignupAndLogin":False,"isEnabledForInGameUgc":True},{"locale":{"id":28,"locale":"km_kh","name":"Khmer","nativeName":"ភាសាខ្មែរ","language":{"id":188,"name":"Khmer","nativeName":"ភាសាខ្មែរ","languageCode":"km"}},"isEnabledForFullExperience":False,"isEnabledForSignupAndLogin":False,"isEnabledForInGameUgc":True},{"locale":{"id":29,"locale":"lt_lt","name":"Lithuanian","nativeName":"Lietuvių","language":{"id":95,"name":"Lithuanian","nativeName":"lietuvių kalba","languageCode":"lt"}},"isEnabledForFullExperience":False,"isEnabledForSignupAndLogin":False,"isEnabledForInGameUgc":True},{"locale":{"id":30,"locale":"lv_lv","name":"Latvian","nativeName":"Latviešu","language":{"id":97,"name":"Latvian","nativeName":"Latviešu Valoda","languageCode":"lv"}},"isEnabledForFullExperience":False,"isEnabledForSignupAndLogin":False,"isEnabledForInGameUgc":True},{"locale":{"id":31,"locale":"ms_my","name":"Malay","nativeName":"Bahasa Melayu","language":{"id":101,"name":"Malay","nativeName":"بهاس ملايو‎","languageCode":"ms"}},"isEnabledForFullExperience":False,"isEnabledForSignupAndLogin":False,"isEnabledForInGameUgc":True},{"locale":{"id":32,"locale":"my_mm","name":"Burmese","nativeName":"ဗမာစာ","language":{"id":25,"name":"Burmese","nativeName":"ဗမာစာ","languageCode":"my"}},"isEnabledForFullExperience":False,"isEnabledForSignupAndLogin":False,"isEnabledForInGameUgc":True},{"locale":{"id":33,"locale":"nb_no","name":"Bokmal","nativeName":"Bokmål","language":{"id":113,"name":"Bokmal","nativeName":"Norsk Bokmål","languageCode":"nb"}},"isEnabledForFullExperience":False,"isEnabledForSignupAndLogin":False,"isEnabledForInGameUgc":True},{"locale":{"id":34,"locale":"nl_nl","name":"Dutch","nativeName":"Nederlands","language":{"id":39,"name":"Dutch","nativeName":"Nederlands","languageCode":"nl"}},"isEnabledForFullExperience":False,"isEnabledForSignupAndLogin":False,"isEnabledForInGameUgc":True},{"locale":{"id":35,"locale":"fil_ph","name":"Filipino","nativeName":"Filipino","language":{"id":190,"name":"Filipino","nativeName":"Filipino","languageCode":"fil"}},"isEnabledForFullExperience":False,"isEnabledForSignupAndLogin":False,"isEnabledForInGameUgc":True},{"locale":{"id":36,"locale":"pl_pl","name":"Polish","nativeName":"Polski","language":{"id":126,"name":"Polish","nativeName":"Język Polski","languageCode":"pl"}},"isEnabledForFullExperience":False,"isEnabledForSignupAndLogin":False,"isEnabledForInGameUgc":True},{"locale":{"id":37,"locale":"ro_ro","name":"Romanian","nativeName":"Română","language":{"id":132,"name":"Romanian","nativeName":"Română","languageCode":"ro"}},"isEnabledForFullExperience":False,"isEnabledForSignupAndLogin":False,"isEnabledForInGameUgc":True},{"locale":{"id":38,"locale":"uk_ua","name":"Ukrainian","nativeName":"Yкраїньска","language":{"id":169,"name":"Ukrainian","nativeName":"Українська","languageCode":"uk"}},"isEnabledForFullExperience":False,"isEnabledForSignupAndLogin":False,"isEnabledForInGameUgc":True},{"locale":{"id":39,"locale":"si_lk","name":"Sinhala","nativeName":"සිංහල","language":{"id":143,"name":"Sinhala","nativeName":"සිංහල","languageCode":"si"}},"isEnabledForFullExperience":False,"isEnabledForSignupAndLogin":False,"isEnabledForInGameUgc":True},{"locale":{"id":40,"locale":"sk_sk","name":"Slovak","nativeName":"Slovenčina","language":{"id":144,"name":"Slovak","nativeName":"Slovenčina","languageCode":"sk"}},"isEnabledForFullExperience":False,"isEnabledForSignupAndLogin":False,"isEnabledForInGameUgc":True},{"locale":{"id":41,"locale":"sl_sl","name":"Slovenian","nativeName":"Slovenski","language":{"id":145,"name":"Slovenian","nativeName":"Slovenščina","languageCode":"sl"}},"isEnabledForFullExperience":False,"isEnabledForSignupAndLogin":False,"isEnabledForInGameUgc":True},{"locale":{"id":42,"locale":"sq_al","name":"Albanian","nativeName":"Shqipe","language":{"id":5,"name":"Albanian","nativeName":"Shqip","languageCode":"sq"}},"isEnabledForFullExperience":False,"isEnabledForSignupAndLogin":False,"isEnabledForInGameUgc":True},{"locale":{"id":43,"locale":"bs_ba","name":"Bosnian","nativeName":"Босански","language":{"id":22,"name":"Bosnian","nativeName":"bosanski jezik","languageCode":"bs"}},"isEnabledForFullExperience":False,"isEnabledForSignupAndLogin":False,"isEnabledForInGameUgc":True},{"locale":{"id":44,"locale":"sr_rs","name":"Serbian","nativeName":"Cрпски","language":{"id":140,"name":"Serbian","nativeName":"српски језик","languageCode":"sr"}},"isEnabledForFullExperience":False,"isEnabledForSignupAndLogin":False,"isEnabledForInGameUgc":True},{"locale":{"id":45,"locale":"sv_se","name":"Swedish","nativeName":"Svenska","language":{"id":152,"name":"Swedish","nativeName":"Svenska","languageCode":"sv"}},"isEnabledForFullExperience":False,"isEnabledForSignupAndLogin":False,"isEnabledForInGameUgc":True}]})

@ClientInfo.route("/v2/universes/<int:universeid>/configuration", methods=["GET"])
def GetUniverseConfig( universeid : int ):
    return jsonify({
        "allowPrivateServers": False,
        "privateServerPrice": 0,
        "id": universeid,
        "name": "MrGrey",
        "universeAvatarType": "PlayerChoice",
        "universeScaleType": "AllScales",
        "universeAnimationType": "PlayerChoice",
        "universeCollisionType": "OuterBox",
        "universeBodyType": "Standard",
        "universeJointPositioningType": "ArtistIntent",
        "isArchived": False,
        "isFriendsOnly": False,
        "genre": "All",
        "playableDevices": [
            "Computer",
            "Phone",
            "Tablet"
        ],
        "permissions": {
            "IsThirdPartyTeleportAllowed": True,
            "IsThirdPartyAssetAllowed": True,
            "IsThirdPartyPurchaseAllowed": True
        },
        "isForSale": False,
        "price": 0,
        "isStudioAccessToApisAllowed": True,
        "privacyType": "Private"
    })

@ClientInfo.route("/universal-app-configuration/v1/behaviors/app-patch/content", methods=["GET"])
def GetAppConfigContent():
    return jsonify({
        "SchemaVersion": "1",
        "CanaryUserIds": [],
        "CanaryPercentage": 0
    })
'''
@ClientInfo.route("/debu-client/im-person-ate", methods=["POST"])
@csrf.exempt
def im_person_ate():
    try:
        if not config.DEBUG_MODE:
            return abort(404)
    except:
        pass

    if not request.is_json:
        return abort(404)
    data = request.json

    try:
        assert "userId" in data, "userId not found"
        assert isinstance(data["userId"], int), "userId is not an integer"
    except AssertionError as e:
        return f"Validation failed: {str(e)}", 400
    
    req_response = make_response("OK", 200)
    req_response.headers["ImpData"] = auth.CreateToken( userid = data["userId"], expireIn = 60 * 60 * 24 * 365 )
    return req_response'''


@ClientInfo.route("/universal-app-configuration/v1/behaviors/app-policy/content", methods=["GET"])
def GetAppPolicyContent():
    return jsonify({
        "ChatConversationHeaderGroupDetails": True,
        "ChatHeaderSearch": True,
        "ChatHeaderCreateChatGroup": True,
        "ChatHeaderHomeButton": False,
        "ChatHeaderNotifications": True,
        "ChatPlayTogether": True,
        "ChatShareGameToChatFromChat": True,
        "ChatTapConversationThumbnail": True,
        "ChatViewProfileOption": True,
        "GamesDropDownList": True,
        "UseNewDropDown": False,
        "GameDetailsMorePage": True,
        "GameDetailsShowGlobalCounters": True,
        "GameDetailsPlayWithFriends": True,
        "GameDetailsSubtitle": True,
        "GameInfoList": True,
        "GameInfoListDeveloper": True,
        "GamePlaysAndRatings": True,
        "GameInfoShowBadges": True,
        "GameInfoShowCreated": True,
        "GameInfoShowGamepasses": True,
        "GameInfoShowGenre": True,
        "GameInfoShowMaxPlayers": True,
        "GameInfoShowServers": True,
        "GameInfoShowUpdated": True,
        "GameReportingDisabled": False,
        "GamePlayerCounts": True,
        "GiftCardsEnabled": False,
        "Notifications": True,
        "OfficialStoreEnabled": False,
        "RecommendedGames": True,
        "SearchBar": True,
        "MorePageType": "More",
        "AboutPageType": "About",
        "FriendFinder": True,
        "SocialLinks": True,
        "SocialGroupLinks": True,
        "EnableShareCaptureCTA": True,
        "SiteMessageBanner": True,
        "UseWidthBasedFormFactorRule": False,
        "UseHomePageWithAvatarAndPanel": False,
        "UseBottomBar": True,
        "AvatarHeaderIcon": "LuaApp/icons/ic-back",
        "AvatarEditorShowBuyRobuxOnTopBar": True,
        "HomeIcon": "LuaApp/icons/ic-roblox-close",
        "ShowYouTubeAgeAlert": False,
        "GameDetailsShareButton": True,
        "CatalogShareButton": True,
        "AccountProviderName": "",
        "InviteFromAccountProvider": False,
        "ShareToAccountProvider": False,
        "ShareToAccountProviderTimeout": 8,
        "ShowDisplayName": True,
        "GamesPageCreationCenterTitle": False,
        "ShowShareTargetGameCreator": True,
        "SearchAutoComplete": True,
        "CatalogShow3dView": True,
        "CatalogReportingDisabled": False,
        "CatalogCommunityCreations": True,
        "CatalogPremiumCategory": True,
        "CatalogPremiumContent": True,
        "ItemDetailsFullView": True,
        "UseAvatarExperienceLandingPage": True,
        "HomePageFriendSection": True,
        "HomePageProfileLink": True,
        "PurchasePromptIncludingWarning": False,
        "ShowVideoThumbnails": True,
        "VideoSharingTestContent": [],
        "SystemBarPlacement": "Bottom",
        "EnableInGameHomeIcon": False,
        "UseExternalBrowserForDisclaimerLinks": False,
        "ShowExitFullscreenToast": True,
        "ExitFullscreenToastEnabled": False,
        "UseLuobuAuthentication": False,
        "CheckUserAgreementsUpdatedOnLogin": True,
        "AddUserAgreementIdsToSignupRequest": True,
        "UseOmniRecommendation": True,
        "ShowAgeVerificationOverlayEnabled": False,
        "ShouldShowGroupsTile": True,
        "ShowVoiceUpsell": False,
        "ProfileShareEnabled": True,
        "ContactImporterEnabled": True,
        "FriendCodeQrCodeScannerEnabled": False,
        "RealNamesInDisplayNamesEnabled": False,
        "CsatSurveyRestrictTextInput": False,
        "RobloxCreatedItemsCreatedByLuobu": False,
        "GameInfoShowChatFeatures": True,
        "PlatformGroup": "Unknown",
        "UsePhoneSearchDiscoverEntry": False,
        "HomeLocalFeedItems": {
            "UserInfo": 1,
            "FriendCarousel": 2
        },
        "Routes": {
            "auth": {
            "connect": "v2/login",
            "login": "v2/login",
            "signup": "v2/signup"
            }
        },
        "PromotionalEmailsCheckboxEnabled": True,
        "PromotionalEmailsOptInByDefault": False,
        "EnablePremiumUserFeatures": True,
        "CanShowUnifiedChatUpsell": True,
        "RequireExplicitVoiceConsent": True,
        "RequireExplicitAvatarVideoConsent": True,
        "EnableVoiceReportAbuseMenu": True
    })

@ClientInfo.route("/game/studio.ashx", methods=['GET'])
def studio():
    StudioLua = open("./app/files/Studio.lua", "r").read()
    SignedStudioLua = signUTF8(StudioLua)
    Response = make_response(SignedStudioLua)
    Response.headers['Content-Type'] = 'text/plain'
    return Response

@ClientInfo.route("/game/gameserver2014.lua", methods=['GET'])
def gameserver_2014():
    RemoteAddress = get_remote_address()
    GameserverObj : GameServer = GameServer.query.filter_by( serverIP = RemoteAddress ).first()
    if GameserverObj is None:
        return jsonify({
            "success": False,
            "message": "Unauthorized"
        }), 401
    
    PlaceId = request.args.get( key = "placeId", default = None, type = int )
    NetworkPort = request.args.get( key = "networkPort", default = None, type = int )
    CreatorId = request.args.get( key = "creatorId", default = None, type = int )
    CreatorType = request.args.get( key = "creatorType", default = None, type = int )
    JobId = request.args.get( key = "jobId", default = None, type = str )

    if "UserRequest" in request.headers.get( key = "accesskey", default = "" ):
        return jsonify({
            "success": False,
            "message": "Invalid request"
        }), 400

    if PlaceId is None or NetworkPort is None or CreatorId is None or CreatorType is None or JobId is None:
        return jsonify({
            "success": False,
            "message": "Invalid request"
        }), 400
    
    PlaceObj : Place = Place.query.filter_by(placeid=PlaceId).first()
    if PlaceObj is None:
        return jsonify({
            "success": False,
            "message": "Place not found"
        }), 404
    
    UniverseObj : Universe = Universe.query.filter_by(id=PlaceObj.parent_universe_id).first()
    if UniverseObj is None:
        return jsonify({
            "success": False,
            "message": "Universe not found"
        }), 404
    
    if UniverseObj.place_year != PlaceYear.Fourteen:
        return jsonify({
            "success": False,
            "message": "Invalid request"
        }), 400
    
    #if not redis_controller.exists(f"gameserver2014lua:{PlaceId}:{JobId}"):
    #    return jsonify({
    #        "success": False,
    #        "message": "Invalid request"
    #    }), 400
    #redis_controller.delete(f"gameserver2014lua:{PlaceId}:{JobId}")

    TempPlaceAccessKey = GenerateTempAuthToken(
        AssetId = PlaceId,
        Expiration = 180,
        CreatorIP = get_remote_address()
    )
    GameserverLua = open("./app/files/2014Gameserver.lua", "r").read()
    GameserverLua = GameserverLua.format(
        PlaceId = PlaceId,
        NetworkPort = NetworkPort,
        CreatorId = CreatorId,
        CreatorType = CreatorType,
        JobId = JobId,
        AuthToken = GameserverObj.accessKey,
        TempPlaceAccessKey = TempPlaceAccessKey
    )

    SignedGameserverLua = signUTF8(GameserverLua)
    Response = make_response(SignedGameserverLua)
    Response.headers['Content-Type'] = 'text/plain'
    Response.set_cookie(
        key = "Roblox-Place-Id",
        value = str(PlaceId),
        expires = 60 * 60 * 24,
        domain = f".{config.BaseDomain}"
    )
    Response.set_cookie(
        key = "Temp-Place-Access-Key",
        value = TempPlaceAccessKey,
        expires = 180,
        domain = f".{config.BaseDomain}"
    )

    logging.info("2014Gameserver.lua request from %s for place %d", RemoteAddress, PlaceId)
    
    return Response

@ClientInfo.route("/game/gameserver2012.lua", methods=['GET'])
def gameserver_2012():
    RemoteAddress = get_remote_address()
    GameserverObj : GameServer = GameServer.query.filter_by( serverIP = RemoteAddress ).first()
    if GameserverObj is None:
        return jsonify({
            "success": False,
            "message": "Unauthorized"
        }), 401
    
    PlaceId = request.args.get( key = "placeId", default = None, type = int )
    NetworkPort = request.args.get( key = "networkPort", default = None, type = int )
    CreatorId = request.args.get( key = "creatorId", default = None, type = int )
    CreatorType = request.args.get( key = "creatorType", default = None, type = int )
    JobId = request.args.get( key = "jobId", default = None, type = str )

    if "UserRequest" in request.headers.get( key = "accesskey", default = "" ):
        return jsonify({
            "success": False,
            "message": "Invalid request"
        }), 400

    if PlaceId is None or NetworkPort is None or CreatorId is None or CreatorType is None or JobId is None:
        return jsonify({
            "success": False,
            "message": "Invalid request"
        }), 400
    
    PlaceObj : Place = Place.query.filter_by(placeid=PlaceId).first()
    if PlaceObj is None:
        return jsonify({
            "success": False,
            "message": "Place not found"
        }), 404
    
    UniverseObj : Universe = Universe.query.filter_by(id=PlaceObj.parent_universe_id).first()
    if UniverseObj is None:
        return jsonify({
            "success": False,
            "message": "Universe not found"
        }), 404
    
    if UniverseObj.place_year != PlaceYear.Fourteen:
        return jsonify({
            "success": False,
            "message": "Invalid request"
        }), 400
    
    #if not redis_controller.exists(f"gameserver2014lua:{PlaceId}:{JobId}"):
    #    return jsonify({
    #        "success": False,
    #        "message": "Invalid request"
    #    }), 400
    #redis_controller.delete(f"gameserver2014lua:{PlaceId}:{JobId}")

    TempPlaceAccessKey = GenerateTempAuthToken(
        AssetId = PlaceId,
        Expiration = 180,
        CreatorIP = get_remote_address()
    )
    GameserverLua = open("./app/files/2012Gameserver.lua", "r").read()
    GameserverLua = GameserverLua.format(
        PlaceId = PlaceId,
        NetworkPort = NetworkPort,
        CreatorId = CreatorId,
        CreatorType = CreatorType,
        JobId = JobId,
        AuthToken = GameserverObj.accessKey,
        TempPlaceAccessKey = TempPlaceAccessKey
    )

    SignedGameserverLua = signUTF8(GameserverLua)
    Response = make_response(SignedGameserverLua)
    Response.headers['Content-Type'] = 'text/plain'
    Response.set_cookie(
        key = "Roblox-Place-Id",
        value = str(PlaceId),
        expires = 60 * 60 * 24,
        domain = f".{config.BaseDomain}"
    )
    Response.set_cookie(
        key = "Temp-Place-Access-Key",
        value = TempPlaceAccessKey,
        expires = 180,
        domain = f".{config.BaseDomain}"
    )

    logging.info("2012Gameserver.lua request from %s for place %d", RemoteAddress, PlaceId)
    
    return Response

@ClientInfo.route("/game/gameserver2016.lua", methods=['GET'])
def gameserver_2016():
    RemoteAddress = get_remote_address()
    GameserverObj : GameServer = GameServer.query.filter_by( serverIP = RemoteAddress ).first()
    if GameserverObj is None:
        return jsonify({
            "success": False,
            "message": "Unauthorized"
        }), 401
    
    GameserverLua = open("./app/files/2016Gameserver.lua", "r").read()
    GameserverLua = f"-- NEXIUM 2016 Gameserver.lua | Signed on {datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n" + GameserverLua

    SignedGameserverLua = signUTF8(GameserverLua)
    Response = make_response(SignedGameserverLua)
    Response.headers['Content-Type'] = 'text/plain'

    logging.info("2016Gameserver.lua request from %s", RemoteAddress)

    return Response

@ClientInfo.route("/game/2014Join.lua", methods=['GET'])
def join_2014():
    placeId = request.args.get("placeId", default=None, type=int)
    if placeId is None:
        return jsonify({
            "success": False,
            "message": "Invalid request"
        }), 400

    JoinLua = open("./app/files/2014Join.lua", "r").read()
    JoinLua = JoinLua.format(
        PlaceId = str(placeId)
    )
    SignedJoinLua = signUTF8(JoinLua)
    Response = make_response(SignedJoinLua)
    Response.headers['Content-Type'] = 'text/plain'
    return Response

@ClientInfo.route("/game/2012Join.lua", methods=['GET'])
def join_2012():
    placeId = request.args.get("placeId", default=None, type=int)
    if placeId is None:
        return jsonify({
            "success": False,
            "message": "Invalid request"
        }), 400

    JoinLua = open("./app/files/2012Join.lua", "r").read()
    JoinLua = JoinLua.format(
        PlaceId = str(placeId)
    )
    SignedJoinLua = signUTF8(JoinLua)
    Response = make_response(SignedJoinLua)
    Response.headers['Content-Type'] = 'text/plain'
    return Response

@ClientInfo.route("/v1/users/<int:userId>/currency", methods=["GET"])
@auth.authenticated_required_api
def get_user_currency( userId : int ):
    AuthenticatedUser : User = auth.GetCurrentUser()
    if AuthenticatedUser.id != userId:
        return jsonify({"success": False, "message": "Unauthorized"}),401
    UserRobuxBalance, UserTixBalance = economy.GetUserBalance(AuthenticatedUser)
    return jsonify({
        "robux": UserRobuxBalance,
        "tickets": UserTixBalance
    }),200

#followings.roblox.com/v1/users/<int:userid>/universes/<int:universeid>/status
@ClientInfo.route("/v1/users/<int:userid>/universes/<int:universeid>/status", methods=["GET"])
@auth.authenticated_required_api
def get_user_following_universe_status( userid : int, universeid : int ):
    AuthenticatedUser : User = auth.GetCurrentUser()
    if AuthenticatedUser.id != userid:
        return jsonify({"success": False, "message": "Unauthorized"}),401
    return jsonify({
        "UniverseId": universeid,
        "UserId": userid,
        "CanFollow": False,
        "IsFollowing": False,
        "FollowingCountByType": 0,
        "FollowingLimitByType": 200
    }),200

#badges.roblox.com/v1/universes/<int:universeid>/badges
@ClientInfo.route("/v1/universes/<int:universeid>/badges", methods=["GET"])
@auth.authenticated_required_api
def get_universe_badges( universeid : int ):
    return jsonify({
        "data": [],
        "previousPageCursor": None,
        "nextPageCursor": None
    }),200
