using System.Net;
using System.Net.Http.Json;
using System.Text.Json;
using FluentAssertions;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using StackExchange.Redis;

[Collection("Integration")]
public class ClinicalDemographicsAndObservationTests : IAsyncLifetime
{
    private readonly ApiFixture _fixture;
    private readonly HttpClient _client;
    private Guid _patientId;
    private Guid _encounterId;

    public ClinicalDemographicsAndObservationTests(ApiFixture fixture)
    {
        _fixture = fixture;
        _client = fixture.CreateClient();
    }

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

        var patient = new Patient
        {
            Id = Guid.NewGuid(), Mrn = "MRN-P10-001", FirstName = "Phase10", LastName = "Test",
            DateOfBirth = new DateOnly(1965, 3, 15), Gender = "F",
            BloodType = BloodType.BPositive, Allergies = "Sulfa",
            EmergencyContactName = "Test Contact", EmergencyContactPhone = "555-9999",
            CreatedAt = DateTimeOffset.UtcNow
        };
        var encounter = new Encounter
        {
            Id = Guid.NewGuid(), PatientId = patient.Id, EncounterType = EncounterType.Inpatient,
            Status = EncounterStatus.Active, Department = Department.Icu,
            AttendingPhysician = "Dr. Phase10",
            RoomBed = "ICU-7A", AdmissionReason = "Sepsis workup",
            AdmittedAt = DateTimeOffset.UtcNow, CreatedAt = DateTimeOffset.UtcNow
        };
        db.Patients.Add(patient);
        db.Encounters.Add(encounter);

        db.AlertThresholds.AddRange(
            new AlertThreshold { Id = Guid.NewGuid(), ObservationCode = "HEART_RATE",
                DisplayName = "Heart Rate", Unit = "bpm",
                CriticalLow = 30, WarningLow = 50, WarningHigh = 100, CriticalHigh = 150,
                CreatedAt = DateTimeOffset.UtcNow },
            new AlertThreshold { Id = Guid.NewGuid(), ObservationCode = "SYSTOLIC_BP",
                DisplayName = "Systolic BP", Unit = "mmHg",
                CriticalLow = 70, WarningLow = 90, WarningHigh = 160, CriticalHigh = 180,
                CreatedAt = DateTimeOffset.UtcNow },
            new AlertThreshold { Id = Guid.NewGuid(), ObservationCode = "DIASTOLIC_BP",
                DisplayName = "Diastolic BP", Unit = "mmHg",
                CriticalLow = 40, WarningLow = 60, WarningHigh = 90, CriticalHigh = 110,
                CreatedAt = DateTimeOffset.UtcNow },
            new AlertThreshold { Id = Guid.NewGuid(), ObservationCode = "LACTATE_MMOL_L",
                DisplayName = "Serum Lactate", Unit = "mmol/L",
                CriticalLow = null, WarningLow = null, WarningHigh = 2.0m, CriticalHigh = 4.0m,
                CreatedAt = DateTimeOffset.UtcNow },
            new AlertThreshold { Id = Guid.NewGuid(), ObservationCode = "AVPU",
                DisplayName = "AVPU Consciousness", Unit = "score",
                CriticalLow = null, WarningLow = null, WarningHigh = null, CriticalHigh = 2m,
                CreatedAt = DateTimeOffset.UtcNow },
            new AlertThreshold { Id = Guid.NewGuid(), ObservationCode = "SUPPLEMENTAL_O2",
                DisplayName = "Supplemental O2", Unit = "flag",
                CriticalLow = null, WarningLow = null, WarningHigh = null, CriticalHigh = null,
                CreatedAt = DateTimeOffset.UtcNow },
            new AlertThreshold { Id = Guid.NewGuid(), ObservationCode = "GLUCOSE_MG_DL",
                DisplayName = "Blood Glucose", Unit = "mg/dL",
                CriticalLow = 40, WarningLow = 70, WarningHigh = 180, CriticalHigh = 400,
                CreatedAt = DateTimeOffset.UtcNow }
        );
        await db.SaveChangesAsync();

        var redis = scope.ServiceProvider.GetRequiredService<IConnectionMultiplexer>();
        var cache = redis.GetDatabase(1);
        foreach (var t in await db.AlertThresholds.ToListAsync())
        {
            var json = JsonSerializer.Serialize(new
            {
                t.ObservationCode, t.CriticalLow, t.WarningLow, t.WarningHigh, t.CriticalHigh
            });
            await cache.StringSetAsync($"threshold:{t.ObservationCode}", json);
        }

        _patientId = patient.Id;
        _encounterId = encounter.Id;
    }

    public Task DisposeAsync() => Task.CompletedTask;

    [Fact]
    public async Task CriticalSystolicBp_AlertCreated()
    {
        var resp = await _client.PostAsJsonAsync(
            $"/api/v1/encounters/{_encounterId}/observations",
            new BatchIngestRequest(new List<IngestObservationRequest>
            {
                new("SYSTOLIC_BP", 65, "mmHg", ObservationSource.Device, DateTimeOffset.UtcNow, null)
            }));

        resp.StatusCode.Should().Be(HttpStatusCode.Created);
        var body = await resp.Content.ReadFromJsonAsync<JsonDocument>();
        body!.RootElement.GetProperty("data").GetProperty("alertGenerated").GetBoolean()
            .Should().BeTrue();

        using var scope = _fixture.Services.CreateScope();
        var db = scope.ServiceProvider.GetRequiredService<AppDbContext>();
        var alert = await db.ClinicalAlerts.SingleAsync();
        alert.AlertType.Should().Be(AlertType.CriticalSystolicBp);
        alert.Severity.Should().Be(AlertSeverity.Critical);
    }

    [Fact]
    public async Task AvpuUnresponsive_CriticalAlert()
    {
        var resp = await _client.PostAsJsonAsync(
            $"/api/v1/encounters/{_encounterId}/observations",
            new BatchIngestRequest(new List<IngestObservationRequest>
            {
                new("AVPU", 3, "score", ObservationSource.Manual, DateTimeOffset.UtcNow, null)
            }));

        resp.StatusCode.Should().Be(HttpStatusCode.Created);

        using var scope = _fixture.Services.CreateScope();
        var db = scope.ServiceProvider.GetRequiredService<AppDbContext>();
        var alert = await db.ClinicalAlerts.SingleAsync();
        alert.AlertType.Should().Be(AlertType.CriticalAvpu);
    }

    [Fact]
    public async Task SupplementalO2_NoAlert()
    {
        var resp = await _client.PostAsJsonAsync(
            $"/api/v1/encounters/{_encounterId}/observations",
            new BatchIngestRequest(new List<IngestObservationRequest>
            {
                new("SUPPLEMENTAL_O2", 1, "flag", ObservationSource.Manual, DateTimeOffset.UtcNow, null)
            }));

        resp.StatusCode.Should().Be(HttpStatusCode.Created);

        using var scope = _fixture.Services.CreateScope();
        var db = scope.ServiceProvider.GetRequiredService<AppDbContext>();
        (await db.ClinicalAlerts.CountAsync()).Should().Be(0);
    }

    [Fact]
    public async Task CriticalGlucose_AlertCreated()
    {
        var resp = await _client.PostAsJsonAsync(
            $"/api/v1/encounters/{_encounterId}/observations",
            new BatchIngestRequest(new List<IngestObservationRequest>
            {
                new("GLUCOSE_MG_DL", 30, "mg/dL", ObservationSource.Lab, DateTimeOffset.UtcNow, null)
            }));

        resp.StatusCode.Should().Be(HttpStatusCode.Created);

        using var scope = _fixture.Services.CreateScope();
        var db = scope.ServiceProvider.GetRequiredService<AppDbContext>();
        var alert = await db.ClinicalAlerts.SingleAsync();
        alert.AlertType.Should().Be(AlertType.CriticalGlucoseMgDl);
    }

    [Fact]
    public async Task PatientRegistration_ClinicalFields_RoundTrip()
    {
        var resp = await _client.PostAsJsonAsync("/api/v1/patients", new
        {
            firstName = "Demo",
            lastName = "Patient",
            dateOfBirth = "1990-06-15",
            gender = "M",
            bloodType = "AB-",
            allergies = "Latex, Iodine",
            emergencyContactName = "Demo Contact",
            emergencyContactPhone = "555-1234"
        });

        resp.StatusCode.Should().Be(HttpStatusCode.Created);
        var body = await resp.Content.ReadFromJsonAsync<JsonDocument>();
        var data = body!.RootElement.GetProperty("data");
        data.GetProperty("bloodType").GetString().Should().Be("AB-");
        data.GetProperty("allergies").GetString().Should().Be("Latex, Iodine");
        data.GetProperty("emergencyContactName").GetString().Should().Be("Demo Contact");
    }

    [Fact]
    public async Task OpenEncounter_WithRoomBed_Success()
    {
        var patientResp = await _client.PostAsJsonAsync("/api/v1/patients", new
        {
            firstName = "Room", lastName = "Test",
            dateOfBirth = "1985-01-01", gender = "F"
        });
        var patientBody = await patientResp.Content.ReadFromJsonAsync<JsonDocument>();
        var newPatientId = patientBody!.RootElement.GetProperty("data").GetProperty("id").GetGuid();

        var resp = await _client.PostAsJsonAsync($"/api/v1/patients/{newPatientId}/encounters", new
        {
            encounterType = "INPATIENT",
            department = "ICU",
            attendingPhysician = "Dr. Room",
            roomBed = "ICU-3C",
            admissionReason = "Acute respiratory distress"
        });

        resp.StatusCode.Should().Be(HttpStatusCode.Created);
        var body = await resp.Content.ReadFromJsonAsync<JsonDocument>();
        var data = body!.RootElement.GetProperty("data");
        data.GetProperty("roomBed").GetString().Should().Be("ICU-3C");
        data.GetProperty("admissionReason").GetString().Should().Be("Acute respiratory distress");
    }
}
