namespace PSLauncher.Core.Lan;
///
/// URL d'un peer LAN qui a déjà la version demandée et a passé la vérification
/// (taille + SHA-256 du manifest match la réponse /v1/has/). À utiliser
/// dans le DownloadJob comme URL initiale au lieu de l'URL OVH.
///
public sealed record PeerSource(Uri ZipUrl, string PeerHost);
///
/// Helper interne aux probers (PeerSourceResolver, PeerManifestFetcher) pour
/// fusionner peers actifs (beacons UDP récents), peers connus (cache disque),
/// peers manuels (config). Évite la duplication entre les 2 fetchers.
///
internal static class PeerCandidates
{
public static List Build(ILanDiscoveryService discovery, IEnumerable manualUrls)
{
var candidates = new List();
var seen = new HashSet(StringComparer.OrdinalIgnoreCase);
// 1) Peers actifs (UDP beacon < 60s) — les plus fiables
foreach (var p in discovery.ListDiscovered())
{
var url = $"http://{p.Host}:{p.Port}";
if (seen.Add(url)) candidates.Add(url);
}
// 2) Peers connus (cache disque, runs précédents) — essayés rapidement même
// si pas encore reçu de beacon ce run (cold start, < 15s d'uptime)
foreach (var p in discovery.ListKnown())
{
var url = $"http://{p.Host}:{p.Port}";
if (seen.Add(url)) candidates.Add(url);
}
// 3) Peers manuels (config user) — fallback / override explicite
foreach (var url in manualUrls)
{
var trimmed = url.Trim().TrimEnd('/');
if (string.IsNullOrEmpty(trimmed)) continue;
if (seen.Add(trimmed)) candidates.Add(trimmed);
}
return candidates;
}
}
///
/// Trouve un peer LAN qui a la version demandée. Combine peers découverts auto
/// (UDP discovery) et peers manuels (config), probe chacun avec un timeout court,
/// retourne le premier qui matche le SHA-256 attendu. Null si aucun → fallback OVH.
///
/// Budget total : ProbeTimeoutMs × max 5 peers = ~5 s pire cas. Ne ralentit
/// jamais le path OVH au-delà.
///
public interface IPeerSourceResolver
{
Task TryResolveAsync(
string version,
long expectedSize,
string expectedSha256,
CancellationToken ct);
}