using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.Json;

namespace VeloeMinecraftLauncher.MinecraftLauncher;

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(Settings.minecraftForlderPath + "versions/" + version.Id + "/natives/")}\"");
        else
            returnString.Append($"-Djava.library.path=\"{Path.GetDirectoryName(Settings.minecraftForlderPath + "versions/" + version.InheritsFrom + "/natives/")}\""); //for forge, vanilla optifine, fabric

        returnString.Append(" -cp \"");

        // 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)
                {
                    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 no path
            if (library.Downloads is null) //for optifine
            {

                string name = library.Name;
                var dirs = name.Split(':');
                dirs[0] = dirs[0].Replace('.','/');
                var libPath = String.Empty;
                foreach (var dir in dirs)
                {
                    libPath += dir+"/";
                }
                if (rulesVaild)
                    returnString.Append(Path.GetFullPath(Settings.minecraftForlderPath + "libraries/" + libPath + dirs[dirs.Length-2]+"-"+dirs[dirs.Length-1]+".jar") + separator);

                continue;
            }

            if (rulesVaild)
                returnString.Append(Path.GetFullPath(Settings.minecraftForlderPath + "libraries/" + library.Downloads.Artifact.Path) + separator);

        }
        
        Entity.Version.Version inheritsFrom = new();
        if (version.InheritsFrom is null)
            returnString.Append(Path.GetFullPath(Settings.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(Settings.minecraftForlderPath + "versions/" + version.InheritsFrom + "/" + version.InheritsFrom + ".json");
            
            inheritsFrom = JsonSerializer.Deserialize<Entity.Version.Version>(inheritsJsonString, new JsonSerializerOptions { PropertyNameCaseInsensitive = true });

            foreach (var library in inheritsFrom.Libraries)
            {
                bool rulesVaild = true;
                if (library.Natives is not null)
                    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(Settings.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($"{Settings.minecraftForlderPath}versions/{version.Id}/{version.Id}.jar");

            if (fileInfo.Exists && fileInfo.Length > 0)
                returnString.Append(Path.GetFullPath(Settings.minecraftForlderPath + "versions/" + version.Id + "/" + version.Id + ".jar"));//for optifine
            else
                returnString.Append(Path.GetFullPath(Settings.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)
                        continue;

                    var type = argument.GetType();

                    if (!(argument is JsonElement))
                        continue;

                    if (!(((JsonElement)argument).ValueKind == JsonValueKind.String))
                        continue;

                    var value = ((JsonElement)argument).Deserialize(typeof(string)) as string;
  
                    //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(Settings.minecraftForlderPath + "libraries/"));
                    }
                    if (value.Contains("${classpath_separator}"))
                    {
                        value = value.Replace("${classpath_separator}", separator.ToString());
                    }

                    bool containsArgWithValue = value.Contains('=');
                    bool containsPathValueOnly = value.StartsWith(Path.GetDirectoryName(Settings.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 (Settings.setMaxRam)
            returnString.Append($" -Xmx{Settings.maxRam}M ");


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

        List<string> args = new List<string>();
        List<string> argsValues = new List<string>();
       
        if (version.Arguments is not null)
        {
            foreach (var argument in version.Arguments.Game)
            {
                if (argument is null)
                    continue;

                var type = argument.GetType();

                if (!(argument is JsonElement))
                    continue;

                if (!(((JsonElement)argument).ValueKind == JsonValueKind.String))
                    continue;

                var value = ((JsonElement)argument).Deserialize(typeof(string));

                //if ()

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

                args.Add(value as string);
            }
        }
        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.Count(); 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 Dictionary<string, bool>()
        {
            { "--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)
                        continue;

                    var type = argument.GetType();

                    if (!(argument is JsonElement))
                        continue;

                    if (!(((JsonElement)argument).ValueKind == JsonValueKind.String))
                        continue;

                    var value = ((JsonElement)argument).Deserialize(typeof(string));

                    if (!(value as string).Contains("--"))
                    {
                        argsValues.Add(value as string);
                        continue;
                    }
                    if (!argsProvided.GetValueOrDefault(value as string, false))
                    {
                        args.Add(value as string);
                        argsProvided[value as string] = 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.Count(); i++)
                {
                    if (minecraftArguments[i].Contains("--"))
                    {
                        args.Add(minecraftArguments[i]);
                        argsProvided[minecraftArguments[i]] = true;
                    }
                    else
                        argsValues.Add(minecraftArguments[i]);
                }
            }
        }

        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.Where(x => x.Contains("forge")).Count() > 0 || version.Id.ToLower().Contains("fabric")))
                        returnString.Append(" --gameDir " + "\"" + Path.GetDirectoryName(Settings.minecraftForlderPath) + "\"");
                    else
                        returnString.Append(" --gameDir " + "\"" + Path.GetDirectoryName(Settings.minecraftForlderPath + "versions/" + version.Id + "/") + "\"");
                    break;
                case "--assetsDir":
                    //for forge
                    if (version.InheritsFrom is null)
                        returnString.Append(" --assetsDir " + "\"" + Path.GetDirectoryName(Settings.minecraftForlderPath + "assets/" + version.Assets + "/") + "\"");
                    else
                        returnString.Append(" --assetsDir " + "\"" + Path.GetDirectoryName(Settings.minecraftForlderPath + "assets/" + inheritsFrom.Assets + "/") + "\"");
                    break;
                case "--assetIndex":
                    //for forge
                    if (version.InheritsFrom is null)
                        returnString.Append(" --assetIndex " + version.Assets);
                    else
                        returnString.Append(" --assetIndex " + inheritsFrom.Assets);
                    break;
                case "--uuid":
                    returnString.Append(" --uuid sample_token");
                    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;

            }
        }
        return returnString.ToString();
    }
}
