from flask import Blueprint, render_template, request, redirect, url_for, flash, make_response, jsonify, abort, after_this_request
from app.models.placeservers import PlaceServer
from app.models.gameservers import GameServer
from app.models.place import Place
from app.models.universe import Universe
from app.models.user import User
from app.extensions import redis_controller, get_remote_address, csrf, limiter, db
from app.enums.PlaceYear import PlaceYear
from app.routes.gamejoin import CreateNewPlaceServer
import logging
import secrets
import string

TeleportServiceRoute = Blueprint('teleportServiceinternal', __name__, url_prefix='/reservedservers')

@TeleportServiceRoute.before_request
def before_request_hook():
    if "Roblox/" not in request.user_agent.string:
        logging.info(f"TeleportServiceInternal - UserAgent {request.user_agent} - Not allowed")
        abort(404)
    RemoteAddress = get_remote_address()
    GameServerObj : GameServer = GameServer.query.filter_by( serverIP = RemoteAddress ).first()
    if GameServerObj is None:
        logging.info(f"TeleportServiceInternal - {RemoteAddress} - Not found")
        abort(404)
    
    if "Roblox-Place-Id" not in request.headers:
        logging.info(f"TeleportServiceInternal - GameServer {RemoteAddress} - Roblox-Place-Id header not found")
        abort(404)

    accessKeyRequest = request.headers.get( key = "accesskey", default = "" )
    if "UserRequest" in accessKeyRequest:
        logging.warning(f"TeleportServiceInternal - GameServer {RemoteAddress} - UserRequest access key used")
        abort(404)
    
    PlaceId = request.headers.get( key = "Roblox-Place-Id", default = None, type = int )
    if PlaceId is None:
        logging.info(f"TeleportServiceInternal - GameServer {RemoteAddress} - Roblox-Place-Id header not expected type")
        abort(404)
    
    PlaceObj : Place = Place.query.filter_by( placeid = PlaceId ).first()
    if PlaceObj is None:
        logging.info(f"TeleportServiceInternal - Place {PlaceId} - Place not found")
        abort(404)

@TeleportServiceRoute.route('/create', methods=['POST'])
@csrf.exempt
def create_reserved_server():
    OriginPlaceId = request.headers.get( key = "Roblox-Place-Id", default = None, type = int )
    TargetPlaceId = request.args.get( key = "placeId", default = None, type = int )

    if OriginPlaceId is None or TargetPlaceId is None:
        logging.error(f"TeleportServiceInternal - create_reserved_server - Bad Request - OriginPlaceId {OriginPlaceId} TargetPlaceId {TargetPlaceId} - Both has to be not none")
        return "Invalid request", 400
    
    OriginPlaceObj : Place = Place.query.filter_by( placeid = OriginPlaceId ).first()
    TargetPlaceObj : Place = Place.query.filter_by( placeid = TargetPlaceId ).first()
    if OriginPlaceObj is None or TargetPlaceObj is None:
        logging.error(f"TeleportServiceInternal - create_reserved_server - Bad Request - Place {OriginPlaceId} or {TargetPlaceId} not found")
        return "Invalid request", 400

    if OriginPlaceObj.parent_universe_id != TargetPlaceObj.parent_universe_id:
        logging.error(f"TeleportServiceInternal - create_reserved_server - Bad Request - OriginPlace {OriginPlaceId} and TargetPlace {TargetPlaceId} are not in the same universe")
        return "Invalid request", 400
    
    UniverseObj : Universe = Universe.query.filter_by( id = OriginPlaceObj.parent_universe_id ).first()
    if UniverseObj is None:
        return "Invalid request", 400
    
    if UniverseObj.place_year not in [ PlaceYear.Sixteen, PlaceYear.Eighteen, PlaceYear.Twenty, PlaceYear.TwentyOne]:
        logging.error(f"TeleportServiceInternal - create_reserved_server - Bad Request - Universe {UniverseObj.id} - PlaceYear {UniverseObj.place_year} is not supported")
        return "Invalid request", 400

    CooldownKeyName = f"reserved_server_creation_cooldown:{OriginPlaceObj.parent_universe_id}"
    ReservedServerAccessCode = ''.join(secrets.choice(string.ascii_uppercase + string.digits) for _ in range(64))
    
    if not redis_controller.exists( CooldownKeyName ):
        redis_controller.setex( name = CooldownKeyName, time = 10, value = "1" )
        try:
            NewPlaceServerObj : PlaceServer = CreateNewPlaceServer(
                placeId = TargetPlaceId,
                reserved_server_access_code = ReservedServerAccessCode
            )
        except Exception as e:
            logging.error(f"TeleportServiceInternal - create_reserved_server - Place {TargetPlaceId} - Universe {UniverseObj.id} - {e}")
            return "Failed to start new server", 400
    
    redis_controller.set( f"reservedserveraccesscode_server:{ReservedServerAccessCode}", TargetPlaceObj.placeid, ex = 60 * 60 * 24 )
    
    return jsonify({
        "ReservedServerAccessCode": ReservedServerAccessCode,
        "ReservedServerGameCode": ReservedServerAccessCode
    })

@TeleportServiceRoute.route('/grantaccess', methods=['POST'])
@csrf.exempt
def grant_access_to_reserved():
    given_reservedServerAccessCode = request.args.get( key = 'reservedServerAccessCode', default = None, type = str )
    OriginPlaceId = request.headers.get( key = "Roblox-Place-Id", default = None, type = int )
    if given_reservedServerAccessCode is None or OriginPlaceId is None:
        return "Invalid request", 400
    
    OriginPlaceObj : Place = Place.query.filter_by( placeid = OriginPlaceId ).first()
    if OriginPlaceObj is None:
        return "Invalid request", 400
    
    TargetPlaceServerObj : PlaceServer = PlaceServer.query.filter_by( reservedServerAccessCode = given_reservedServerAccessCode ).first()
    if TargetPlaceServerObj is None:
        accessCodePlaceIdOrigin = redis_controller.get( f"reservedserveraccesscode_server:{given_reservedServerAccessCode}" )
        if accessCodePlaceIdOrigin is None:
            return "Invalid request", 400
        TargetPlaceObj : Place = Place.query.filter_by( placeid = int( accessCodePlaceIdOrigin ) ).first()
        if TargetPlaceObj is None:
            return "Invalid request", 400
        
        if TargetPlaceObj.parent_universe_id != OriginPlaceObj.parent_universe_id:
            return "Invalid request", 400
        
        try:
            TargetPlaceServerObj : PlaceServer = CreateNewPlaceServer(
                placeId = TargetPlaceObj.placeid,
                reserved_server_access_code = given_reservedServerAccessCode
            )
        except Exception as e:
            logging.error(f"TeleportServiceInternal - grant_access_to_reserved - Place {TargetPlaceObj.placeid} - Universe {TargetPlaceObj.parent_universe_id} - {e}")
            return "Failed to start new server", 400
        redis_controller.set( f"reservedserveraccesscode_server:{given_reservedServerAccessCode}", TargetPlaceObj.placeid, ex = 60 * 60 * 24 )
    
    AllowedUserIds = request.form.getlist( key = "playerIds", type = int )
    if len( AllowedUserIds ) == 0:
        return "Invalid request", 400
    
    for UserId in AllowedUserIds:
        UserObj : User = User.query.filter_by( id = UserId ).first()
        if UserObj is None:
            return "Invalid request", 400
        redis_controller.set( f"reservedserveraccesscode:{TargetPlaceServerObj.serveruuid}:{UserObj.id}", "1", ex = 60 * 8)
    
    return "", 200
