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

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

public class ModpackDownloadTaskFactory : INetworkTaskFactory
{
    INetworkTaskFactory _implementationVersion;
    private GameVersionDescriptor _descriptor;

    public ModpackDownloadTaskFactory(GameVersionDescriptor descriptor)
    {
        if(descriptor.modpack is null)
            throw new ArgumentNullException(nameof(descriptor.modpack));
        
        _descriptor = descriptor;
        _implementationVersion = descriptor.modpack.format_version switch
        {
            1 => new MyModpackV1DownloadTaskFactory(descriptor),
            _ => throw new Exception($"Unknown Modpack format_version: {descriptor.modpack.format_version}")
        };
    }

    public Task<NetworkTask?> CreateAsync(bool checkHashes)
    {
        // Modpacks can include libraries.
        // This libraries are downloaded in launcher_dir/libraries through symbolic link.
        var localLibsDir = Path.Concat(PathHelper.GetVersionDir(_descriptor.id), "libraries");
        var globalLibsDir = PathHelper.GetLibrariesDir();
        var dirInfo = new DirectoryInfo(localLibsDir.ToString());
        // delete dir if it isn't symlink
        if(dirInfo.Exists && dirInfo.LinkTarget is null)
            dirInfo.Delete(true);
        if (!dirInfo.Exists)
        {
            LauncherApp.Logger.LogInfo(nameof(ModpackDownloadTaskFactory),
                $"Creating symbolic link '{localLibsDir}' -> '{globalLibsDir}'");
            dirInfo.CreateAsSymbolicLink(globalLibsDir.ToString());
        }
        
        return _implementationVersion.CreateAsync(checkHashes);
    }
}

public class MyModpackV1DownloadTaskFactory : INetworkTaskFactory
{
    private readonly GameVersionDescriptor _descriptor;
    private IOPath _modpackDescriptorPath;
    private IOPath _versionDir;
    private MyModpackV1? _modpack;
    private HashSet<IOPath> _filesToDosnload = new();

    public MyModpackV1DownloadTaskFactory(GameVersionDescriptor descriptor)
    {
        _descriptor = descriptor;
        _modpackDescriptorPath = PathHelper.GetModpackDescriptorPath(_descriptor.id);
        _versionDir = PathHelper.GetVersionDir(_descriptor.id);
    }
    
    public async Task<NetworkTask?> CreateAsync(bool checkHashes)
    {
        if(_descriptor.modpack is null)
            throw new ArgumentNullException(nameof(_descriptor.modpack));
        
        (_modpack, bool didDownloadModpackDescriptor) = await NetworkHelper.ReadOrDownloadAndDeserialize<MyModpackV1>(
            _modpackDescriptorPath,
            _descriptor.modpack.artifact.url, 
            _descriptor.modpack.artifact.sha1,
            checkHashes);
        if (_modpack.format_version != _descriptor.modpack.format_version)
            throw new Exception($"Modpack.format_version mismatches descriptor.modpack.version: " +
                                $"{_modpack.format_version} != {_descriptor.modpack.format_version}");
        
        NetworkTask? networkTask = null;
        if(!_modpack.CheckFiles(_versionDir, checkHashes, didDownloadModpackDescriptor, _filesToDosnload))
        {
            networkTask = new NetworkTask(
                $"modpack '{_descriptor.assetIndex.id}'",
                _modpack.zip.size,
                Download
            );
        }

        return networkTask;
    }

    private async Task Download(NetworkProgressReporter pr, CancellationToken ct)
    {
        LauncherApp.Logger.LogInfo(nameof(NetworkHelper), $"started downloading modpack '{_modpack!.name}'");
        if(string.IsNullOrEmpty(_modpack.zip.url))
            throw new Exception($"modpack '{_modpack.name}' doesn't have a url to download");

        var _archivePath = Path.Concat(PathHelper.GetCacheDir(), "modpacks", _modpack.name + ".zip");
        await NetworkHelper.DownloadFile(_modpack.zip.url, _archivePath, ct, pr.AddBytesCount);
        
        await using var zipf = File.OpenRead(_archivePath);
        using var archive = new ZipArchive(zipf);
        foreach (var entry in archive.Entries)
        {
            IOPath localPath = new(entry.FullName);
            if(_filesToDosnload.Contains(localPath))
            {
                var real_path = Path.Concat(_versionDir, localPath);
                Directory.Create(real_path.ParentDir());
                entry.ExtractToFile(real_path.ToString(), true);
            }
        }
        
        LauncherApp.Logger.LogInfo(nameof(NetworkHelper), $"finished downloading modpack '{_modpack.name}'");
    }
}