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 JavaDownloadTaskFactory : INetworkTaskFactory
{
    private const string CATALOG_URL =
        "https://launchermeta.mojang.com/v1/products/java-runtime/2ec0cc96c44e5a76b9c8b7c39df7210883d12871/all.json";
    private GameVersionDescriptor _descriptor;
    private IOPath _javaVersionDir;
    private JavaDistributiveManifest? _distributiveManifest;
    private List<(IOPath path, JavaDistributiveElementProps props)> _filesToDownload = new();
    
    public JavaDownloadTaskFactory(GameVersionDescriptor descriptor)
    {
        _descriptor = descriptor;
        _javaVersionDir = PathHelper.GetJavaRuntimeDir(_descriptor.javaVersion.component);
    }

    public async Task<NetworkTask?> CreateAsync(bool checkHashes)
    {
        var catalog = await DownloadStringAndDeserialize<JavaVersionCatalog>(CATALOG_URL);
        var versionProps = catalog.GetVersionProps(_descriptor.javaVersion);
        _distributiveManifest = await DownloadStringAndDeserialize<JavaDistributiveManifest>(versionProps.manifest.url);
        
        NetworkTask? networkTask = null;
        if (!CheckFiles(checkHashes))
        {
            networkTask = new NetworkTask(
                $"java runtime '{_descriptor.javaVersion.component}'",
                GetTotalSize(),
                Download
            );
        }
        
        return networkTask;
    }

    private bool CheckFiles(bool checkHashes)
    {
        _filesToDownload.Clear();
        foreach (var pair in _distributiveManifest!.files)
        {
            if (pair.Value.type != "file")
                continue;

            if (pair.Value.downloads != null)
            {
                var artifact = pair.Value.downloads;
                IOPath file_path = Path.Concat(_javaVersionDir, pair.Key);
                if (!HashHelper.CheckFileSHA1(file_path, artifact.raw.sha1, checkHashes))
                {
                    _filesToDownload.Add((file_path, pair.Value));
                }
            }
        }

        return _filesToDownload.Count == 0;
    }
    
    private long GetTotalSize()
    {
        long totalSize = 0;
        foreach (var file in _filesToDownload)
        {
            if(file.props.downloads == null)
                continue;
            totalSize += file.props.downloads.lzma?.size ?? file.props.downloads.raw.size;
        }
        return totalSize;
    }

    private async Task Download(NetworkProgressReporter pr, CancellationToken ct)
    {
        LauncherApp.Logger.LogInfo(nameof(NetworkHelper), "started downloading java runtime " +
                    $"{_descriptor.javaVersion.majorVersion} '{_descriptor.javaVersion.component}'");
        
        ParallelOptions opt = new()
        {
            MaxDegreeOfParallelism = LauncherApp.Config.max_parallel_downloads,
            CancellationToken = ct
        };
        await Parallel.ForEachAsync(_filesToDownload, opt, async (f, _ct) =>
        {
            if (f.props.downloads!.lzma != null)
            {
                LauncherApp.Logger.LogDebug(nameof(NetworkHelper), $"downloading lzma-compressed file '{f.path}'");
                await using var pipe = new TransformStream(await GetStream(f.props.downloads.lzma.url, _ct));
                pipe.AddTransform(pr.AddBytesCount);
                await using var fs = File.OpenWrite(f.path);
                LZMAHelper.Decompress(pipe, fs);
            }
            else
            {
                LauncherApp.Logger.LogDebug(nameof(NetworkHelper), $"downloading raw file '{f.path}'");
                await DownloadFile(f.props.downloads.raw.url, f.path, _ct, pr.AddBytesCount);
            }
            
            if(!OperatingSystem.IsWindows() && f.props.executable is true)
            {
                LauncherApp.Logger.LogDebug(nameof(NetworkHelper), $"adding execute rights to file '{f.path}'");
                System.IO.File.SetUnixFileMode(f.path.ToString(), UnixFileMode.UserExecute);
            }
        });
        
        LauncherApp.Logger.LogInfo(nameof(NetworkHelper), "finished downloading java runtime " + 
                    $"{_descriptor.javaVersion.majorVersion} '{_descriptor.javaVersion.component}'");
    }
    
    
}