import os
import sys
import ctypes
import subprocess
import tkinter as tk
from tkinter import filedialog, messagebox
import urllib.request
import requests
from tqdm import tqdm
import logging
import psutil

# Logging konfigurieren
logging.basicConfig(filename='minecraft_server_setup.log', level=logging.INFO,
                    format='%(asctime)s - %(levelname)s - %(message)s')


def is_admin():
    """Prüft, ob das Skript mit Administratorrechten ausgeführt wird."""
    try:
        return ctypes.windll.shell32.IsUserAnAdmin()
    except:
        return False


def check_ports():
    """Prüft, ob die Minecraft-Ports bereits geöffnet sind."""
    ports = ["25565", "25577"]
    for port in ports:
        result = subprocess.run(f"netsh advfirewall firewall show rule name=all | findstr {port}",
                                stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
        if result.returncode == 0:
            return True
    return False


def open_ports():
    """Öffnet die Minecraft-Ports 25565 und 25577 in der Firewall."""
    logging.info("Öffne Minecraft-Ports 25565 und 25577 in der Firewall.")
    ports_open = check_ports()

    if ports_open:
        logging.info("Ports sind bereits geöffnet.")
        root = tk.Tk()
        root.withdraw()
        response = messagebox.askyesno("Ports bereits geöffnet", "Die Ports 25565 und 25577 sind bereits geöffnet. "
                                                                 "Möchten Sie die Ports erneut öffnen?")
        if not response:
            logging.info("Der Nutzer hat sich entschieden, das Öffnen der Ports zu überspringen.")
            return

    update_terminal_progress("Ports öffnen gestartet", 50)
    subprocess.run(
        "netsh advfirewall firewall add rule name=\"Minecraft Server Port 25565\" dir=in action=allow protocol=TCP localport=25565",
        shell=True)
    subprocess.run(
        "netsh advfirewall firewall add rule name=\"Minecraft Server Port 25577\" dir=in action=allow protocol=TCP localport=25577",
        shell=True)
    logging.info("Ports erfolgreich geöffnet.")
    update_terminal_progress("Ports öffnen abgeschlossen", 100)


def install_java():
    """Installiert Java 21, falls noch nicht installiert."""
    try:
        java_version = subprocess.check_output(['java', '-version'], stderr=subprocess.STDOUT).decode()
        if "21" in java_version:
            logging.info("Java 21 ist bereits installiert.")
            return
    except Exception as e:
        logging.warning("Gefundene Java-Version ist nicht Java 21.")

    logging.info("Starte die Installation von Java 21.")
    java_url = "https://download.bell-sw.com/java/21.0.4+9/bellsoft-jdk21.0.4+9-windows-amd64.msi"
    java_installer = "bellsoft-jdk21.0.4+9-windows-amd64.msi"
    urllib.request.urlretrieve(java_url, java_installer)
    update_terminal_progress("Java Download gestartet", 40)
    subprocess.run(f"msiexec /i {java_installer} /quiet /norestart", shell=True)
    logging.info("Java 21 erfolgreich installiert.")
    update_terminal_progress("Java Installation abgeschlossen", 100)


def download_file(url, dest):
    """Lädt eine Datei von der angegebenen URL herunter und zeigt einen Fortschrittsbalken an."""
    try:
        response = requests.get(url, stream=True, timeout=30)
        response.raise_for_status()  # Raise an error for bad responses
        total_size = int(response.headers.get('content-length', 0))
        with open(dest, 'wb') as file:
            for data in tqdm(response.iter_content(1024), total=total_size // 1024, unit='KB'):
                file.write(data)
    except Exception as e:
        logging.error(f"Fehler beim Herunterladen der Datei: {e}")
        sys.exit()


def install_server(server_type, install_dir):
    """Installiert den Minecraft Vanilla oder BungeeCord Server."""
    if server_type == "yes":
        logging.info("Vanilla Server wird installiert.")
        server_url = "https://piston-data.mojang.com/v1/objects/45810d238246d90e811d896f87b14695b7fb6839/server.jar"
        server_jar = os.path.join(install_dir, "minecraft_server.jar")
    else:
        logging.info("BungeeCord Server wird installiert.")
        server_url = "https://ci.md-5.net/job/BungeeCord/lastSuccessfulBuild/artifact/bootstrap/target/BungeeCord.jar"
        server_jar = os.path.join(install_dir, "BungeeCord.jar")

    update_terminal_progress(f"{server_type} Server herunterladen gestartet", 40)
    try:
        download_file(server_url, server_jar)
        logging.info(f"{server_type} Server heruntergeladen.")
    except Exception as e:
        logging.error(f"Fehler beim Herunterladen des Servers: {e}")
        sys.exit()

    update_terminal_progress(f"{server_type} Server herunterladen abgeschlossen", 100)


def start_minecraft_server(install_dir, server_type):
    """Startet den Minecraft Server mit 50% des verfügbaren RAM."""

    # EULA-Datei erstellen und akzeptieren
    eula_file_path = os.path.join(install_dir, "eula.txt")
    if not os.path.exists(eula_file_path):
        with open(eula_file_path, 'w') as eula_file:
            eula_file.write("eula=true\n")
        logging.info("EULA-Datei erstellt und akzeptiert.")

    total_ram = psutil.virtual_memory().total // (1024 * 1024)  # In MB
    allocated_ram = total_ram // 2  # 50% des verfügbaren RAM

    if server_type == "yes":
        server_jar = os.path.join(install_dir, "minecraft_server.jar")
        start_command = f'"C:\\Program Files\\BellSoft\\LibericaJDK-21\\bin\\java.exe" -Xmx{allocated_ram}M -Xms{allocated_ram}M -jar {server_jar} nogui'
        logging.info(f"Starte Minecraft-Server mit {allocated_ram}MB RAM.")
        subprocess.Popen(start_command, shell=True, cwd=install_dir)
    else:
        server_jar = os.path.join(install_dir, "BungeeCord.jar")
        start_command = f'"C:\\Program Files\\BellSoft\\LibericaJDK-21\\bin\\java.exe" -Xmx{allocated_ram}M -Xms{allocated_ram}M -jar {server_jar} nogui'
        logging.info(f"Starte BungeeCord Server mit {allocated_ram}MB RAM.")
        subprocess.Popen(start_command, shell=True, cwd=install_dir)


def update_terminal_progress(message, progress):
    """Aktualisiert den Fortschrittsbalken im Terminal."""
    print(f"{message}: {progress}% abgeschlossen.")


def main():
    """Hauptlogik für die Minecraft Server Installation."""
    if not is_admin():
        logging.info("Skript wurde ohne Administratorrechte gestartet. Neustart mit Administratorrechten.")
        ctypes.windll.shell32.ShellExecuteW(None, "runas", sys.executable, __file__, None, 1)
        sys.exit()

    logging.info("Minecraft Server Setup gestartet.")

    # Ordnerauswahl
    root = tk.Tk()
    root.withdraw()
    messagebox.showinfo("Ordner auswählen", "Wählen Sie im nächsten Schritt das Installationsverzeichnis aus.")
    install_dir = filedialog.askdirectory(title="Wählen Sie das Installationsverzeichnis aus.")
    logging.info(f"Gewähltes Installationsverzeichnis: {install_dir}")

    # Server-Typ abfragen
    server_type = messagebox.askquestion("Server-Typ",
                                         "Möchten Sie einen Vanilla(ja) oder BungeeCord(nein) Server installieren?",
                                         icon='question')
    logging.info(f"Gewählter Server-Typ: {server_type}")

    # Java Installation
    logging.info("Überprüfe, ob Java 21 bereits installiert ist.")
    install_java()

    # Ports öffnen
    open_ports()

    # Minecraft Server Installation
    install_server(server_type, install_dir)

    # Minecraft Server starten
    start_minecraft_server(install_dir, server_type)

    # Erfolgsmeldung nach Abschluss
    messagebox.showinfo("Installation abgeschlossen", "Minecraft Server erfolgreich installiert und gestartet.")
    logging.info("Minecraft Server Setup abgeschlossen.")


if __name__ == "__main__":
    main()
