namespace PSLauncher.Core.Lan;

/// <summary>
/// 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 <c>/v1/has/</c>). À utiliser
/// dans le DownloadJob comme URL initiale au lieu de l'URL OVH.
/// </summary>
public sealed record PeerSource(Uri ZipUrl, string PeerHost);

/// <summary>
/// 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.
/// </summary>
internal static class PeerCandidates
{
    public static List<string> Build(ILanDiscoveryService discovery, IEnumerable<string> manualUrls)
    {
        var candidates = new List<string>();
        var seen = new HashSet<string>(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;
    }
}

/// <summary>
/// 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 : <c>ProbeTimeoutMs</c> × max 5 peers = ~5 s pire cas. Ne ralentit
/// jamais le path OVH au-delà.
/// </summary>
public interface IPeerSourceResolver
{
    Task<PeerSource?> TryResolveAsync(
        string version,
        long expectedSize,
        string expectedSha256,
        CancellationToken ct);
}
