using System.Runtime.InteropServices;
using System.Text.Json;
using System.Text.Json.Serialization;

namespace Nebula.Shared.Utils;

public static class RidUtility
{
    public static string? FindBestRid(ICollection<string> runtimes, string? currentRid = null)
    {
        var catalog = LoadRidCatalog();

        if (currentRid == null)
        {
            var reportedRid = RuntimeInformation.RuntimeIdentifier;
            if (!catalog.Runtimes.ContainsKey(reportedRid))
            {
                currentRid = GuessRid();
                Console.WriteLine(".NET reported unknown RID: {0}, guessing: {1}", reportedRid, currentRid);
            }
            else
            {
                currentRid = reportedRid;
            }
        }

        // Breadth-first search.
        var q = new Queue<string>();
        if (!catalog.Runtimes.TryGetValue(currentRid, out var root))
            // RID doesn't exist in catalog???
            return null;

        root.Discovered = true;
        q.Enqueue(currentRid);

        while (q.TryDequeue(out var v))
        {
            if (runtimes.Contains(v)) return v;

            foreach (var w in catalog.Runtimes[v].Imports)
            {
                var r = catalog.Runtimes[w];
                if (!r.Discovered)
                {
                    q.Enqueue(w);
                    r.Discovered = true;
                }
            }
        }

        return null;
    }

    private static string GuessRid()
    {
        if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
            return RuntimeInformation.ProcessArchitecture switch
            {
                Architecture.X86 => "linux-x86",
                Architecture.X64 => "linux-x64",
                Architecture.Arm => "linux-arm",
                Architecture.Arm64 => "linux-arm64",
                _ => "unknown"
            };

        if (RuntimeInformation.IsOSPlatform(OSPlatform.FreeBSD))
            return RuntimeInformation.ProcessArchitecture switch
            {
                Architecture.X64 => "freebsd-x64",
                _ => "unknown"
            };

        if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
            return RuntimeInformation.ProcessArchitecture switch
            {
                Architecture.X86 => "win-x86",
                Architecture.X64 => "win-x64",
                Architecture.Arm => "win-arm",
                Architecture.Arm64 => "win-arm64",
                _ => "unknown"
            };

        if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
            return RuntimeInformation.ProcessArchitecture switch
            {
                Architecture.X64 => "osx-x64",
                Architecture.Arm64 => "osx-arm64",
                _ => "unknown"
            };

        return "unknown";
    }

    private static RidCatalog LoadRidCatalog()
    {
        using var stream = typeof(RidCatalog).Assembly.GetManifestResourceStream("Utility.runtime.json")!;
        var ms = new MemoryStream();
        stream.CopyTo(ms);

        return JsonSerializer.Deserialize<RidCatalog>(ms.GetBuffer().AsSpan(0, (int)ms.Length))!;
    }

#pragma warning disable 649
    private sealed class RidCatalog
    {
        [JsonInclude] [JsonPropertyName("runtimes")]
        public Dictionary<string, Runtime> Runtimes = default!;

        public class Runtime
        {
            public bool Discovered;

            [JsonInclude] [JsonPropertyName("#import")]
            public string[] Imports = default!;
        }
    }
#pragma warning restore 649
}