using System.Text;
using System.Text.Json;

namespace ZB.MOM.WW.ScadaBridge.DelmiaNotifier;

/// <summary>
/// <see cref="IRecipeSender"/> over a real <see cref="HttpClient"/>: POSTs the recipe payload to
/// <c>{baseUrl}/api/DelmiaRecipeDownload</c> with the <c>X-API-Key</c> header. A reachable server
/// (any status) is <see cref="AttemptKind.Connected"/>; a connection/timeout failure is <see cref="AttemptKind.ConnectFailed"/>.
/// </summary>
internal sealed class HttpRecipeSender(HttpClient http, string apiKey) : IRecipeSender
{
    private const string MethodPath = "/api/DelmiaRecipeDownload";

    /// <inheritdoc />
    public async Task<AttemptOutcome> SendAsync(string baseUrl, RecipeDownload payload, CancellationToken ct)
    {
        var url = baseUrl.TrimEnd('/') + MethodPath;
        try
        {
            using var request = new HttpRequestMessage(HttpMethod.Post, url);
            request.Headers.TryAddWithoutValidation("X-API-Key", apiKey);
            var json = JsonSerializer.Serialize(payload, NotifierJsonContext.Default.RecipeDownload);
            request.Content = new StringContent(json, Encoding.UTF8, "application/json");

            using var response = await http.SendAsync(request, ct);
            var status = (int)response.StatusCode;
            RecipeDownloadResult? body = null;
            if (response.IsSuccessStatusCode)
            {
                var responseText = await response.Content.ReadAsStringAsync(ct);
                body = TryParse(responseText);
            }

            return new AttemptOutcome(AttemptKind.Connected, status, body, null);
        }
        catch (HttpRequestException ex)
        {
            return new AttemptOutcome(AttemptKind.ConnectFailed, 0, null, ex.Message);
        }
        catch (OperationCanceledException ex) when (!ct.IsCancellationRequested)
        {
            // No external cancellation requested → this is an HttpClient.Timeout, i.e. the node never answered.
            return new AttemptOutcome(AttemptKind.ConnectFailed, 0, null, "timeout: " + ex.Message);
        }
    }

    private static RecipeDownloadResult? TryParse(string responseText)
    {
        try
        {
            return JsonSerializer.Deserialize(responseText, NotifierJsonContext.Default.RecipeDownloadResult);
        }
        catch (JsonException)
        {
            return null; // unparseable body on a 2xx → treated as failure by the loop
        }
    }
}
