using CliWrap;
using DTLib.Extensions;
using Млаумчерб.Клиент.видимое;
using Млаумчерб.Клиент.классы;
using static Млаумчерб.Клиент.классы.Пути;

namespace Млаумчерб.Клиент;

public class GameVersionDescriptor
{
    private readonly GameVersionProps _props;
    public string Name => _props.Name;
    public IOPath WorkingDirectory { get; }

    private IOPath JavaExecutableFilePath;

    private MinecraftVersionDescriptor descriptor;
    private JavaArguments javaArgs;
    private GameArguments gameArgs;
    private CancellationTokenSource? gameCts;
    private CancellationTokenSource? downloadCts;
    private CommandTask<CommandResult>? commandTask;
    
    public static async Task<List<GameVersionProps>> GetAllVersionsAsync()
    {
        var propsList = new List<GameVersionProps>();
        foreach (IOPath f in Directory.GetFiles(GetVersionDescriptorDir()))
        {
            string name = GetVersionDescriptorName(f);
            propsList.Add(new GameVersionProps(name, null, f));
        }
        var remoteVersions = await Сеть.GetDownloadableVersions();
        propsList.AddRange(remoteVersions);
        return propsList;
    } 
    
    public static async Task<GameVersionDescriptor> CreateFromPropsAsync(GameVersionProps props)
    {
        if(!File.Exists(props.LocalDescriptorPath))
        {
            if (props.RemoteDescriptorUrl is null)
                throw new NullReferenceException("can't download game version descriptor '"
                     + props.Name + "', because RemoteDescriptorUrl is null");
            await Сеть.DownloadFileHTTP(props.RemoteDescriptorUrl, props.LocalDescriptorPath);
        }
        return new GameVersionDescriptor(props);
    }
    
    private GameVersionDescriptor(GameVersionProps props)
    {
        _props = props;
        WorkingDirectory =  Path.Concat(Приложение.Настройки.путь_к_кубачу, Name);
        string descriptorText = File.ReadAllText(props.LocalDescriptorPath);
        descriptor = JsonConvert.DeserializeObject<MinecraftVersionDescriptor>(descriptorText) 
            ?? throw new Exception($"can't parse descriptor file '{props.LocalDescriptorPath}'");
        javaArgs = new JavaArguments(descriptor);
        gameArgs = new GameArguments(descriptor);
        JavaExecutableFilePath = Path.Concat(Приложение.Настройки.путь_к_жабе, "bin",
            OperatingSystem.IsWindows() ? "javaw.exe" : "javaw");
    }
    
    public async void BeginUpdate(bool force)
    {
        try
        {
            downloadCts = new CancellationTokenSource();
            if(Приложение.Настройки.скачать_жабу)
                await Сеть.DownloadJava(descriptor.javaVersion, Приложение.Настройки.путь_к_жабе, force);
            await Сеть.DownloadAssets(descriptor.assetIndex, downloadCts.Token, force);
            await Сеть.DownloadVersionFile(descriptor.downloads.client.url, GetVersionJarFilePath(Name), force);
            await Сеть.DownloadLibraries(descriptor.libraries, GetLibrariesDir(), force);
            // await Network.DownloadModpack(modpack, WorkingDirectory, force);
            _props.IsDownloaded = true;
        }
        catch (Exception ex)
        {
            Ошибки.ПоказатьСообщение("GameUpdate", ex);
        }
    }

    public void CancelUpdate()
    {
        downloadCts?.Cancel();
    }

    public async Task Launch()
    {
        var javaArgsList = javaArgs.FillPlaceholders([]);
        var gameArgsList = gameArgs.FillPlaceholders([]);
        var command = Cli.Wrap(JavaExecutableFilePath.ToString())
            .WithWorkingDirectory(WorkingDirectory.ToString())
            .WithArguments(javaArgsList)
            .WithArguments(gameArgsList);
        Приложение.Логгер.LogInfo(nameof(GameVersionDescriptor),
            $"launching the game" +
            "\njava: " + command.TargetFilePath +
            "\nworking_dir: " + command.WorkingDirPath +
            "\njava_arguments: \n\t" + javaArgsList.MergeToString("\n\t") +
            "\ngame_arguments: \n\t" + gameArgsList.MergeToString("\n\t"));
        gameCts = new();
        commandTask = command.ExecuteAsync(gameCts.Token);
        var result = await commandTask;
        Приложение.Логгер.LogInfo(nameof(GameVersionDescriptor), $"game exited with code {result.ExitCode}");
    }

    public void Close()
    {
        gameCts?.Cancel();
    }

    public override string ToString() => Name;
}