import os
import sys
import time
import ctypes
import subprocess
import urllib.request
import winreg
import threading

CREATE_NO_WINDOW = 0x08000000

# Progress window
_progress_win = None
_progress_label = None
_progress_thread = None

def create_progress_window():
    global _progress_win, _progress_label
    import tkinter as tk
    _progress_win = tk.Tk()
    _progress_win.title("CompArt Assistencia Remota")
    _progress_win.geometry("400x140")
    _progress_win.resizable(False, False)
    _progress_win.configure(bg="#1e293b")
    try:
        _progress_win.iconbitmap(default='')
    except:
        pass
    tk.Label(_progress_win, text="CompArt Systems", font=("Segoe UI", 14, "bold"), bg="#1e293b", fg="#ffffff").pack(pady=(15, 5))
    _progress_label = tk.Label(_progress_win, text="A preparar instalacao...", font=("Segoe UI", 11), bg="#1e293b", fg="#94a3b8")
    _progress_label.pack(pady=(0, 15))
    _progress_win.protocol("WM_DELETE_WINDOW", lambda: None)
    _progress_win.update()

def update_progress(text):
    global _progress_label
    if _progress_label:
        try:
            _progress_label.config(text=text)
            _progress_win.update()
        except:
            pass

def close_progress():
    global _progress_win
    if _progress_win:
        try:
            _progress_win.destroy()
        except:
            pass

def run_progress_in_thread():
    try:
        create_progress_window()
        _progress_win.mainloop()
    except:
        pass

def show_progress():
    global _progress_thread
    _progress_thread = threading.Thread(target=run_progress_in_thread, daemon=True)
    _progress_thread.start()
    time.sleep(0.5)

def is_admin():
    try:
        return ctypes.windll.shell32.IsUserAnAdmin()
    except:
        return False

def run_as_admin():
    params = f'"{os.path.abspath(sys.argv[0])}"'
    ctypes.windll.shell32.ShellExecuteW(None, "runas", sys.executable, params, None, 1)

def msg_box(text, title="CompArt Assistencia Remota"):
    close_progress()
    ctypes.windll.user32.MessageBoxW(0, text, title, 0x40)

def run_hidden(cmd, **kwargs):
    if isinstance(cmd, str):
        cmd = cmd.split()
    si = subprocess.STARTUPINFO()
    si.dwFlags |= subprocess.STARTF_USESHOWWINDOW
    si.wShowWindow = 0
    return subprocess.run(cmd, startupinfo=si, creationflags=CREATE_NO_WINDOW, **kwargs)

def get_original_user_profile():
    try:
        key = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, r"SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList", access=winreg.KEY_READ)
        i = 0
        while True:
            try:
                sid = winreg.EnumKey(key, i)
                if sid.startswith("S-1-5-21") and not sid.endswith("-500"):
                    prof_path = winreg.QueryValueEx(winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, f"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\ProfileList\\{sid}"), "ProfileImagePath")[0]
                    if os.path.exists(prof_path):
                        return prof_path, sid
                i += 1
            except WindowsError:
                break
    except:
        pass
    return os.environ.get("USERPROFILE", "C:\\Users\\user"), None

SERVER = "116.203.52.248"
KEY = "VoNciHfK04+8tqVDJH8gLs5CbsXpECQ4jBTuFPEt1XU="
RUSTDESK_URL = "https://github.com/rustdesk/rustdesk/releases/download/1.4.6/rustdesk-1.4.6-x86_64.exe"
RUSTDESK_BUNDLED = os.path.join(os.path.dirname(sys.argv[0]), "rustdesk-portable.exe")
if hasattr(sys, '_MEIPASS'):
    BUNDLED_RUSTDESK = os.path.join(sys._MEIPASS, "rustdesk-portable.exe")
    if os.path.exists(BUNDLED_RUSTDESK):
        RUSTDESK_BUNDLED = BUNDLED_RUSTDESK
DOWNLOAD_PATH = os.path.join(os.environ.get("TEMP", "C:\\Temp"), "CompArt_Assistencia_Remota.exe")

if os.path.exists(RUSTDESK_BUNDLED):
    INSTALL_PATH = RUSTDESK_BUNDLED
else:
    INSTALL_PATH = DOWNLOAD_PATH

# RustDesk2.toml (Config2) - options guardam-se como strings (HashMap<String,String>).
# NOTA: nao colocar key_confirmed aqui como bool - partiria a desserializacao de todo o [options].
CONFIG2_TOML = f"""rendezvous_server = '{SERVER}:21116'
nat_type = 1
serial = 0
unlock_pin = ''
trusted_devices = ''

[options]
custom-rendezvous-server = '{SERVER}:21116'
relay-server = '{SERVER}:21117'
key = '{KEY}'
"""

# RustDesk.toml (Config) - key_confirmed e um campo bool ao nivel raiz deste ficheiro.
CONFIG_TOML = f"""key_confirmed = true
"""

if not is_admin():
    run_as_admin()
    sys.exit(0)

user_profile, user_sid = get_original_user_profile()
user_appdata = os.path.join(user_profile, "AppData", "Roaming")
user_local_appdata = os.path.join(user_profile, "AppData", "Local")
desktop = os.path.join(user_profile, "Desktop")

rustdesk_exe = os.path.join(os.environ.get("ProgramFiles", "C:\\Program Files"), "RustDesk", "rustdesk.exe")
alt_rustdesk = os.path.join(os.environ.get("ProgramFiles(x86)", "C:\\Program Files (x86)"), "RustDesk", "rustdesk.exe")
if not os.path.exists(rustdesk_exe) and os.path.exists(alt_rustdesk):
    rustdesk_exe = alt_rustdesk

# Show progress window
show_progress()
time.sleep(1)
update_progress("A preparar instalacao...")

# Add Windows Defender exclusions
for excl_path in [r"C:\Program Files\RustDesk", os.path.join(os.environ.get("LOCALAPPDATA", ""), "rustdesk"), os.path.join(os.environ.get("APPDATA", ""), "RustDesk")]:
    try:
        run_hidden(["powershell", "-Command", f"Add-MpPreference -ExclusionPath '{excl_path}'"], capture_output=True, timeout=10)
    except:
        pass

update_progress("A fechar versoes anteriores...")

# Kill ALL RustDesk processes
run_hidden(["taskkill", "/f", "/im", "rustdesk.exe"], capture_output=True)
run_hidden(["taskkill", "/f", "/im", "RustDesk.exe"], capture_output=True)
run_hidden(["taskkill", "/f", "/im", "RuntimeBroker_rustdesk.exe"], capture_output=True)
run_hidden(["net", "stop", "RustDesk"], capture_output=True)
run_hidden(["sc", "stop", "RustDesk"], capture_output=True)
time.sleep(3)

# Force stop service and remove registration (skip --uninstall to avoid CMD window)
update_progress("A remover versao anterior...")
run_hidden(["net", "stop", "RustDesk"], capture_output=True)
run_hidden(["taskkill", "/f", "/im", "rustdesk.exe"], capture_output=True)
run_hidden(["taskkill", "/f", "/im", "RustDesk.exe"], capture_output=True)
run_hidden(["sc", "delete", "RustDesk"], capture_output=True)
time.sleep(3)

# Force remove program files and registry
update_progress("A limpar ficheiros antigos...")
import shutil
for d in [r"C:\Program Files\RustDesk", r"C:\Program Files (x86)\RustDesk"]:
    if os.path.exists(d):
        try:
            shutil.rmtree(d, ignore_errors=True)
        except:
            pass

for reg_path in [r"HKCU\Software\RustDesk", r"HKLM\Software\RustDesk", r"HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\RustDesk", r"HKLM\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\RustDesk"]:
    run_hidden(["reg", "delete", reg_path, "/f"], capture_output=True)

# Kill one more time
run_hidden(["taskkill", "/f", "/im", "rustdesk.exe"], capture_output=True)
run_hidden(["taskkill", "/f", "/im", "RustDesk.exe"], capture_output=True)
time.sleep(2)

# Download or use bundled RustDesk
if os.path.exists(RUSTDESK_BUNDLED):
    update_progress("A instalar CompArt AT...")
    INSTALL_PATH = RUSTDESK_BUNDLED
else:
    update_progress("A descarregar CompArt AT...")
    try:
        urllib.request.urlretrieve(RUSTDESK_URL, DOWNLOAD_PATH)
    except Exception as e:
        msg_box(f"Erro ao descarregar: {e}\n\nVerifique a ligacao a internet.")
        sys.exit(1)
    INSTALL_PATH = DOWNLOAD_PATH

update_progress("A instalar CompArt AT...")

# Install RustDesk silently
si = subprocess.STARTUPINFO()
si.dwFlags |= subprocess.STARTF_USESHOWWINDOW
si.wShowWindow = 0
process = subprocess.Popen([INSTALL_PATH, "--silent-install"], startupinfo=si, creationflags=CREATE_NO_WINDOW, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
try:
    process.wait(timeout=60)
except subprocess.TimeoutExpired:
    process.kill()
    process.wait()
time.sleep(3)

if not os.path.exists(rustdesk_exe):
    update_progress("A tentar novamente...")
    process = subprocess.Popen([INSTALL_PATH, "--install"], startupinfo=si, creationflags=CREATE_NO_WINDOW, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    try:
        process.wait(timeout=60)
    except subprocess.TimeoutExpired:
        process.kill()
        process.wait()
    time.sleep(3)

if not os.path.exists(rustdesk_exe):
    msg_box("Erro: Nao foi possivel instalar o CompArt AT.\n\nContacte-nos pelo 933 946 917.")
    sys.exit(1)

update_progress("A configurar...")

# Configure service
# Stop to write config
run_hidden(["net", "stop", "RustDesk"], capture_output=True)
run_hidden(["taskkill", "/f", "/im", "rustdesk.exe"], capture_output=True)
time.sleep(3)

# Clean old configs
for reg_path in [r"HKCU\Software\RustDesk", r"HKLM\Software\RustDesk"]:
    run_hidden(["reg", "delete", reg_path, "/f"], capture_output=True)

clean_paths = [
    os.path.join(user_appdata, "RustDesk", "config", "RustDesk2.toml"),
    os.path.join(user_appdata, "RustDesk", "RustDesk2.toml"),
    os.path.join(user_local_appdata, "RustDesk", "config", "RustDesk2.toml"),
    r"C:\Windows\ServiceProfiles\LocalService\AppData\Roaming\RustDesk\config\RustDesk2.toml",
    r"C:\Windows\ServiceProfiles\LocalService\AppData\Local\RustDesk\config\RustDesk2.toml",
]
for f in clean_paths:
    if os.path.exists(f):
        try:
            os.chmod(f, 0o777)
        except:
            pass
        try:
            os.remove(f)
        except:
            pass

# Write config
config_dirs = [
    os.path.join(user_appdata, "RustDesk", "config"),
    os.path.join(user_local_appdata, "RustDesk", "config"),
    r"C:\Windows\ServiceProfiles\LocalService\AppData\Roaming\RustDesk\config",
    r"C:\Windows\ServiceProfiles\LocalService\AppData\Local\RustDesk\config",
]
for d in config_dirs:
    try:
        os.makedirs(d, exist_ok=True)
        for fname, content in [("RustDesk2.toml", CONFIG2_TOML), ("RustDesk.toml", CONFIG_TOML)]:
            toml_path = os.path.join(d, fname)
            if os.path.exists(toml_path):
                try:
                    os.chmod(toml_path, 0o777)
                except:
                    pass
            with open(toml_path, "w", encoding="utf-8") as f:
                f.write(content)
        try:
            os.chmod(toml_path, 0o444)
        except:
            pass
    except Exception as e:
        print(f"Erro ao escrever config em {d}: {e}")

# Write registry
for reg_root, reg_path in [(winreg.HKEY_CURRENT_USER, r"Software\RustDesk\config"), (winreg.HKEY_LOCAL_MACHINE, r"Software\RustDesk\config")]:
    try:
        key = winreg.CreateKey(reg_root, reg_path)
        winreg.SetValueEx(key, "custom-rendezvous-server", 0, winreg.REG_SZ, SERVER)
        winreg.SetValueEx(key, "relay-server", 0, winreg.REG_SZ, SERVER)
        winreg.SetValueEx(key, "key", 0, winreg.REG_SZ, KEY)
        winreg.CloseKey(key)
    except:
        pass

# Start service
# NOTA: os ficheiros RustDesk.toml / RustDesk2.toml ja foram escritos diretamente
# nas pastas de config acima - e esse o metodo fiável. O --import-config tem uma
# condicao de tempo (config tem de ser mais antigo que o exe) que falha aqui.
# Garantir que o servico arranca automaticamente com o Windows (unattended apos reboot)
run_hidden(["sc", "config", "RustDesk", "start=auto"], capture_output=True)
run_hidden(["net", "start", "RustDesk"], capture_output=True)

# Remove old shortcuts
for desk in [desktop, os.path.join(os.environ.get("PUBLIC", "C:\\Users\\Public"), "Desktop")]:
    for name in ["RustDesk.lnk", "rustdesk.lnk", "RustDesk.url"]:
        p = os.path.join(desk, name)
        if os.path.exists(p):
            try:
                os.remove(p)
            except:
                pass

# Create shortcut
try:
    import win32com.client
    shell = win32com.client.Dispatch("WScript.Shell")
    shortcut = shell.CreateShortCut(os.path.join(desktop, "CompArt Assistencia Remota.lnk"))
    shortcut.Targetpath = rustdesk_exe
    shortcut.WorkingDirectory = os.path.dirname(rustdesk_exe)
    shortcut.Description = "CompArt Assistencia Remota"
    shortcut.Save()
except:
    bat_content = f'''@echo off\nstart "" "{rustdesk_exe}"\n'''
    with open(os.path.join(desktop, "CompArt Assistencia Remota.bat"), "w") as f:
        f.write(bat_content)

# Start RustDesk UI
subprocess.Popen([rustdesk_exe])
time.sleep(5)

# Get RustDesk ID and send to server
rustdesk_id = ""
update_progress("A registar assistencia...")
try:
    result = run_hidden([rustdesk_exe, "--get-id"], capture_output=True, text=True, timeout=15)
    rustdesk_id = result.stdout.strip()
    if not rustdesk_id or len(rustdesk_id) < 6:
        rustdesk_id = ""
except:
    pass

if not rustdesk_id:
    try:
        config_path = os.path.join(user_appdata, "RustDesk", "config", "RustDesk.toml")
        if os.path.exists(config_path):
            enc_id = ""
            with open(config_path, "r", encoding="utf-8") as f:
                for line in f:
                    line = line.strip()
                    if line.startswith("enc_id"):
                        enc_id = line.split("=", 1)[1].strip().strip("'\"")
                        break
            if enc_id:
                result = run_hidden([rustdesk_exe, "--decrypt", enc_id], capture_output=True, text=True, timeout=10)
                candidate = result.stdout.strip()
                if candidate and len(candidate) >= 6 and candidate.isdigit():
                    rustdesk_id = candidate
    except:
        pass

if rustdesk_id:
    try:
        hostname = os.environ.get("COMPUTERNAME", "Desconhecido")
        data = f'{{"rustdesk_id": "{rustdesk_id}", "hostname": "{hostname}"}}'.encode("utf-8")
        req = urllib.request.Request(
            "https://admin.compartsystems.pt/api/clients?secret=cs-remote-2026-xK9mP",
            data=data,
            headers={"Content-Type": "application/json"},
            method="POST"
        )
        urllib.request.urlopen(req, timeout=10)
    except:
        pass

    try:
        heartbeat_dir = os.path.join(os.environ.get("ProgramFiles", "C:\\Program Files"), "CompArt")
        os.makedirs(heartbeat_dir, exist_ok=True)
        id_file = os.path.join(heartbeat_dir, "rustdesk_id.txt")
        with open(id_file, "w", encoding="utf-8") as f:
            f.write(rustdesk_id)
        heartbeat_ps1 = os.path.join(heartbeat_dir, "heartbeat.ps1")
        with open(heartbeat_ps1, "w", encoding="utf-8") as f:
            f.write(f'''try {{
    $id = Get-Content "C:\\Program Files\\CompArt\\rustdesk_id.txt" -ErrorAction SilentlyContinue
    if ($id -match "^\\d{{6,}}$") {{
        $body = '{{"rustdesk_id": "' + $id.Trim() + '", "hostname": "' + $env:COMPUTERNAME + '"}}'
        Invoke-RestMethod -Uri "https://admin.compartsystems.pt/api/clients?secret=cs-remote-2026-xK9mP" -Method Post -Body $body -ContentType "application/json" -TimeoutSec 10 | Out-Null
    }}
}} catch {{}}''')
        heartbeat_vbs = os.path.join(heartbeat_dir, "heartbeat.vbs")
        with open(heartbeat_vbs, "w", encoding="utf-8") as f:
            f.write(f'CreateObject("WScript.Shell").Run "powershell -ExecutionPolicy Bypass -WindowStyle Hidden -File \\"{heartbeat_ps1}\\"", 0, False')
        task_name = "CompArt-Heartbeat"
        run_hidden(["schtasks", "/delete", "/tn", task_name, "/f"], capture_output=True)
        run_hidden(["schtasks", "/create", "/tn", task_name, "/tr", f'wscript.exe "{heartbeat_vbs}"', "/sc", "minute", "/mo", "5", "/ru", "SYSTEM", "/rl", "highest", "/f"], capture_output=True)
    except:
        pass

close_progress()

msg_box(
    "Instalacao concluida com sucesso!\n\n"
    "O atalho 'CompArt Assistencia Remota' esta no Ambiente de Trabalho.\n\n"
    "Telefone/WhatsApp: +351 933 946 917\n"
    "www.compartsystems.pt"
)