using Ludarium.Domain;
namespace Ludarium.Application;
///
/// Everything the library browser can ask of the catalog.
///
///
/// Filtering and ordering belong to the query, not to the page the browser happens to hold. The
/// library previously sorted the forty-eight games already on screen, which reordered a page rather
/// than the collection and quietly disagreed with what the operator asked for.
///
public sealed record GameQuery
{
public string? Search { get; init; }
public string? Platform { get; init; }
/// Ordering key. Unknown values fall back to title.
public string? Sort { get; init; }
public bool? Favorite { get; init; }
public GamePlayStatus? Status { get; init; }
public Guid? CollectionId { get; init; }
/// Normalized tag name, as stored by .
public string? Tag { get; init; }
/// Keep only games rated at least this highly.
public int? MinimumRating { get; init; }
///
/// Keep only games with a linked, present copy in a format some player accepts.
///
///
/// This is a property of the collection, not of the runtime: a sidecar that is switched off does
/// not make a game disappear from the library. Whether a specific game can start right now stays
/// the capability's answer, shown on the game itself.
///
public bool PlayableOnly { get; init; }
public int Page { get; init; } = 1;
public int PageSize { get; init; } = 50;
public static readonly IReadOnlyList SortKeys =
["title", "recent", "updated", "played", "rating"];
public bool HasFilters => !string.IsNullOrWhiteSpace(Search) || !string.IsNullOrWhiteSpace(Platform) ||
Favorite is not null || Status is not null || CollectionId is not null ||
!string.IsNullOrWhiteSpace(Tag) || MinimumRating is not null || PlayableOnly;
}
///
/// The exact platform-and-container pairs some Ludarium player accepts.
///
///
/// Derived from the browser matrix and the native registry so the library's "playable copy" filter
/// can never claim a format no player takes, and can never drift from the policies that decide it.
///
public static class PlayableFormats
{
/// Each entry is platform|.extension, lower-cased.
public static IReadOnlyList Pairs { get; } = BuildPairs();
private static string[] BuildPairs()
{
var pairs = new SortedSet(StringComparer.Ordinal);
foreach (var platform in BrowserPlayPolicy.DescribePlatforms())
foreach (var extension in platform.Extensions.Concat(platform.CandidateExtensions ?? new HashSet()))
pairs.Add($"{platform.Platform.ToLowerInvariant()}|{extension.ToLowerInvariant()}");
foreach (var player in NativeRemotePlayerRegistry.All)
foreach (var platform in player.Platforms)
foreach (var extension in player.Extensions)
pairs.Add($"{platform.ToLowerInvariant()}|{extension.ToLowerInvariant()}");
return [.. pairs];
}
}