using System.IO.Compression;
using Lentia.Core.Utils;

namespace Lentia.Core.Game;

public static class LibrariesHelper {

    public static async Task DownloadLibraries(List<Library> libraries, string root) {
        string osName = OsHelper.GetOSName();
        string librariesDir = Path.Combine(root);

        var downloadsToProcess = new List<DownloadInfo>();

        foreach (var library in libraries) {
            if (!IsAllowed(library.Rules, osName)) continue;

            if (library.Downloads?.Artifact != null) {
                downloadsToProcess.Add(library.Downloads.Artifact);
            }

            if (library.Natives != null && library.Natives.TryGetValue(osName, out string? classifierKey)) {
                if (library.Downloads?.Classifiers != null && library.Downloads.Classifiers.TryGetValue(classifierKey, out var nativeInfo)) {
                    downloadsToProcess.Add(nativeInfo);
                }
            }
        }

        var uniqueDownloads = downloadsToProcess
            .GroupBy(d => d.Path)
            .Select(g => g.First())
            .ToList();

        var options = new ParallelOptions { MaxDegreeOfParallelism = 8 };
        await Parallel.ForEachAsync(uniqueDownloads, options, async (info, token) => {
            await DownloadDownloadInfo(info, librariesDir);
        });
}

    private static async Task DownloadDownloadInfo(DownloadInfo info, string baseDir) {
        if (string.IsNullOrEmpty(info.Url) || string.IsNullOrEmpty(info.Path)) return;
        
        string localPath = Path.Combine(baseDir, info.Path);
        
        if (File.Exists(localPath) && new FileInfo(localPath).Length == info.Size) return;

        await FileHelper.DownloadFileAsync(info.Url, localPath);
    }

    public static void ExtractNatives(MinecraftVersion version, string root) {
        string osName = OsHelper.GetOSName();
        string librariesDir = Path.Combine(root, "libraries");
        string nativesDir = Path.Combine(root, "versions", version.Id!, "natives");

        if (Directory.Exists(nativesDir)) Directory.Delete(nativesDir, true);
        Directory.CreateDirectory(nativesDir);

        foreach (var library in version.Libraries!) {
            if (!IsAllowed(library.Rules, osName) || library.Natives == null) continue;

            if (library.Natives.TryGetValue(osName, out string? classifierKey)) {
                if (library.Downloads?.Classifiers != null && library.Downloads.Classifiers.TryGetValue(classifierKey, out var nativeInfo)) {
                    
                    string jarPath = Path.Combine(librariesDir, nativeInfo.Path!);
                    if (!File.Exists(jarPath)) continue;

                    using (ZipArchive archive = ZipFile.OpenRead(jarPath)) {
                        foreach (ZipArchiveEntry entry in archive.Entries) {
                            if (entry.FullName.EndsWith("/") || entry.FullName.StartsWith("META-INF")) continue;
                            string destinationPath = Path.Combine(nativesDir, entry.Name);
                            entry.ExtractToFile(destinationPath, true);
                        }
                    }
                }
            }
        }
    }

    public static bool IsAllowed(List<Rule>? rules, string osName) {
        if (rules == null || rules.Count == 0) return true;
        
        bool allowed = false;
        foreach (var rule in rules) {
            if (rule.Os == null || rule.Os.Name == osName) {
                allowed = rule.Action == "allow";
            }
        }
        return allowed;
    }

}