import requests
from bs4 import BeautifulSoup
import os
import py7zr
import subprocess
import sys
import argparse

# ASCII-Print-Text
ascii_art = r"""
  /$$$$$$                                /$$   /$$                       /$$    
 /$$__  $$                              | $$  | $$                      | $$    
| $$  \__/  /$$$$$$   /$$$$$$   /$$$$$$ | $$  | $$  /$$$$$$   /$$$$$$$ /$$$$$$  
| $$       /$$__  $$ /$$__  $$ /$$__  $$| $$$$$$$$ /$$__  $$ /$$_____/|_  $$_/  
| $$      | $$  \ $$| $$  \__/| $$  \ $$| $$__  $$| $$  \ $$|  $$$$$$   | $$    
| $$    $$| $$  | $$| $$      | $$  | $$| $$  | $$| $$  | $$ \____  $$  | $$ /$$
|  $$$$$$/|  $$$$$$/| $$      |  $$$$$$/| $$  | $$|  $$$$$$/ /$$$$$$$/  |  $$$$/ 
 \______/  \______/ |__/       \______/ |__/  |__/ \______/ |_______/    \___/  
 by Myzi
"""


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():
    """Parses command line arguments."""
    parser = argparse.ArgumentParser(description="FiveM Installation Script")
    parser.add_argument("--directory", type=str, required=True, help="Path to save the server files.")
    parser.add_argument("--Build", type=str, required=True, help="Build number to download.")
    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()

    # Convert arguments to variables
    directory = args.directory.strip('"')
    build = args.Build.strip('"')
    ports = args.port.strip('"')

    log(f"Arguments parsed: Directory={directory}, Build={build}, Ports={ports}")
    return directory, build, ports


def main():
    log("Skript gestartet.")
    print(ascii_art)

    # Parse arguments
    storage_path, build_number, port_string = parse_arguments()
    base_url = "https://runtime.fivem.net/artifacts/fivem/build_server_windows/master/"
    log(f"Lade Webseite herunter: {base_url}")

    # Process the ports string into a list of dictionaries
    ports = []
    try:
        for port_pair in port_string.split():
            port, protocol = port_pair.split(":")
            ports.append({"port": int(port), "protocol": protocol.upper()})
            log(f"Port hinzugefügt: {port}/{protocol.upper()}")
    except ValueError:
        log("Ungültiges Port-String-Format. Erwartet: \"port:protocol port:protocol ...\"", "ERROR")
        sys.exit(1)

    if not ports:
        log("Es wurden keine gültigen Ports übergeben.", "ERROR")
        sys.exit(1)

    try:
        response = requests.get(base_url)
        response.raise_for_status()
        log("Webseite erfolgreich heruntergeladen.")

        soup = BeautifulSoup(response.text, 'html.parser')
        links = soup.find_all('a', href=True)
        found_link = None

        for link in links:
            if build_number in link.text:
                found_link = link['href']
                break

        if found_link:
            full_link = os.path.join(base_url, found_link)
            log(f"Gefundener Link für Build {build_number}: {full_link}")
            local_filename = download_file(full_link)
            if local_filename:
                server_folder = os.path.join(storage_path, "server")
                os.makedirs(server_folder, exist_ok=True)
                log(f"Erstelle Server-Verzeichnis: {server_folder}")
                extract_file(local_filename, server_folder)
                create_readme(storage_path)
                os.remove(local_filename)
                log(f"Datei {local_filename} wurde gelöscht.")

                for rule in ports:
                    create_firewall_rule(rule)

                fxserver_path = os.path.join(server_folder, "FXServer.exe")
                if os.path.exists(fxserver_path):
                    start_fxserver(fxserver_path)
                else:
                    log("FXServer.exe wurde nicht gefunden.", "ERROR")
        else:
            log(f"Kein Link für Build {build_number} gefunden.", "WARNING")

    except requests.RequestException as e:
        log(f"Fehler beim Herunterladen der Webseite: {e}", "ERROR")
    except Exception as e:
        log(f"Ein unerwarteter Fehler ist aufgetreten: {e}", "ERROR")

    log("Skript beendet.")


def download_file(url):
    log(f"Beginne mit dem Download der Datei: {url}")
    try:
        response = requests.get(url, stream=True)
        response.raise_for_status()
        filename = url.split('/')[-1]

        with open(filename, 'wb') as file:
            for chunk in response.iter_content(chunk_size=8192):
                file.write(chunk)

        log(f"Datei erfolgreich heruntergeladen: {filename}")
        return filename
    except requests.RequestException as e:
        log(f"Fehler beim Herunterladen der Datei: {e}", "ERROR")
        return None


def extract_file(file_path, output_folder):
    log(f"Entpacke Datei: {file_path} nach {output_folder}")
    try:
        with py7zr.SevenZipFile(file_path, mode='r') as archive:
            archive.extractall(path=output_folder)
        log("Datei erfolgreich entpackt.")
    except Exception as e:
        log(f"Fehler beim Entpacken der Datei: {e}", "ERROR")


def create_readme(output_folder):
    log(f"Erstelle Readme-Datei im Verzeichnis: {output_folder}")
    readme_content = (
        "Danke, dass du dich für das automatische FiveM Installationsskript entschieden hast.\n"
        "Bei Fragen wende dich gerne an uns:\n"
        "https://Discord.gg/corohost"
    )
    readme_path = os.path.join(output_folder, "Readme.txt")
    try:
        with open(readme_path, 'w', encoding='utf-8') as readme_file:
            readme_file.write(readme_content)
        log("Readme.txt erfolgreich erstellt.")
    except Exception as e:
        log(f"Fehler beim Erstellen der Readme-Datei: {e}", "ERROR")


def create_firewall_rule(rule):
    log(f"Erstelle Firewall-Regel für Port {rule['port']} ({rule['protocol']})")
    try:
        rule_name = f"FiveM-Port-{rule['port']}-{rule['protocol']}"
        subprocess.run(
            ["netsh", "advfirewall", "firewall", "add", "rule",
             f"name={rule_name}", "dir=in", "action=allow",
             f"protocol={rule['protocol']}", f"localport={rule['port']}",
             "enable=yes"],
            check=True
        )
        subprocess.run(
            ["netsh", "advfirewall", "firewall", "add", "rule",
             f"name={rule_name}", "dir=out", "action=allow",
             f"protocol={rule['protocol']}", f"localport={rule['port']}",
             "enable=yes"],
            check=True
        )
        log(f"Firewall-Regel erstellt: {rule_name}")
    except subprocess.CalledProcessError as e:
        log(f"Fehler beim Erstellen der Firewall-Regel: {e}", "ERROR")


def start_fxserver(fxserver_path):
    log(f"Starte FXServer.exe: {fxserver_path}")
    try:
        subprocess.Popen(fxserver_path, creationflags=subprocess.CREATE_NEW_CONSOLE)
        log("FXServer.exe erfolgreich gestartet.")
    except Exception as e:
        log(f"Fehler beim Starten von FXServer.exe: {e}", "ERROR")


if __name__ == "__main__":
    main()
