using System.IO.Compression;
using Mlaumcherb.Client.Avalonia.зримое;
using Mlaumcherb.Client.Avalonia.классы;
using Mlaumcherb.Client.Avalonia.холопы;
using static Mlaumcherb.Client.Avalonia.сеть.NetworkHelper;

namespace Mlaumcherb.Client.Avalonia.сеть.TaskFactories;

public class LibrariesDownloadTaskFactory : INetworkTaskFactory
{
    private GameVersionDescriptor _descriptor;
    private Libraries _libraries;
    private List<Libraries.JarLib> _libsToDownload = new();
    private IOPath _nativesDir;

    public LibrariesDownloadTaskFactory(GameVersionDescriptor descriptor, Libraries libraries)
    {
        _descriptor = descriptor;
        _libraries = libraries;
        _nativesDir = PathHelper.GetNativeLibrariesDir(descriptor.id);
    }
    
    public Task<NetworkTask?> CreateAsync(bool checkHashes)
    {
        NetworkTask? networkTask = null;
        if (!CheckFiles(checkHashes))
        {
            networkTask = new NetworkTask(
                $"libraries '{_descriptor.id}'",
                GetTotalSize(),
                Download
            );
        }
        
        return Task.FromResult(networkTask);
    }
    
    private bool CheckFiles(bool checkHashes)
    {
        _libsToDownload.Clear();
        bool nativeDirExists = Directory.Exists(_nativesDir);
        
        foreach (var l in _libraries.Libs)
        {
            if (!HashHelper.CheckFileSHA1(l.jarFilePath, l.artifact?.sha1, checkHashes))
            {
                _libsToDownload.Add(l);
            }
            //TODO: replace with actual native libraries check
            else if (!nativeDirExists && l is Libraries.NativeLib)
            {
                _libsToDownload.Add(l);
            }
        }

        return _libsToDownload.Count == 0;
    }
    
    private long GetTotalSize()
    {
        long total = 0;
        foreach (var l in _libsToDownload)
            total += l.artifact?.size ?? 0;
        return total;
    }

    private async Task Download(NetworkProgressReporter pr, CancellationToken ct)
    {
        LauncherApp.Logger.LogInfo(nameof(NetworkHelper), $"started downloading libraries '{_descriptor.id}'");
        
        ParallelOptions opt = new()
        {
            MaxDegreeOfParallelism = LauncherApp.Config.max_parallel_downloads,
            CancellationToken = ct
        };
        await Parallel.ForEachAsync(_libsToDownload, opt, async (l, _ct) =>
        {
            LauncherApp.Logger.LogDebug(nameof(NetworkHelper), $"downloading library '{l.name}' to '{l.jarFilePath}'");
            if(string.IsNullOrEmpty(l.artifact?.url))
                throw new Exception($"library '{l.name}' doesn't have a url to download");
            await DownloadFile(l.artifact.url, l.jarFilePath, _ct, pr.AddBytesCount);
            if (l is Libraries.NativeLib n)
            {
                await using var zipf = File.OpenRead(n.jarFilePath);
                using var archive = new ZipArchive(zipf);
                foreach (var entry in archive.Entries)
                {
                    if (n.extractionOptions?.exclude?.Contains(entry.FullName) is true or null) 
                        continue;
                    
                    var real_path = Path.Concat(_nativesDir, entry.FullName);
                    Directory.Create(real_path.ParentDir());
                    entry.ExtractToFile(real_path.ToString(), true);
                }
            }
        });
        
        LauncherApp.Logger.LogInfo(nameof(NetworkHelper), $"finished downloading libraries '{_descriptor.id}'");
    }
}