using Microsoft.Extensions.Logging;
using PSLauncher.Models;

namespace PSLauncher.Core.Lan;

public sealed class PeerManifestFetcher : IPeerManifestFetcher
{
    private const int MaxPeersToProbe = 5;

    private readonly Func<LanCacheConfig> _configProvider;
    private readonly ILanDiscoveryService _discovery;
    private readonly HttpClient _http;
    private readonly ILogger<PeerManifestFetcher> _logger;

    public PeerManifestFetcher(
        Func<LanCacheConfig> configProvider,
        ILanDiscoveryService discovery,
        HttpClient http,
        ILogger<PeerManifestFetcher> logger)
    {
        _configProvider = configProvider;
        _discovery = discovery;
        _http = http;
        _logger = logger;
    }

    public async Task<string?> TryFetchRawAsync(CancellationToken ct)
    {
        var cfg = _configProvider();
        if (!cfg.ClientEnabled) return null;

        // Liste fusionnée : actifs + connus (cache disque) + manuels.
        var candidates = PeerCandidates.Build(_discovery, cfg.ManualPeerUrls);

        if (candidates.Count == 0)
        {
            _logger.LogDebug("No peers available for manifest fallback");
            return null;
        }

        var probeMs = cfg.ProbeTimeoutMs > 0 ? cfg.ProbeTimeoutMs : 1000;
        foreach (var baseUrl in candidates.Take(MaxPeersToProbe))
        {
            ct.ThrowIfCancellationRequested();
            try
            {
                using var probeCts = CancellationTokenSource.CreateLinkedTokenSource(ct);
                probeCts.CancelAfter(probeMs);

                var url = $"{baseUrl}/v1/manifest";
                using var resp = await _http.GetAsync(url, probeCts.Token).ConfigureAwait(false);
                if (!resp.IsSuccessStatusCode)
                {
                    _logger.LogDebug("Peer {Url} manifest fetch returned {Status}", baseUrl, (int)resp.StatusCode);
                    continue;
                }
                var json = await resp.Content.ReadAsStringAsync(probeCts.Token).ConfigureAwait(false);
                if (string.IsNullOrWhiteSpace(json)) continue;
                _logger.LogInformation("Manifest fetched from peer {Url} ({Bytes} bytes)", baseUrl, json.Length);
                return json;
            }
            catch (OperationCanceledException) when (!ct.IsCancellationRequested)
            {
                _logger.LogDebug("Peer {Url} manifest probe timed out", baseUrl);
            }
            catch (Exception ex)
            {
                _logger.LogDebug(ex, "Peer {Url} manifest probe failed", baseUrl);
            }
        }

        _logger.LogInformation("No peer responded with a manifest");
        return null;
    }
}
