using System.Net;
using System.Net.Http.Headers;
using System.Text.Json;
using FluentAssertions;
using Microsoft.Extensions.DependencyInjection;

/// <summary>
/// Integration tests for FHIR R4 read/search endpoints (Phase 11 Step 7).
/// </summary>
[Collection("Database")]
public class FhirIntegrationTests : IAsyncLifetime
{
    private readonly ApiFixture _fixture;
    private HttpClient _client = null!;
    private HttpClient _anonymousClient = null!;

    public FhirIntegrationTests(ApiFixture fixture) => _fixture = fixture;

    public async Task InitializeAsync()
    {
        using var scope = _fixture.Services.CreateScope();
        var db = scope.ServiceProvider.GetRequiredService<AppDbContext>();
        await DbResetHelper.ResetAsync(db);
        await DataSeeder.SeedAsync(db);
        await FhirClinicalSeedHelper.SeedAsync(db);

        _client = await AuthHelper.LoginAsync(_fixture, "admin1");
        _anonymousClient = _fixture.CreateClient();
    }

    public Task DisposeAsync() => Task.CompletedTask;

    [Fact]
    public async Task Metadata_ReturnsCapabilityStatementWithSupportedResources()
    {
        var response = await GetFhirAsync("/fhir/metadata", authenticated: false);

        response.StatusCode.Should().Be(HttpStatusCode.OK);

        var body = await ParseJsonAsync(response);
        var resourceTypes = body.GetProperty("rest")[0]
            .GetProperty("resource")
            .EnumerateArray()
            .Select(r => r.GetProperty("type").GetString())
            .ToList();

        resourceTypes.Should().Contain(new[] { "Patient", "Encounter", "Observation" });
    }

    [Fact]
    public async Task ReadPatient_ReturnsFhirPatientWithMrnNameAndGender()
    {
        var response = await GetFhirAsync($"/fhir/Patient/{FhirClinicalSeedHelper.Patient1Id}");

        response.StatusCode.Should().Be(HttpStatusCode.OK);

        var body = await ParseJsonAsync(response);
        body.GetProperty("resourceType").GetString().Should().Be("Patient");
        body.GetProperty("id").GetString()
            .Should().Be(FhirClinicalSeedHelper.Patient1Id.ToString());

        var identifier = body.GetProperty("identifier")[0];
        identifier.GetProperty("value").GetString().Should().Be("VCR-000001");

        body.GetProperty("name")[0].GetProperty("text").GetString()
            .Should().Be("MARIA SANTOS");
        body.GetProperty("gender").GetString().Should().Be("female");
        body.GetProperty("birthDate").GetString().Should().Be("1978-03-15");
    }

    [Fact]
    public async Task SearchPatients_ByName_ReturnsMatchingBundle()
    {
        var response = await GetFhirAsync("/fhir/Patient?name=Santos");

        response.StatusCode.Should().Be(HttpStatusCode.OK);

        var body = await ParseJsonAsync(response);
        body.GetProperty("type").GetString().Should().Be("searchset");
        body.GetProperty("total").GetInt32().Should().BeGreaterThan(0);
        body.GetProperty("entry").GetArrayLength().Should().BeGreaterThan(0);
        body.GetProperty("entry")[0].GetProperty("resource")
            .GetProperty("name")[0].GetProperty("text").GetString()
            .Should().Contain("SANTOS");
    }

    [Fact]
    public async Task SearchPatients_ByIdentifier_ReturnsPatientByMrn()
    {
        var response = await GetFhirAsync("/fhir/Patient?identifier=VCR-000001");

        response.StatusCode.Should().Be(HttpStatusCode.OK);

        var body = await ParseJsonAsync(response);
        body.GetProperty("entry").GetArrayLength().Should().Be(1);
        body.GetProperty("entry")[0].GetProperty("resource")
            .GetProperty("identifier")[0].GetProperty("value").GetString()
            .Should().Be("VCR-000001");
    }

    [Fact]
    public async Task ReadEncounter_ReturnsFhirEncounterWithStatusAndPatientReference()
    {
        var response = await GetFhirAsync($"/fhir/Encounter/{FhirClinicalSeedHelper.Encounter1Id}");

        response.StatusCode.Should().Be(HttpStatusCode.OK);

        var body = await ParseJsonAsync(response);
        body.GetProperty("resourceType").GetString().Should().Be("Encounter");
        body.GetProperty("status").GetString().Should().Be("in-progress");
        body.GetProperty("subject").GetProperty("reference").GetString()
            .Should().Be($"Patient/{FhirClinicalSeedHelper.Patient1Id}");
    }

    [Fact]
    public async Task SearchEncounters_ByPatient_ReturnsMatchingEncounters()
    {
        var patientId = FhirClinicalSeedHelper.Patient1Id;
        var response = await GetFhirAsync($"/fhir/Encounter?patient={patientId}");

        response.StatusCode.Should().Be(HttpStatusCode.OK);

        var body = await ParseJsonAsync(response);
        body.GetProperty("total").GetInt32().Should().BeGreaterThan(0);
        body.GetProperty("entry").EnumerateArray().Should().AllSatisfy(entry =>
        {
            entry.GetProperty("resource").GetProperty("subject")
                .GetProperty("reference").GetString()
                .Should().Be($"Patient/{patientId}");
        });
    }

    [Fact]
    public async Task ReadObservation_ReturnsFhirObservationWithLoincCodeAndUcumUnit()
    {
        var response = await GetFhirAsync($"/fhir/Observation/{FhirClinicalSeedHelper.HeartRateObsId}");

        response.StatusCode.Should().Be(HttpStatusCode.OK);

        var body = await ParseJsonAsync(response);
        body.GetProperty("resourceType").GetString().Should().Be("Observation");

        var coding = body.GetProperty("code").GetProperty("coding")[0];
        coding.GetProperty("system").GetString().Should().Be("http://loinc.org");
        coding.GetProperty("code").GetString().Should().Be("8867-4");
        if (coding.TryGetProperty("display", out var display))
            display.GetString().Should().Be("Heart rate");

        var value = body.GetProperty("valueQuantity");
        value.GetProperty("value").GetDecimal().Should().Be(88m);
        value.GetProperty("unit").GetString().Should().Be("bpm");
        value.GetProperty("code").GetString().Should().Be("/min");
    }

    [Fact]
    public async Task SearchObservations_ByCategoryVitalSigns_ReturnsVitalSignObservationsOnly()
    {
        var patientId = FhirClinicalSeedHelper.Patient1Id;
        var response = await GetFhirAsync(
            $"/fhir/Observation?patient={patientId}&category=vital-signs");

        response.StatusCode.Should().Be(HttpStatusCode.OK);

        var body = await ParseJsonAsync(response);
        body.GetProperty("entry").GetArrayLength().Should().BeGreaterThan(0);

        foreach (var entry in body.GetProperty("entry").EnumerateArray())
        {
            var category = entry.GetProperty("resource").GetProperty("category")[0]
                .GetProperty("coding")[0].GetProperty("code").GetString();
            category.Should().Be("vital-signs");
        }
    }

    [Fact]
    public async Task SearchObservations_ByLoincCode_ResolvesToHeartRate()
    {
        var response = await GetFhirAsync("/fhir/Observation?code=8867-4");

        response.StatusCode.Should().Be(HttpStatusCode.OK);

        var body = await ParseJsonAsync(response);
        body.GetProperty("entry").GetArrayLength().Should().BeGreaterThan(0);

        var coding = body.GetProperty("entry")[0].GetProperty("resource")
            .GetProperty("code").GetProperty("coding")[0];
        coding.GetProperty("code").GetString().Should().Be("8867-4");

        var value = body.GetProperty("entry")[0].GetProperty("resource")
            .GetProperty("valueQuantity").GetProperty("value").GetDecimal();
        value.Should().Be(88m);
    }

    [Fact]
    public async Task SearchObservations_ByDateGreaterOrEqual_FiltersByRecordedAt()
    {
        var patientId = FhirClinicalSeedHelper.Patient1Id;
        var response = await GetFhirAsync(
            $"/fhir/Observation?patient={patientId}&date=ge2026-06-20");

        response.StatusCode.Should().Be(HttpStatusCode.OK);

        var body = await ParseJsonAsync(response);
        body.GetProperty("total").GetInt32().Should().BeGreaterThan(0);
        body.GetProperty("entry").GetArrayLength().Should().BeGreaterThan(0);
    }

    [Fact]
    public async Task PatientEverything_ReturnsCompletePatientBundle()
    {
        var response = await GetFhirAsync(
            $"/fhir/Patient/{FhirClinicalSeedHelper.Patient1Id}/$everything");

        response.StatusCode.Should().Be(HttpStatusCode.OK);

        var body = await ParseJsonAsync(response);
        body.GetProperty("type").GetString().Should().Be("searchset");
        body.GetProperty("total").GetInt32().Should().BeGreaterThan(1);

        var resourceTypes = body.GetProperty("entry").EnumerateArray()
            .Select(e => e.GetProperty("resource").GetProperty("resourceType").GetString())
            .ToList();

        resourceTypes.Should().Contain("Patient");
        resourceTypes.Should().Contain("Encounter");
        resourceTypes.Should().Contain("Observation");

        var includeModes = body.GetProperty("entry").EnumerateArray()
            .Where(e => e.GetProperty("resource").GetProperty("resourceType").GetString() != "Patient")
            .Select(e => e.GetProperty("search").GetProperty("mode").GetString());

        includeModes.Should().AllBe("include");
    }

    [Fact]
    public async Task ReadPatient_NotFound_Returns404OperationOutcome()
    {
        var missingId = Guid.Parse("00000000-0000-0000-0000-000000000001");
        var response = await GetFhirAsync($"/fhir/Patient/{missingId}");

        response.StatusCode.Should().Be(HttpStatusCode.NotFound);

        var body = await ParseJsonAsync(response);
        body.GetProperty("resourceType").GetString().Should().Be("OperationOutcome");

        var issue = body.GetProperty("issue")[0];
        issue.GetProperty("severity").GetString().Should().Be("error");
        issue.GetProperty("code").GetString().Should().Be("not-found");
        issue.GetProperty("diagnostics").GetString()
            .Should().Contain($"Patient/{missingId}");
    }

    [Fact]
    public async Task ReadPatient_ReturnsApplicationFhirJsonContentType()
    {
        var response = await GetFhirAsync($"/fhir/Patient/{FhirClinicalSeedHelper.Patient1Id}");

        response.StatusCode.Should().Be(HttpStatusCode.OK);
        response.Content.Headers.ContentType!.MediaType.Should().Be("application/fhir+json");
    }

    [Fact]
    public async Task SearchPatients_PaginationLinks_AreCorrect()
    {
        var response = await GetFhirAsync("/fhir/Patient?_count=1&_offset=0");

        response.StatusCode.Should().Be(HttpStatusCode.OK);

        var body = await ParseJsonAsync(response);
        body.GetProperty("total").GetInt32().Should().BeGreaterThanOrEqualTo(2);
        body.GetProperty("entry").GetArrayLength().Should().Be(1);

        var links = body.GetProperty("link").EnumerateArray()
            .ToDictionary(l => l.GetProperty("relation").GetString()!, l => l.GetProperty("url").GetString());

        links.Should().ContainKey("self");
        links["self"].Should().Contain("_count=1");
        links["self"].Should().Contain("_offset=0");

        links.Should().ContainKey("next");
        links["next"].Should().Contain("_count=1");
        links["next"].Should().Contain("_offset=1");
    }

    private async Task<HttpResponseMessage> GetFhirAsync(string path, bool authenticated = true)
    {
        var client = authenticated ? _client : _anonymousClient;
        var request = new HttpRequestMessage(HttpMethod.Get, path);
        request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/fhir+json"));
        return await client.SendAsync(request);
    }

    private static async Task<JsonElement> ParseJsonAsync(HttpResponseMessage response)
    {
        var json = await response.Content.ReadAsStringAsync();
        return JsonDocument.Parse(json).RootElement;
    }
}
