using System.Net.Http;
using System.Text.Json;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using PackCheck.App.Infrastructure;
using PackCheck.Servers;
namespace PackCheck.App.ViewModels;
/// Pick the mods first: the wizard finds the newest Minecraft version and loader they all support, then installs them with the server.
public sealed partial class CreateServerViewModel
{
private List _pendingMods = [];
[ObservableProperty]
private string _modList = string.Empty;
[ObservableProperty]
private string? _modSolveStatus;
private static HttpClient Http => (HttpClient)App.Services.GetService(typeof(HttpClient))!;
[RelayCommand]
private async Task SolveModsAsync()
{
var slugs = ModList.Split([',', '\n', '\r', ' ', ';'], StringSplitOptions.RemoveEmptyEntries)
.Select(ContentLinks.Parse).Where(l => l?.Source == LinkSource.Modrinth).Select(l => l!.Id).Distinct(StringComparer.OrdinalIgnoreCase).ToList();
if (slugs.Count == 0)
{
ModSolveStatus = Loc.T("ModFirst_NoMods");
return;
}
try
{
ModSolveStatus = Loc.F("ModFirst_Checking", slugs.Count);
var support = new List>();
foreach (var slug in slugs)
{
using var doc = JsonDocument.Parse(await Http.GetStringAsync(new Uri($"https://api.modrinth.com/v2/project/{Uri.EscapeDataString(slug)}/version")).ConfigureAwait(true));
support.Add(doc.RootElement.EnumerateArray()
.SelectMany(v => v.GetProperty("loaders").EnumerateArray().Select(l => l.GetString()!)
.SelectMany(l => v.GetProperty("game_versions").EnumerateArray().Select(g => (l, g.GetString()!))))
.ToHashSet());
}
if (ModSolver.Solve(support) is not { } solution)
{
ModSolveStatus = Loc.T("ModFirst_NoCommon");
return;
}
Platform = ModSolver.Platform(solution.Loader);
for (var i = 0; i < 100 && !Versions.Contains(solution.MinecraftVersion); i++)
{
await Task.Delay(100).ConfigureAwait(true);
}
if (Versions.Contains(solution.MinecraftVersion))
{
Version = solution.MinecraftVersion;
}
_pendingMods = slugs;
ModSolveStatus = Loc.F("ModFirst_Solved", Platform, solution.MinecraftVersion, slugs.Count);
}
catch (Exception ex) when (ex is HttpRequestException or JsonException or TaskCanceledException or KeyNotFoundException or InvalidOperationException)
{
ModSolveStatus = ex.Message;
}
}
/// After the server exists, the chosen mods are installed for its loader and version.
private async Task InstallPendingModsAsync(ServerDefinition def)
{
if (_pendingMods.Count == 0 || def.Platform != Platform)
{
return;
}
var done = 0;
foreach (var slug in _pendingMods)
{
Status = Loc.F("ModFirst_Installing", slug, ++done, _pendingMods.Count);
try
{
var d = await ContentLinks.ResolveAsync(Http, new ContentLink(LinkSource.Modrinth, slug), def, CancellationToken.None).ConfigureAwait(true);
await ContentLinks.InstallAsync(Http, d, def, CancellationToken.None).ConfigureAwait(true);
}
catch (Exception ex) when (ex is HttpRequestException or InvalidOperationException or System.IO.InvalidDataException or System.IO.IOException or JsonException or TaskCanceledException)
{
Serilog.Log.Warning(ex, "Mod {Slug} could not be installed", slug);
}
}
}
}