using System.Net.Http.Headers;
using System.Text;
using System.Text.Json;

namespace ScadaLink.CLI;

public class ManagementHttpClient : IDisposable
{
    private readonly HttpClient _httpClient;

    public ManagementHttpClient(string baseUrl, string username, string password)
    {
        _httpClient = new HttpClient { BaseAddress = new Uri(baseUrl.TrimEnd('/') + "/") };
        var credentials = Convert.ToBase64String(Encoding.UTF8.GetBytes($"{username}:{password}"));
        _httpClient.DefaultRequestHeaders.Authorization =
            new AuthenticationHeaderValue("Basic", credentials);
    }

    public async Task<ManagementResponse> SendCommandAsync(string commandName, object payload, TimeSpan timeout)
    {
        using var cts = new CancellationTokenSource(timeout);

        var body = JsonSerializer.Serialize(new { command = commandName, payload },
            new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase });

        var content = new StringContent(body, Encoding.UTF8, "application/json");

        HttpResponseMessage httpResponse;
        try
        {
            httpResponse = await _httpClient.PostAsync("management", content, cts.Token);
        }
        catch (TaskCanceledException)
        {
            return new ManagementResponse(504, null, "Request timed out.", "TIMEOUT");
        }
        catch (HttpRequestException ex)
        {
            return new ManagementResponse(0, null, $"Connection failed: {ex.Message}", "CONNECTION_FAILED");
        }

        var responseBody = await httpResponse.Content.ReadAsStringAsync(cts.Token);

        if (httpResponse.IsSuccessStatusCode)
        {
            return new ManagementResponse((int)httpResponse.StatusCode, responseBody, null, null);
        }

        // Parse error response
        string? error = null;
        string? code = null;
        try
        {
            using var doc = JsonDocument.Parse(responseBody);
            error = doc.RootElement.TryGetProperty("error", out var e) ? e.GetString() : responseBody;
            code = doc.RootElement.TryGetProperty("code", out var c) ? c.GetString() : null;
        }
        catch
        {
            error = responseBody;
        }

        return new ManagementResponse((int)httpResponse.StatusCode, null, error, code);
    }

    public void Dispose() => _httpClient.Dispose();
}

public record ManagementResponse(int StatusCode, string? JsonData, string? Error, string? ErrorCode);
