using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Text.Json;

namespace VeloeMinecraftLauncher.Utils.Starter;

internal static class StartCommandBuilder
{
    public static string Build(Entity.Version.Version version, string username)
    {
        var returnString = new StringBuilder();

        char separator = ':';

        if (OperatingSystem.IsWindows())
            separator = ';';

        if (version is null)
            return returnString.ToString();

        // setting natives folder
        if (version.InheritsFrom is null)
            returnString.Append($"-Djava.library.path=\"{Path.GetDirectoryName(SettingsService.Instance.MinecraftForlderPath + "versions/" + version.Id + "/natives/")}\"");
        else
            returnString.Append($"-Djava.library.path=\"{Path.GetDirectoryName(SettingsService.Instance.MinecraftForlderPath + "versions/" + version.InheritsFrom + "/natives/")}\""); //for forge, vanilla optifine, fabric

        returnString.Append(" -cp \"");
        var libHashes = new HashSet<string>();
        // add libraries
        foreach (var library in version.Libraries)
        {
            bool rulesVaild = true;
            //check if native
            if (library.Natives is not null)
                continue;

            //rules check
            if (library.Rules is not null)
            {
                rulesVaild = false;
                foreach (var rule in library.Rules)
                {
                    if (rule.Action == "allow" && rule.Os is null)
                    {
                        rulesVaild = true;
                        continue;
                    }

                    if (rule.Os is not null &&
                        rule.Action == "allow" &&
                            (rule.Os.Name == "linux" && OperatingSystem.IsLinux() ||
                            rule.Os.Name == "windows" && OperatingSystem.IsWindows()))
                    {
                        rulesVaild = true;
                        continue;
                    }
                }
            }

            bool _separatorFound = false;
            bool IsSecondSeparator(char symbol,char separator = ':')
            {
                if (symbol == separator)
                    if (_separatorFound)
                    {
                        _separatorFound = false;
                        return false;
                    }
                    else
                        _separatorFound = true;
                return true;
            }

            if (rulesVaild)
            {                                                                                                                                   // for optifine                     // default
                returnString.Append(Path.GetFullPath(SettingsService.Instance.MinecraftForlderPath + "libraries/" + (library.Downloads is null ? GetLibPathFromName(library.Name) : library.Downloads.Artifact.Path)) + separator);
                libHashes.Add(string.Concat(library.Name.TakeWhile(x => IsSecondSeparator(x))));
            }
        }

        Entity.Version.Version? inheritsFrom = null;
        if (version.InheritsFrom is null)
            returnString.Append(Path.GetFullPath(SettingsService.Instance.MinecraftForlderPath + "versions/" + version.Id + "/" + version.Id + ".jar")); //main jar file
        else
        {
            //for forge, vanilla optifine, fabric
            //add libraries from inherited version
            var inheritsJsonString = File.ReadAllText(SettingsService.Instance.MinecraftForlderPath + "versions/" + version.InheritsFrom + "/" + version.InheritsFrom + ".json");

            inheritsFrom = JsonSerializer.Deserialize<Entity.Version.Version>(inheritsJsonString, new JsonSerializerOptions { PropertyNameCaseInsensitive = true });

            foreach (var library in inheritsFrom?.Libraries ?? new())
            {
                bool rulesVaild = true;
                if (library.Natives is not null)
                    continue;

                if (libHashes.Contains(string.Join(':', library.Name.Split(':').Take(2))))
                    continue;

                if (library.Rules is not null)
                {
                    rulesVaild = false;
                    foreach (var rule in library.Rules)
                    {
                        bool isRuleOsExist = rule.Os is not null;

                        if (rule.Action == "allow" && rule.Os is null)
                        {
                            rulesVaild = true;
                            continue;
                        }

                        if (rule.Os is not null)
                            if (rule.Action == "allow" &&
                                    (rule.Os.Name == "linux" && OperatingSystem.IsLinux() ||
                                    rule.Os.Name == "windows" && OperatingSystem.IsWindows())
                                )
                            {
                                rulesVaild = true;
                                continue;
                            }
                    }
                }

                if (rulesVaild)
                    returnString.Append(Path.GetFullPath(SettingsService.Instance.MinecraftForlderPath + "libraries/" + library.Downloads.Artifact.Path) + separator);

            }

            //main jar file
            //check here if there is jar file not inherited
            //check if it is not 0kb size (fabric)
            var fileInfo = new FileInfo($"{SettingsService.Instance.MinecraftForlderPath}versions/{version.Id}/{version.Id}.jar");

            if (fileInfo.Exists && fileInfo.Length > 0)
                returnString.Append(Path.GetFullPath(SettingsService.Instance.MinecraftForlderPath + "versions/" + version.Id + "/" + version.Id + ".jar"));//for optifine
            else
                returnString.Append(Path.GetFullPath(SettingsService.Instance.MinecraftForlderPath + "versions/" + version.InheritsFrom + "/" + version.InheritsFrom + ".jar"));//for forge
        }

        returnString.Append('"');

        //check for jvm fabric options

        if (version.Arguments is not null && version.InheritsFrom is not null)
        {
            if (version.Arguments.Jvm is not null)
                foreach (var argument in version.Arguments.Jvm)
                {
                    if (argument is null ||
                        argument is not JsonElement jsonArgument ||
                        jsonArgument.ValueKind != JsonValueKind.String ||
                        jsonArgument.Deserialize(typeof(string)) is not string value ||
                        value.Contains("-Djava.library.path") || //this for fabric for 1.5.2
                        value.Contains("-cp") ||                // this args already added to args
                        value.Equals("${classpath}")) continue;


                    //for new forge versions (1.18, 1.19)
                    if (value.Contains("${version_name}"))
                    {
                        value = value.Replace("${version_name}", version.InheritsFrom);
                    }
                    if (value.Contains("${library_directory}"))
                    {
                        value = value.Replace("${library_directory}", Path.GetDirectoryName(SettingsService.Instance.MinecraftForlderPath + "libraries/"));
                    }
                    if (value.Contains("${classpath_separator}"))
                    {
                        value = value.Replace("${classpath_separator}", separator.ToString());
                    }

                    bool containsArgWithValue = value.Contains('=');
                    bool containsPathValueOnly = value.StartsWith(Path.GetDirectoryName(SettingsService.Instance.MinecraftForlderPath + "libraries/")!);
                    //value = value.Replace(" ", "");
                    if (containsArgWithValue)
                        value = value.Replace("=", "=\"");
                    returnString.Append(' ');
                    if (containsPathValueOnly)
                        returnString.Append('"');
                    returnString.Append(value);
                    if (containsArgWithValue || containsPathValueOnly)
                        returnString.Append('"');
                }
        }

        //max ram
        if (SettingsService.Instance.SetMaxRam)
            returnString.Append($" -Xmx{SettingsService.Instance.MaxRam}M ");


        returnString.Append(" " + version.MainClass);

        List<string> args = new();
        List<string> argsValues = new();

        if (version.Arguments is not null)
        {
            foreach (var argument in version.Arguments.Game)
            {
                if (argument is null ||
                    argument is not JsonElement jsonArgument ||
                    jsonArgument.ValueKind != JsonValueKind.String ||
                    jsonArgument.Deserialize(typeof(string)) is not string value) continue;

                if (!value.Contains("--"))
                {
                    argsValues.Add(value);
                    continue;
                }

                args.Add(value);
            }
        }
        if (version.MinecraftArguments is not null)
        {
            var minecraftArguments = version.MinecraftArguments.Split(' ');
            //args = version.minecraftArguments.Split(' ').Where(x=> x.Contains("--")).ToList();
            for (int i = 0; i < minecraftArguments.Length; i++)
            {
                if (minecraftArguments[i].Contains("--"))
                {
                    args.Add(minecraftArguments[i]);
                    if (minecraftArguments[i + 1] is not null)
                        argsValues.Add(minecraftArguments[i + 1]);
                }
                //else
                //    argsValues.Add(minecraftArguments[i]);
            }
        }


        Dictionary<string, bool> argsProvided = new()
        {
            { "--username", false },
            { "--version", false },
            { "--gameDir", false},
            { "--assetsDir", false },
            { "--assetIndex", false },
            { "--uuid", false },
            { "--accessToken", false},
            { "--userType", false},
            { "--tweakClass", false }
        };

        //check if all required arguments provided. Use inheritFrom if needed
        foreach (var arg in args)
        {
            argsProvided[arg] = true;
        }

        if (argsProvided.ContainsValue(false))
        {
            if (inheritsFrom?.Arguments is not null)
            {
                foreach (var argument in inheritsFrom.Arguments.Game)
                {
                    if (argument is null ||
                        argument is not JsonElement jsonArgument ||
                        jsonArgument.ValueKind != JsonValueKind.String ||
                        jsonArgument.Deserialize(typeof(string)) is not string value) continue;

                    if (!value.Contains("--"))
                    {
                        argsValues.Add(value);
                        continue;
                    }
                    if (!argsProvided.GetValueOrDefault(value, false))
                    {
                        args.Add(value);
                        argsProvided[value] = true;
                    }
                }
            }
            if (inheritsFrom?.MinecraftArguments is not null)
            {
                var minecraftArguments = inheritsFrom.MinecraftArguments.Split(' ');
                //args = version.minecraftArguments.Split(' ').Where(x=> x.Contains("--")).ToList();
                for (int i = 0; i < minecraftArguments.Length; i++)
                {
                    if (minecraftArguments[i].Contains("--"))
                    {
                        args.Add(minecraftArguments[i]);
                        argsProvided[minecraftArguments[i]] = true;
                    }
                    else
                        argsValues.Add(minecraftArguments[i]);
                }
            }
        }

        //for old versions username fix

        if (!args.Contains("--username"))
            returnString.Append(" " + username);

        if (!args.Contains("--uuid")) // or maybe uuid
            returnString.Append(" " + NameUUIDFromBytes(Encoding.UTF8.GetBytes($"OfflinePlayer:{username}")).ToString());

        // add ${auth_session} for old versions

        for (int i = 0; i < args.Count; i++)
        {
            switch (args[i])
            {
                case "--username":
                    returnString.Append(" --username " + username);
                    break;
                case "--version":
                    returnString.Append(" --version " + version.Id /*"'Copy of VeloeLauncher'"*/);
                    break;
                case "--gameDir":
                    //for forge
                    if (!(argsValues.Any(x => x.Contains("forge")) || version.MainClass.Contains("fabric") || version.Id.ToLower().Contains("fabric")))
                        returnString.Append(" --gameDir " + "\"" + Path.GetDirectoryName(SettingsService.Instance.MinecraftForlderPath) + "\"");
                    else
                        returnString.Append(" --gameDir " + "\"" + Path.GetDirectoryName(SettingsService.Instance.MinecraftForlderPath + "versions/" + version.Id + "/") + "\"");
                    break;
                case "--assetsDir":
                    //for forge
                    if (version.InheritsFrom is null)
                        returnString.Append(" --assetsDir " + "\"" + Path.GetDirectoryName(SettingsService.Instance.MinecraftForlderPath + "assets/" + version.Assets + "/") + "\"");
                    else if (inheritsFrom is not null)
                        returnString.Append(" --assetsDir " + "\"" + Path.GetDirectoryName(SettingsService.Instance.MinecraftForlderPath + "assets/" + inheritsFrom.Assets + "/") + "\"");
                    break;
                case "--assetIndex":
                    //for forge
                    if (version.InheritsFrom is null)
                        returnString.Append(" --assetIndex " + version.Assets);
                    else if (inheritsFrom is not null)
                        returnString.Append(" --assetIndex " + inheritsFrom.Assets);
                    break;
                case "--uuid":
                    returnString.Append($" --uuid {NameUUIDFromBytes(Encoding.UTF8.GetBytes($"OfflinePlayer:{username}")).ToString()}");
                    break;
                case "--accessToken":
                    returnString.Append(" --accessToken sample_token");
                    break;
                case "--userType":
                    returnString.Append(" --userType offline");
                    break;
                case "--userProperties":
                    returnString.Append(" --userProperties  " + "{\"veloelauncher\":[\"wtfisthis\"]}");
                    break;
                case "--versionType":
                    returnString.Append(" --versionType " + version.Type);
                    break;
                case "--tweakClass":
                    returnString.Append(" --tweakClass");
                    if (argsValues[i] is not null)
                        returnString.Append(" " + argsValues[i]);
                    break;
                //for new forge
                default:
                    if (argsValues[i] is not null && !argsValues[i].Contains("${"))
                        returnString.Append($" {args[i]} {argsValues[i]}");
                    break;
            }
        }

        if (SettingsService.Instance.FullScreen)
            returnString.Append(" --fullscreen");

        if (SettingsService.Instance.CustomSize && !SettingsService.Instance.FullScreen)
            returnString.Append($" --width {SettingsService.Instance.ClientWidth} --height {SettingsService.Instance.ClientHeight}");

        return returnString.ToString();
    }

    public static string GetLibPathFromName(string name)
    {
        string libPath = GetLibRelativePathFromName(name);

#if DEBUG
        if (File.Exists(SettingsService.Instance.MinecraftForlderPath + "libraries/" + libPath))
            Debug.WriteLine($"Lib exists: {SettingsService.Instance.MinecraftForlderPath + "libraries/" + libPath}");
        else
            Debug.WriteLine($"Lib not exists: {SettingsService.Instance.MinecraftForlderPath + "libraries/" + libPath}");
#endif
        return libPath;
    }

    public static string GetLibRelativePathFromName(string name)
    {
        Debug.WriteLine($"GameLibPathFromName: {name}");
        var dirs = name.Split(':');
        dirs[0] = dirs[0].Replace('.', '/');
        var libPath = string.Empty;
        foreach (var dir in dirs)
        {
            libPath += dir + "/";
        }

        libPath += dirs[^2] + "-" + dirs[^1] + ".jar";
        return libPath;
    }

    public static string NameUUIDFromBytes(byte[] input)
    {
        MD5 md5 = MD5.Create();
        byte[] hash = md5.ComputeHash(input);
        hash[6] &= 0x0f;
        hash[6] |= 0x30;
        hash[8] &= 0x3f;
        hash[8] |= 0x80;
        string hex = BitConverter.ToString(hash).Replace("-", string.Empty).ToLower();
        return hex.Insert(8, "-").Insert(13, "-").Insert(18, "-").Insert(23, "-");
    }
}
