import os
import sys
import subprocess
import urllib.request
import requests
from tqdm import tqdm
import psutil
import argparse


def log(message, level="INFO"):
    """Simple logging to console."""
    levels = {
        "INFO": "[INFO]",
        "WARNING": "[WARNING]",
        "ERROR": "[ERROR]"
    }
    print(f"{levels.get(level, '[INFO]')} {message}")


def parse_arguments():
    """Parst Kommandozeilenargumente."""
    parser = argparse.ArgumentParser(description="Minecraft Server Setup Script")
    parser.add_argument("--directory", type=str, required=True, help="Path to install the server.")
    parser.add_argument("--type", type=str, required=True, choices=["vanilla", "bungeecord"],
                        help="Server type (vanilla or bungeecord).")
    parser.add_argument("--port", type=str, required=True,
                        help="Ports and protocols in a single string (format: \"port:protocol port:protocol ...\").")

    args = parser.parse_args()

    install_dir = args.directory.strip('"')
    server_type = args.type.lower()
    ports = args.port.strip('"')

    log(f"Arguments processed: Install Directory={install_dir}, Server Type={server_type}, Ports={ports}")
    return install_dir, server_type, ports


def check_ports(port_list):
    """Prüft, ob die angegebenen Ports bereits geöffnet sind."""
    log("Überprüfe Ports auf bestehende Firewall-Regeln.")
    for port in port_list:
        port_num, _ = port.split(":")
        result = subprocess.run(f"netsh advfirewall firewall show rule name=all | findstr {port_num}",
                                stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
        if result.returncode == 0:
            log(f"Port {port_num} ist bereits geöffnet.", "WARNING")
            return True
    log("Keine doppelten Ports gefunden.")
    return False


def open_ports(port_string):
    """Öffnet die angegebenen Ports in der Firewall."""
    log("Öffne Ports in der Firewall.")
    try:
        port_list = port_string.split()
        for port in port_list:
            port_num, protocol = port.split(":")
            subprocess.run(
                f"netsh advfirewall firewall add rule name=\"Minecraft Port {port_num}\" dir=in action=allow protocol={protocol.upper()} localport={port_num}",
                shell=True)
            subprocess.run(
                f"netsh advfirewall firewall add rule name=\"Minecraft Port {port_num}\" dir=out action=allow protocol={protocol.upper()} localport={port_num}",
                shell=True)
            log(f"Port {port_num} ({protocol.upper()}) erfolgreich geöffnet.")
    except Exception as e:
        log(f"Fehler beim Öffnen der Ports: {e}", "ERROR")


def install_java():
    """Installiert Java 21, falls noch nicht installiert."""
    java_installer = "bellsoft-jdk21.0.4+9-windows-amd64.msi"
    try:
        java_version = subprocess.check_output(['java', '-version'], stderr=subprocess.STDOUT).decode()
        if "21" in java_version:
            log("Java 21 ist bereits installiert.")
            return
    except Exception:
        log("Java ist nicht installiert oder nicht Java 21. Installation erforderlich.", "WARNING")

    log("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"
    log(f"Lade Java 21 herunter: {java_url}")
    urllib.request.urlretrieve(java_url, java_installer)
    log("Java-Installer heruntergeladen.")
    subprocess.run(f"msiexec /i {java_installer} /quiet /norestart", shell=True)
    log("Java 21 erfolgreich installiert.")

    if os.path.exists(java_installer):
        os.remove(java_installer)
        log("Java-Installer gelöscht.")


def download_file(url, dest):
    """Lädt eine Datei von der angegebenen URL herunter und zeigt einen Fortschrittsbalken an."""
    log(f"Beginne Download: {url}")
    try:
        response = requests.get(url, stream=True, timeout=30)
        response.raise_for_status()
        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)
        log(f"Datei erfolgreich heruntergeladen: {dest}")
    except Exception as e:
        log(f"Fehler beim Herunterladen der Datei: {e}", "ERROR")
        sys.exit()


def install_server(install_dir, server_type):
    """Installiert den Minecraft Vanilla oder BungeeCord Server."""
    log("Starte Serverinstallation.")
    if server_type == "vanilla":
        log("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:
        log("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")

    os.makedirs(install_dir, exist_ok=True)
    log(f"Installationsverzeichnis erstellt: {install_dir}")
    download_file(server_url, server_jar)
    log(f"Server-Datei ({server_type}) erfolgreich heruntergeladen.")


def create_start_bat(install_dir, server_type, allocated_ram):
    """Erstellt eine start_minecraft_server.bat im Installationsverzeichnis."""
    log("Erstelle Start-Skript im Installationsverzeichnis.")
    if server_type == "vanilla":
        server_jar = os.path.join(install_dir, "minecraft_server.jar")
    else:
        server_jar = os.path.join(install_dir, "BungeeCord.jar")

    bat_path = os.path.join(install_dir, "start_minecraft_server.bat")
    start_command = f'"C:\\Program Files\\BellSoft\\LibericaJDK-21\\bin\\java.exe" -Xmx{allocated_ram}M -Xms{allocated_ram}M -jar "{server_jar}" nogui'

    with open(bat_path, 'w') as bat_file:
        bat_file.write(start_command)
    log(f"Start-Skript erstellt: {bat_path}")
    return bat_path


def start_minecraft_server(install_dir, server_type):
    """Startet den Minecraft Server mit 50% des verfügbaren RAM und erstellt eine Start-Batch-Datei."""
    log("Bereite Serverstart vor.")
    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")
        log("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
    log(f"Verfügbarer RAM: {total_ram}MB, zugewiesener RAM: {allocated_ram}MB")

    bat_path = create_start_bat(install_dir, server_type, allocated_ram)
    log("Server-Start-Skript erfolgreich erstellt.")

    log("Starte die Server-Batch-Datei in einer neuen Konsole.")
    subprocess.Popen(["cmd.exe", "/c", bat_path], cwd=install_dir)


def main():
    """Hauptlogik für die Minecraft Server Installation."""
    log("Minecraft Server Setup gestartet.")
    install_dir, server_type, port_string = parse_arguments()

    install_java()
    open_ports(port_string)
    install_server(install_dir, server_type)
    start_minecraft_server(install_dir, server_type)

    log("Minecraft Server Setup abgeschlossen.")
    print("Minecraft Server erfolgreich installiert und gestartet.")


if __name__ == "__main__":
    main()
