using Microsoft.Extensions.Logging;

namespace ZB.MOM.WW.ScadaBridge.SiteRuntime.Deployment;

/// <summary>HTTP <see cref="IDeploymentConfigFetcher"/> — GETs the config from central's internal endpoint.</summary>
public sealed class HttpDeploymentConfigFetcher : IDeploymentConfigFetcher
{
    private readonly HttpClient _http;
    private readonly ILogger<HttpDeploymentConfigFetcher> _log;

    /// <summary>Initializes the fetcher with its HTTP client and logger.</summary>
    /// <param name="http">The HTTP client used to call central's internal endpoint.</param>
    /// <param name="log">Logger for fetch diagnostics.</param>
    public HttpDeploymentConfigFetcher(HttpClient http, ILogger<HttpDeploymentConfigFetcher> log)
    {
        _http = http;
        _log = log;
    }

    /// <inheritdoc />
    public async Task<string> FetchAsync(string centralFetchBaseUrl, string deploymentId, string token, CancellationToken ct)
    {
        var url = $"{centralFetchBaseUrl.TrimEnd('/')}/api/internal/deployments/{Uri.EscapeDataString(deploymentId)}/config";
        using var req = new HttpRequestMessage(HttpMethod.Get, url);
        req.Headers.Add("X-Deployment-Token", token);

        HttpResponseMessage resp;
        try
        {
            resp = await _http.SendAsync(req, ct);
        }
        catch (OperationCanceledException) when (ct.IsCancellationRequested)
        {
            throw; // genuine caller cancellation — propagate
        }
        catch (Exception ex)
        {
            // Includes HttpClient.Timeout (surfaces as TaskCanceledException with ct NOT cancelled) — treat as transport.
            _log.LogWarning(ex, "deployment-config fetch transport error for {DeploymentId}", deploymentId);
            throw new DeploymentConfigFetchException($"deployment-config fetch transport error: {ex.Message}", isSuperseded: false, ex);
        }

        using (resp)
        {
            if (resp.StatusCode == System.Net.HttpStatusCode.NotFound)
            {
                _log.LogWarning("deployment-config not found (expired/superseded/unknown) for {DeploymentId} (HTTP 404)", deploymentId);
                throw new DeploymentConfigFetchException($"deployment-config not found (expired/superseded/unknown): {deploymentId}", isSuperseded: true);
            }
            if (!resp.IsSuccessStatusCode)
            {
                _log.LogWarning("deployment-config fetch failed for {DeploymentId}: HTTP {StatusCode}", deploymentId, (int)resp.StatusCode);
                throw new DeploymentConfigFetchException($"deployment-config fetch failed: HTTP {(int)resp.StatusCode}", isSuperseded: false);
            }

            var body = await resp.Content.ReadAsStringAsync(ct);
            _log.LogDebug("deployment-config fetched for {DeploymentId} (HTTP {StatusCode})", deploymentId, (int)resp.StatusCode);
            return body;
        }
    }
}
