import platform
import requests
from os import remove,mkdir
import subprocess
import shutil
from time import sleep
import re
import pathlib
import json
import socket

def checkConnection(host="8.8.8.8", port=53, timeout=3):
    try:
        socket.setdefaulttimeout(timeout)
        socket.socket(socket.AF_INET, socket.SOCK_STREAM).connect((host, port))
        return True
    except socket.error:
        return False

def regainedConnection() -> bool :
    global lostConnection
    checkedConnection = checkConnection()
    if checkedConnection == True :
        if checkedConnection == lostConnection :
            lostConnection = not checkedConnection
            return True
        else :
            return False
    else :
        lostConnection = not checkedConnection
        return True

lostConnection = True

def getArch() -> str :
    arch = platform.machine()

    if arch in ['x86_64','AMD64'] :
        return 'x86_64'
    elif arch in ['arm64','aarch64'] :
        return 'arm64'

OS = platform.system()

if OS == 'Linux' :
    APPDATA = f"{pathlib.Path.home()}/.gvm_data/"
elif OS == 'Windows' :
    APPDATA =f"{pathlib.Path.home()}\\AppData\\Roaming\\GVM\\"

FIRSTOPEN = not pathlib.Path(f"{APPDATA}settings.json").exists()

if FIRSTOPEN :
    try :
        mkdir(APPDATA)
    except :
        pass
    finally :
        open(f"{APPDATA}settings.json",'w').write(json.dumps({'appPath':'','token':''}))
            

SETTINGS = json.load(open(f"{APPDATA}settings.json",'r'))

def updateSettings(appPath: str, token: str) -> None :
    global APP_PATH
    global TOKEN
    APP_PATH = appPath
    TOKEN = token

APP_PATH: str = SETTINGS['appPath']
WIN_COMP: str = APP_PATH.replace('/','\\')
TOKEN: str = SETTINGS['token']

ARCH = getArch()

def requestGitHubInfo() -> list :
    if len(TOKEN) > 0 :
        response = requests.get("https://api.github.com/repos/godotengine/godot/releases?per_page=100",headers={"Accept":"application/vnd.github+json","Authorization":f"Bearer {TOKEN}","X-GitHub-Api-Version":"2022-11-28"})
    else :
        return []

    return response.json()

def loadAltOptions(mono: str) -> list :
    installed_files = getAllFiles(mono)
    installed_files.sort(reverse=True)
    if installed_files != [] :
        return installed_files
    else:
        return ['No options available']

# Get the versions of the Godot Engine and sort them from latest to oldest

def verOptions(mono: str, curVersion: list|None) -> list:
    tag_list = []
    
    altOptions = loadAltOptions(mono)
    
    if not checkConnection() :
        return altOptions
    elif curVersion == None and checkConnection() :
        response = requestGitHubInfo()
    elif curVersion == altOptions and checkConnection() :
        response = requestGitHubInfo()
    elif curVersion != altOptions :
        return altOptions
    else :
        return curVersion

    if type(response) == dict or response == [] :
        return altOptions
    
    for i in response :
       tag_list.append(i['tag_name'])

    if tag_list != [] :
        tag_list.sort(reverse=True)
        tag_list.pop()
    else :
        return altOptions

    return tag_list

def downloadCmd(asset_url: str) -> int :
    curlStr = f"curl -Lo {APP_PATH}/Godot.zip {asset_url}"
    if subprocess.run(curlStr.split(' ')).returncode != 0 :
        return 1

    if OS == 'Linux' :
        unzipStr = f"unzip -d {APP_PATH} {APP_PATH}/Godot.zip"
    elif OS == 'Windows' :
        unzipStr = f"tar -xf {APP_PATH}/Godot.zip -C {APP_PATH}"
    
    if subprocess.run(unzipStr.split(' ')).returncode != 0 :
        return 1
    
    remove(f"{APP_PATH}/Godot.zip")
    
    return 0
    

def downloadGodot(cur_version: str,mono: str) -> int :

    if not pathlib.Path(APP_PATH).exists() :
        return 1

    response = requestGitHubInfo()

    if type(response) == dict :
        return 1

    VERSION = cur_version

    response.sort(key=lambda x: x['tag_name'],reverse=True)

    ASSETS = getAssets(response, VERSION)

    if ASSETS != [] :
        for asset in ASSETS :
            if OS == 'Linux' :
                regex: list = re.findall("linux|x11",asset['name']) + re.findall("x86_64|64",asset['name'])
                
                if mono != 'no_mono' :
                    regex += re.findall(mono,asset['name'])
                    monoCatch = ""
                    monoGoal = ['mono']
                else :
                    monoCatch = "|mono"
                    monoGoal = []


                if re.findall(f"server|headless{monoCatch}",asset['name']) == [] and regex in [['linux','x86_64']+monoGoal,['x11','64']+monoGoal] :
                    return downloadCmd(asset['browser_download_url]'])
                
            elif OS == 'Windows' :

                if mono != 'no_mono' :
                    monoGoal = ['mono']
                else :
                    monoGoal = []

                if ['win','arm64'] + monoGoal == re.findall('win',asset['name']) + re.findall('arm64',asset['name']) + re.findall('mono',asset['name']) :
                    return downloadCmd(asset['browser_download_url]'])
                
                elif ['win64'] + monoGoal == re.findall('win64',asset['name']) + re.findall('mono',asset['name']) :
                    return downloadCmd(asset['browser_download_url]'])
            else :
                return 1
    else :
        return 1

def getAssets(response: list, tag: str) -> list :

    for i in response :
        if i['tag_name'] == tag :
            return i['assets']
        else :
            continue
    
    return []

def launch(cur_version: str, mono: str) -> int :

    if not pathlib.Path(APP_PATH).exists() :
        return 1

    VERSION = cur_version.split('-')[0]

    file = getFile(VERSION,mono)

    if file.exists() and not file.is_dir() :
        popen(f"{file.as_posix()}")
        return 0
    elif file.exists() and file.is_dir() :
        for exe in file.iterdir() :
            if re.findall('exe|x86_64|64',exe.name) and exe.is_file() :
                popen(f"{exe.as_posix()}")
    else :
        return 1

def getFile(cur_version: str, mono: str) -> pathlib.Path | int :

    if not pathlib.Path(APP_PATH).exists() :
        return 1

    VERSION = cur_version.split('-')[0]

    appDir = pathlib.Path(APP_PATH)

    if mono == 'no_mono' :
        for i in appDir.iterdir() :
            if re.findall(f"{VERSION}-|{VERSION}_",i.name) in [[f"{VERSION}-"],[f"{VERSION}_"]] and i.is_file() :
                return i
    elif mono == 'mono' :
        for i in appDir.iterdir() :
            if re.findall(f"{VERSION}-|{VERSION}_",i.name) + re.findall('mono',i.name) in [[f"{VERSION}-",'mono'],[f"{VERSION}_",'mono']] and i.is_dir() :
                return i
    
    return 1

def getAllFiles(mono: str) -> list :
    if not pathlib.Path(APP_PATH).exists() :
        return []
    
    files = []
    
    appDir = pathlib.Path(APP_PATH)

    if mono == 'no_mono' :
        for i in appDir.iterdir() :
            if re.findall("-stable|_stable",i.name) in [["-stable"],["_stable"]] and i.is_file() :
                if re.findall("_stable",i.name) in ["_stable"] :
                    i.name.replace("_stable","-stable")

                files.append(re.findall("[1-9].+-stable",i.name)[0])
    elif mono == 'mono' :
        for i in appDir.iterdir() :
            if re.findall("-stable|_stable",i.name) + re.findall('mono',i.name) in [["-stable",'mono'],["_stable",'mono']] and i.is_dir() :
                if re.findall("_stable",i.name) in ["_stable"] :
                    i.name.replace("_stable","-stable")

                files.append(re.findall("[1-9].+-stable",i.name)[0])
    
    if files != [] :
        return files
    else :
        return []

def suddenSet(appPath: str, token: str) -> None :
    open(f"{APPDATA}settings.json",'w').write(json.dumps({'appPath':appPath,'token':token}))
    updateSettings(appPath,token)

def checkSettings() -> bool :
    settings = json.load(open(f"{APPDATA}settings.json",'r'))

    if settings['appPath'] == '' or settings['token'] == '' :
        return False
    else :
        return True

def removeGodot(cur_version: str, mono: str) -> int :
    if not pathlib.Path(APP_PATH).exists() :
        return 1

    VERSION = cur_version.split('-')[0]

    file = getFile(VERSION,mono)

    if file.exists() :
        if file.is_file() :
            remove(f"{file.as_posix()}")
        else :
            shutil.rmtree(file.as_posix())
        return 0
    else :
        return 1