using System.Diagnostics;
using System.Text.Json;
using Lentia.Core.Auth.Yggdrasil;
using Lentia.Core.Constants;
using Lentia.Core.Game.Extra;
using Lentia.Core.Utils;
using Lentia.Utils;

namespace Lentia.Core.Game;


public class LaunchOptions {
    
    public enum ModLoaderType {
        Vanilla,
        Forge,
        Fabric,
        NeoForge
    }

    public VersionOptions Version { get; set; } = new();
    public ModLoaderType ModLoader { get; set; } = ModLoaderType.Vanilla;
    public string? Forge { get; set; }

    public MemoryOptions Memory { get; set; } = new();
    public string JavaPath { get; set; } = "java";
    public int Timeout { get; set; } = 10000;

    public List<string> CustomArgs { get; set; } = new();
    public List<string> CustomLaunchArgs { get; set; } = new();
    public List<string> Features { get; set; } = new();

    public WindowOptions Window { get; set; } = new();

    public QuickPlayOptions? QuickPlay { get; set; }

    public ProxyOptions? Proxy { get; set; }

    public OverridesOptions Overrides { get; set; } = new();

    public class VersionOptions {
        public string Number { get; set; } = "1.12.2";
        public string Type { get; set; } = "release";
        public string? Custom { get; set; }
    }

    public class MemoryOptions {
        public string Max { get; set; } = "2048M";
        public string Min { get; set; } = "512M";
    }

    public class WindowOptions {
        public int? Width { get; set; }
        public int? Height { get; set; }
        public bool Fullscreen { get; set; } = false;
    }

    public class QuickPlayOptions {
        public string Type { get; set; } = "singleplayer";
        public string? Identifier { get; set; }
        public string? Path { get; set; }
    }

    public class ProxyOptions {
        public string? Host { get; set; }
        public string Port { get; set; } = "8080";
        public string? Username { get; set; }
        public string? Password { get; set; }
    }

    public class OverridesOptions {
        public string? GameDirectory { get; set; }
        public string? MinecraftJar { get; set; }
        public string? VersionName { get; set; }
        public string? VersionJson { get; set; }
        public string? Directory { get; set; }
        public string? Natives { get; set; }
        public string? AssetRoot { get; set; }
        public string? AssetIndex { get; set; }
        public string? LibraryRoot { get; set; }
        public string? Cwd { get; set; }
        public bool Detached { get; set; } = false;
        public List<string>? Classes { get; set; }
        public string? Log4jConfigurationFile { get; set; }

        public UrlOverrides Url { get; set; } = new();
    }

    public class UrlOverrides {
        public string Meta { get; set; } = "https://launchermeta.mojang.com";
        public string Resource { get; set; } = "https://resources.download.minecraft.net";
        public string MavenForge { get; set; } = "https://maven.minecraftforge.net/";
        public string DefaultRepoForge { get; set; } = "https://libraries.minecraft.net/";
        public string FallbackMaven { get; set; } = "https://search.maven.org/remotecontent?filepath=";
    }
}

public static class GameHelper {
    
    public static async Task<MinecraftVersion> PrepareGame(LaunchOptions options, string gameRoot) {
        string root = options.Overrides.GameDirectory ?? gameRoot;

        MinecraftVersion baseVersion;
        if (!string.IsNullOrEmpty(options.Overrides.VersionJson)) {
            string jsonContent = await File.ReadAllTextAsync(options.Overrides.VersionJson);
            baseVersion = JsonSerializer.Deserialize<MinecraftVersion>(jsonContent, LauncherConstants._jsonOptions)!;
        } else {
            string versionUrl = await VersionsHelper.GetVersionUrlAsync(options.Version.Number);
            baseVersion = await VersionsHelper.GetVersionDetailsAsync(versionUrl);
        }

        MinecraftVersion workingVersion = baseVersion;

        switch (options.ModLoader) {
            case LaunchOptions.ModLoaderType.Forge:
                var forgeMeta = await ForgeHelper.ProcessForge(baseVersion, root);
                if (forgeMeta != null) {
                    workingVersion = baseVersion; 
                }
                break;
        }

        if (baseVersion.AssetIndex == null) 
            throw new Exception("Erreur : L'index des assets est manquant dans le manifest de version.");

        string assetRoot = options.Overrides.AssetRoot ?? Path.Combine(root, "assets");
        AssetsIndex assetsIndex = await AssetsHelper.GetAssetsIndexAsync(baseVersion.AssetIndex.Url!);
        await AssetsHelper.DownloadAssetsParallel(assetsIndex, assetRoot);

        string libRoot = options.Overrides.LibraryRoot ?? Path.Combine(root, "libraries");
        await LibrariesHelper.DownloadLibraries(workingVersion.Libraries!, libRoot);
        
        await VersionsHelper.DownloadClientJar(baseVersion, root);
        LibrariesHelper.ExtractNatives(workingVersion, root);

        return workingVersion;
    }

    public static string BuildClasspath(MinecraftVersion version, string gameRoot) {
        var paths = new List<string>();
        string libRoot = Path.Combine(gameRoot, "libraries");

        string clientJar = Path.Combine(gameRoot, "versions", version.Id!, $"{version.Id}.jar");
        if (File.Exists(clientJar)) paths.Add(clientJar);

        if (version.Libraries != null) {
            foreach (var lib in version.Libraries) {
                if (!LibrariesHelper.IsAllowed(lib.Rules, OsHelper.GetOSName())) continue;

                string? relPath = lib.Downloads?.Artifact?.Path ?? ForgeHelper.GeneratePathFromArtifactName(lib.Name!);
                
                string fullPath = Path.Combine(libRoot, relPath);
                
                if (File.Exists(fullPath)) {
                    paths.Add(fullPath);
                } else {
                    Console.WriteLine($"[Classpath] Manquant: {fullPath}");
                }
            }
        }
        return string.Join(Path.PathSeparator.ToString(), paths);
    }

    public static List<string> GenerateGameArguments(MinecraftVersion version, AuthenticateResponse auth, string gameRoot) {
        string? rawArgs = version.MinecraftArguments;

        if (string.IsNullOrEmpty(rawArgs)) {
            return new List<string>(); 
        }

        rawArgs = rawArgs.Replace("${auth_player_name}", auth.User.Username);
        rawArgs = rawArgs.Replace("${version_name}", version.Id ?? "Lentia");
        rawArgs = rawArgs.Replace("${game_directory}", gameRoot);
        rawArgs = rawArgs.Replace("${assets_root}", Path.Combine(gameRoot, "assets"));
        rawArgs = rawArgs.Replace("${assets_index_name}", version.AssetIndex?.Id ?? "legacy");
        rawArgs = rawArgs.Replace("${auth_uuid}", auth.SelectedProfile.Id);
        rawArgs = rawArgs.Replace("${auth_access_token}", auth.AccessToken);
        rawArgs = rawArgs.Replace("${user_type}", "mojang");
        
        rawArgs = rawArgs.Replace("${user_properties}", "{}");

        return rawArgs.Split(" ", StringSplitOptions.RemoveEmptyEntries).ToList();
    }

    public static void Launch(MinecraftVersion version, AuthenticateResponse auth, string gameRoot, LaunchOptions options, Action<string> logHandler) {
        string root = options.Overrides.GameDirectory ?? gameRoot;
        string nativesDir = options.Overrides.Natives ?? Path.Combine(root, "versions", version.Id!, "natives");
        string javaPath = options.JavaPath ?? SettingsManager.ReadSettings().JavaPath;

        var jvmArgs = new List<string> {
            $"-Xms{options.Memory.Min}",
            $"-Xmx{options.Memory.Max}",
            $"-Djava.library.path={nativesDir}",
            "-Dminecraft.launcher.brand=Lentia",
            "-Dminecraft.launcher.version=1.0.0"
        };

        // string? logConfigFile = options.Overrides.Log4jConfigurationFile;
        // if (string.IsNullOrEmpty(logConfigFile) && version.Logging?.Client?.Argument != null) {
        //     logConfigFile = Path.Combine(root, "assets", "log_configs", version.Logging.Client.File?.Id ?? "client-log4j2.xml");
        //     jvmArgs.Add(version.Logging.Client.Argument.Replace("${path}", logConfigFile));
        // } else if (!string.IsNullOrEmpty(logConfigFile)) {
        //     jvmArgs.Add($"-Dlog4j.configurationFile={logConfigFile}");
        // }

        if (options.CustomArgs != null && options.CustomArgs.Any()) {
            jvmArgs.AddRange(options.CustomArgs);
        }

        string classpath = (options.Overrides.Classes != null && options.Overrides.Classes.Any()) 
            ? string.Join(Path.PathSeparator.ToString(), options.Overrides.Classes)
            : BuildClasspath(version, root);
        
        jvmArgs.Add("-cp");
        jvmArgs.Add(classpath);

        jvmArgs.Add(version.MainClass ?? "net.minecraft.client.main.Main");

        var gameArgs = GenerateGameArguments(version, auth, root);

        if (options.QuickPlay != null) {
            gameArgs.Add($"--quickPlayPath {options.QuickPlay.Path}");
            gameArgs.Add($"--quickPlay{options.QuickPlay.Type} {options.QuickPlay.Identifier}");
        }

        if (options.Window.Fullscreen) {
            gameArgs.Add("--fullscreen");
        }
        if (options.Window.Width.HasValue) {
            gameArgs.Add("--width"); gameArgs.Add(options.Window.Width.Value.ToString());
        }
        if (options.Window.Height.HasValue) {
            gameArgs.Add("--height"); gameArgs.Add(options.Window.Height.Value.ToString());
        }

        if (options.CustomLaunchArgs != null && options.CustomLaunchArgs.Any()) {
            gameArgs.AddRange(options.CustomLaunchArgs);
        }

        var startInfo = new ProcessStartInfo(javaPath) {
            WorkingDirectory = options.Overrides.Cwd ?? root,
            Arguments = string.Join(" ", jvmArgs.Concat(gameArgs).Select(a => a.Contains(" ") ? $"\"{a}\"" : a)),
            UseShellExecute = options.Overrides.Detached,
            RedirectStandardOutput = !options.Overrides.Detached,
            RedirectStandardError = !options.Overrides.Detached,
            CreateNoWindow = true
        };

        Console.WriteLine($"Start command: {javaPath} {startInfo.Arguments}");

        var process = new Process { StartInfo = startInfo, EnableRaisingEvents = true };

        if (!options.Overrides.Detached) {
            process.OutputDataReceived += (s, e) => { if (e.Data != null) logHandler(e.Data); };
            process.ErrorDataReceived += (s, e) => { if (e.Data != null) logHandler($"[ERROR] {e.Data}"); };
        }

        process.Exited += (sender, e) => {
            logHandler("GAME_CLOSED");
        };

        if (process.Start()) {
            if (!options.Overrides.Detached) {
                process.BeginOutputReadLine();
                process.BeginErrorReadLine();
            }
        }
    }

}