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

namespace VigilCare.Simulation;

public class VigilCareApiClient
{
    private static readonly JsonSerializerOptions ApiJsonOptions = new(JsonSerializerDefaults.Web);

    private readonly HttpClient _http;

    public VigilCareApiClient(HttpClient http)
    {
        _http = http;
    }

    public async Task LoginAsync(string username, string password)
    {
        var response = await _http.PostAsJsonAsync("/api/v1/auth/login",
            new SimLoginRequest(username, password));

        if (!response.IsSuccessStatusCode)
        {
            var body = await response.Content.ReadAsStringAsync();
            throw new HttpRequestException(
                $"Login failed ({(int)response.StatusCode} {response.StatusCode}): {body}");
        }

        var envelope = await response.Content.ReadFromJsonAsync<ApiResponse<SimLoginResponse>>();
        var token = envelope!.Data!.AccessToken;
        _http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
    }

    public void SetBearerToken(string token)
    {
        _http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
    }

    public async Task<PatientResponse> RegisterPatientAsync(RegisterPatientRequest req)
    {
        var response = await _http.PostAsJsonAsync("/api/v1/patients", req);
        if (!response.IsSuccessStatusCode)
        {
            var body = await response.Content.ReadAsStringAsync();
            throw new HttpRequestException(
                $"Register patient failed ({(int)response.StatusCode} {response.StatusCode}): {body}");
        }
        var envelope = await response.Content.ReadFromJsonAsync<ApiResponse<PatientResponse>>();
        return envelope!.Data!;
    }

    public async Task<EncounterResponse> OpenEncounterAsync(Guid patientId, OpenEncounterRequest req)
    {
        var response = await _http.PostAsJsonAsync($"/api/v1/patients/{patientId}/encounters", req);
        response.EnsureSuccessStatusCode();
        var envelope = await response.Content.ReadFromJsonAsync<ApiResponse<EncounterResponse>>();
        return envelope!.Data!;
    }

    public async Task SendObservationBatchAsync(
        Guid encounterId, List<IngestObservationRequest> observations,
        ReplayTarget target = ReplayTarget.Central)
    {
        if (target == ReplayTarget.Gateway)
        {
            foreach (var obs in observations)
            {
                var response = await _http.PostAsJsonAsync(
                    $"/api/v1/encounters/{encounterId}/observations",
                    new
                    {
                        observationCode = obs.ObservationCode,
                        value = obs.Value,
                        unit = obs.Unit,
                        source = obs.Source,
                        recordedAt = obs.RecordedAt,
                        idempotencyKey = obs.IdempotencyKey ?? Guid.NewGuid().ToString()
                    });
                if (!response.IsSuccessStatusCode)
                {
                    var body = await response.Content.ReadAsStringAsync();
                    throw new HttpRequestException(
                        $"Observation ingest failed ({(int)response.StatusCode} {response.StatusCode}): {body}");
                }
            }
            return;
        }

        foreach (var chunk in observations.Chunk(10))
        {
            var batch = new BatchIngestRequest(chunk.ToList());
            var response = await _http.PostAsJsonAsync(
                $"/api/v1/encounters/{encounterId}/observations", batch);
            if (!response.IsSuccessStatusCode)
            {
                var body = await response.Content.ReadAsStringAsync();
                throw new HttpRequestException(
                    $"Observation batch failed ({(int)response.StatusCode} {response.StatusCode}): {body}");
            }
        }
    }

    public async Task<bool> TrySendMedicationAsync(
        Guid encounterId, CreateMedicationAdministrationRequest req)
    {
        var response = await _http.PostAsJsonAsync(
            $"/api/v1/encounters/{encounterId}/medications", req);
        if (response.StatusCode == System.Net.HttpStatusCode.NotFound)
            return false;
        response.EnsureSuccessStatusCode();
        return true;
    }

    public async Task<bool> TryCreateOrderAsync(
        Guid encounterId, CreateOrderRequest req)
    {
        var response = await _http.PostAsJsonAsync(
            $"/api/v1/encounters/{encounterId}/orders", req);
        return response.IsSuccessStatusCode;
    }

    public async Task<bool> TryResultOrderAsync(
        Guid encounterId, string orderDescription, string? resultSummary)
    {
        var ordersResponse = await _http.GetAsync(
            $"/api/v1/encounters/{encounterId}/orders?status=PENDING");
        if (!ordersResponse.IsSuccessStatusCode)
            return false;

        var envelope = await ordersResponse.Content
            .ReadFromJsonAsync<ApiResponse<PagedResponse<OrderResponse>>>();
        var order = FindPendingOrder(envelope?.Data?.Items ?? [], orderDescription);
        if (order is null)
            return false;

        var resultResponse = await _http.PatchAsJsonAsync(
            $"/api/v1/orders/{order.Id}/result",
            new RecordOrderResultRequest(resultSummary));
        return resultResponse.IsSuccessStatusCode;
    }

    private static OrderResponse? FindPendingOrder(
        List<OrderResponse> items, string description)
    {
        return items.FirstOrDefault(o => o.Description == description)
            ?? items.FirstOrDefault(o =>
                description.StartsWith(o.Description, StringComparison.OrdinalIgnoreCase))
            ?? items.FirstOrDefault(o =>
                o.Description.StartsWith(description, StringComparison.OrdinalIgnoreCase));
    }

    // --- Polling endpoints ---

    public async Task<List<AlertResponse>> GetAlertsAsync(Guid encounterId)
    {
        var response = await _http.GetAsync($"/api/v1/encounters/{encounterId}/alerts");
        if (!response.IsSuccessStatusCode) return new();
        var envelope = await response.Content
            .ReadFromJsonAsync<ApiResponse<PagedResponse<AlertResponse>>>(ApiJsonOptions);
        return envelope?.Data?.Items?.ToList() ?? new();
    }

    public async Task<News2Response?> GetCurrentNews2Async(Guid encounterId)
    {
        var response = await _http.GetAsync($"/api/v1/encounters/{encounterId}/news2/current");
        if (!response.IsSuccessStatusCode) return null;
        var envelope = await response.Content.ReadFromJsonAsync<ApiResponse<News2Response>>();
        return envelope?.Data;
    }

    public async Task<SepsisBundleResponse?> GetSepsisBundleAsync(Guid encounterId)
    {
        var response = await _http.GetAsync(
            $"/api/v1/encounters/{encounterId}/sepsis-bundle/current");
        if (!response.IsSuccessStatusCode) return null;
        var envelope = await response.Content
            .ReadFromJsonAsync<ApiResponse<SepsisBundleResponse>>();
        return envelope?.Data;
    }

    public async Task<GcsResponse?> GetCurrentGcsAsync(Guid encounterId)
    {
        var response = await _http.GetAsync($"/api/v1/encounters/{encounterId}/gcs");
        if (!response.IsSuccessStatusCode) return null;
        var envelope = await response.Content.ReadFromJsonAsync<ApiResponse<GcsResponse>>();
        return envelope?.Data;
    }

    public async Task<SofaResponse?> GetCurrentSofaAsync(Guid encounterId)
    {
        var response = await _http.GetAsync($"/api/v1/encounters/{encounterId}/sofa");
        if (!response.IsSuccessStatusCode) return null;
        var envelope = await response.Content.ReadFromJsonAsync<ApiResponse<SofaResponse>>();
        return envelope?.Data;
    }

    public async Task<QsofaResponse?> GetCurrentQsofaAsync(Guid encounterId)
    {
        var response = await _http.GetAsync($"/api/v1/encounters/{encounterId}/qsofa/current");
        if (!response.IsSuccessStatusCode) return null;
        var envelope = await response.Content.ReadFromJsonAsync<ApiResponse<QsofaResponse>>();
        return envelope?.Data;
    }

    public async Task<bool> TryAcknowledgeAlertAsync(
        Guid encounterId, string alertType, string clinicianId, string? note,
        TimeSpan? waitForAlert = null, CancellationToken ct = default)
    {
        const int pollIntervalMs = 500;
        var deadline = waitForAlert.HasValue
            ? DateTimeOffset.UtcNow.Add(waitForAlert.Value)
            : (DateTimeOffset?)null;

        while (true)
        {
            ct.ThrowIfCancellationRequested();

            var alerts = await GetAlertsAsync(encounterId);
            var alert = alerts.FirstOrDefault(a =>
                IsMatchingAlertType(a.AlertType, alertType)
                && IsOpenAlertStatus(a.Status));
            if (alert is not null)
            {
                var response = await _http.PostAsJsonAsync(
                    $"/api/v1/alerts/{alert.Id}/acknowledge",
                    new { note, clinicianId }, ct);
                return response.IsSuccessStatusCode;
            }

            if (!deadline.HasValue || DateTimeOffset.UtcNow >= deadline.Value)
                return false;

            await Task.Delay(pollIntervalMs, ct);
        }
    }

    private static bool IsOpenAlertStatus(string status) =>
        string.Equals(status, "OPEN", StringComparison.OrdinalIgnoreCase);

    private static bool IsMatchingAlertType(string actual, string expected)
    {
        if (string.Equals(actual, expected, StringComparison.OrdinalIgnoreCase))
            return true;

        static string Normalize(string s) =>
            s.Replace("_", "", StringComparison.Ordinal).ToUpperInvariant();

        return Normalize(actual) == Normalize(expected);
    }
}
