import sys,os
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))

import getpass

import json
import os
import pathlib
import pickle
import subprocess
import sys
from dataclasses import dataclass

import requests

from libs.dataclass_io.dcio import DataClassIO
from libs.creds.creds import CredentialManager
from libs.osaf.k_osa import kOSA, AppEnv
from libs.osaf.osaf_core import OSAF_Core
from libs.pykek import kek
from libs.xdtui.xdtui import TUIXD

# launcher specific funcs
project_pub_url = "voxels.java86.ru"
def print_banner1():
    log.d("Print banner 1...")
    print(f"""
 __   __    /   /       __       ___       __  
|__) |__)  /   /  \  / /  \ \_/ |__  |    /__` 
|    |  \ /   /    \/  \__/ / \ |___ |___ .__/    

Sucrose launcher v {app_version} 
                                                                          
""")
def run(master, target_conf, user : "UserData", mc: "MinecraftConfig", ac : "AuthConf"):
    import platform

    os_code = platform.system()

    arg_sepratator = ":"
    binary_dir = None
    if os_code == 'Linux':
        print("OS: Linux")
        binary_dir = "../../../bin/lx-x64"
        arg_sepratator = ":"
    elif os_code == "Windows":
        print("OS: Windows")
        binary_dir = "../../../bin/nt-x64"
        arg_sepratator = ";"
    else:
        print("Извините, ваша операционная система не поддерживается!")
        print("Либо не верно распознана")
        exit(2)

    directory = f'app/{master.get("directory")}/'
    java = f"{binary_dir}/jre-official/bin/java"
    libs = target_conf.get("libraries")
    args = target_conf.get("args")

    new_args = []

    # build libs string
    libs_str = ''
    libs_dir = "./"
    sepratator = arg_sepratator
    for l in libs:
        if not osa.files.exists(f'{directory}/{l}'):
            print("LIB NOE: " + l)

        libs_str = libs_str + libs_dir + l + sepratator

    libs_str = libs_str[:-1]

    for a in args:
        new_args.append(
            a.replace("%libraries%", libs_str).replace("%java_bin%", java).replace("%gamedir%", "./").replace(
                "%user_name%", user.name)
            .replace("%uuid%", user.uuid)
            .replace("%xuid%", user.xuid)
            .replace("%access_token%", user.access_token)
            .replace("%client_id%", user.client_id)
            .replace("%memory_min%", mc.memory_min)
            .replace("%memory_max%", mc.memory_max)
            .replace("%game_window_width%", str(mc.game_window_width))
            .replace("%game_window_height%", str(mc.game_window_height))
            .replace("%auth_server%", ac.auth_server)
        )

    start_dir = os.getcwd()
    os.chdir(directory)
    log.d(os.getcwd())

    log.i("Starting game client...")
    c, _ = ev.osa.processes.exec(new_args, timeout=None)
    log.d(f"ExitCode == {c}")
    os.chdir(start_dir)

@dataclass
class AuthConf:
    auth_server: str
    service_cred_name: str
    client_token: str
@dataclass
class UserData:
    name: str
    uuid: str
    access_token: str
    client_id: str
    xuid: str = ""

    @staticmethod
    def offline_fallback(username):
        UserData(name=username, uuid="baka", access_token="baka-baka",
                 client_id='baka-baka-baka', xuid="")
@dataclass
class MinecraftConfig:
    memory_min: str = "1500M"
    memory_max: str = "3000M"
    game_window_width: int = 1280
    game_window_height: int = 720
@dataclass
class GameRunTask:
    target: str
    actions: list

@dataclass
class LauncherInstanceData:
    client_token : str



def unzip(source, out):
    print(source)
    print(out)
    exec(['7za', 'x', '-y', source, f'-o{out}'])
def unpack(master_conf):
    contains = master_conf.get("contains")
    for c in contains:
        pkg_root = repo + c
        suc_pack = json.loads(osa.files.read_str(pkg_root + "/sucrose-pack.json"))
        print(suc_pack)
        for f in suc_pack.get("files"):
            unzip(pkg_root + "/" + f.get("filepath"), "app/" + master_conf.get('directory'))

app_version = "2.0"
def print_about():
    print(f"""
=== about ===
Sucrose Launcher version {app_version}

A simple game launcher for Project Voxels ({project_pub_url})
the part of PR//Automata

=== credits ===
coded: Sera (Java_x86), Λlina v1

testing: Mortus_D, No_Voises
    """)
    input("[Press ENTER to close]")


def login(ac: AuthConf):

    cm = CredentialManager("PR//Voxels")
    print(cm.get_saved_usernames())

    last_user = cm.get_last_saved_user()

    if last_user:
        choice = TUIXD().select("Меню:", {
            "login_last": f"Играть как {last_user}",
            "login_other": "Играть с другой учетной записью...",
            "login_clear": "Забыть все учетные записи",
            "about": "О программе",
            "exit": "Выход",
        })
    else:
        choice = TUIXD().select("Меню:", {
            "login_other": "Войти в учетную запись и играть",
            "about": "О программе",
            "exit": "Выход",
        })

    username = ""
    password = ""
    if choice == "login_last":
        username = last_user
        password = cm.get_credentials(last_user)

    elif choice == "login_other":
        print(f"=== Авторизация [{ac.auth_server}] ===")
        username = input("Логин: ")
        password = getpass.getpass("Пароль: ")

    elif choice == "login_clear":
        print("Очистка...")
        cm.clear_all_credentials()
        return None
    elif choice == "about":
        print_about()
        return None
    elif choice == "exit":
        print("Выход...")
        exit(0)

    payload = {
        "agent": {"name": "Minecraft", "version": 1},
        "username": username,
        "password": password,
        "clientToken": ac.client_token
    }


    try:
        r = requests.post(f"{ac.auth_server}/authenticate", json=payload, timeout=3)
        if r.status_code == 200:
            data = r.json()
            selected = data["selectedProfile"]

            print("=== Авторизация успешна ===")

            cm.save_credentials(username, password)
            log.d(f"Auth success! User: {selected['name']}, UUID: {selected['id']}")

            return UserData(name=selected["name"], uuid=selected["id"], access_token=data["accessToken"], client_id=data["clientToken"], xuid="")
        else:
            log.e(f"auth error: ({r.status_code}): {r.text}")
            print(f"Ошибка авторизации ({r.status_code}): {r.text}")
            return None

    except Exception as e:
        print(f"Ошибка при подключении к серверу: {e}")
        a = input("Запустить в режиме оффлайн? (y/n)").lower()
        if a == "y":
            auth_conf.auth_server = "ely.by"
            return UserData.offline_fallback(username)
        else:
            exit(2)



ev = kOSA().appenv_kick_t1(
    AppEnv(pathlib.Path(__file__).parent.resolve()),
    app_id="sucrose",
    app_displayname="Sucrose Game Launcher for PR//Voxels",
    app_binname=pathlib.Path(__file__).name
)


ev.argproc.nocommand_silent = True
ev.argproc.now(sys.argv)
ev.kosa.conf_write_a("etc/log.conf", ev.log.config)
osa : OSAF_Core = ev.osa
log = ev.log
log.i("Starting...")


dcio = DataClassIO()


ev.lch_instance_data_f = ev.app_root / "data" / "laucher_instance.dat"

launcher_instance_data = None
if osa.files.exists(ev.lch_instance_data_f):
    log.d(f"load launcher_instance_data...")
    try:
        launcher_instance_data = dcio.load_from_disk(ev.lch_instance_data_f)
        if launcher_instance_data is None: raise ValueError(f"{ev.lch_instance_data_f} is empty!")
    except:
        # fixme
        log.d(f"load launcher_instance_data failed... create new")
        launcher_instance_data = LauncherInstanceData(kek.generate_uuid())
        dcio.save_to_disk(ev.lch_instance_data_f, launcher_instance_data)
else:
    log.d(f"launcher_instance_data not found... create new")
    # fixme
    launcher_instance_data = LauncherInstanceData(kek.generate_uuid())
    dcio.save_to_disk(ev.lch_instance_data_f, launcher_instance_data)

log.d(f"Client token: {launcher_instance_data.client_token}")

auth_conf = AuthConf(auth_server="https://sucrose-auth.java86.ru/", service_cred_name="PR//Voxels", client_token=launcher_instance_data.client_token)
grtask = GameRunTask(target="voxels4/2.2", actions=["banner1","actual", "run"])
mc_conf = MinecraftConfig(memory_min="1500M", memory_max="3000M", game_window_width=1280, game_window_height=720)



repo = "data/repo/"
master_conf_file = f"{repo}/{grtask.target}/sucrose.json"


try:
    master_conf = json.loads(osa.files.read_str(master_conf_file))
except:
    log.e("Master conf not load")
    exit(2)


for act in grtask.actions:
    if act == "actual":
        # unpack(master_conf)
        pass

    if act == "banner1":
        print_banner1()
    if act == "run":
        log.d("try run a GAME...")

        while True:
            user_conf = login(auth_conf)
            if user_conf is not None:
                break

        run_conf = json.loads(osa.files.read_str(f"{repo}/{master_conf.get('run_target')}/sucrose-run.json"))
        run(master_conf, run_conf, user_conf, mc_conf, auth_conf)

log.i(f"Sucrose Launcher {app_version}. Created by Sera and Λlina with 🩵")
log.i("PR//Automata (c) 2023 - until the end of time")


ev.log.shutdown()