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

[Collection("Integration")]
public class ExplainableAlertsTests : IAsyncLifetime
{
    private readonly ApiFixture _fixture;

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

    public async Task InitializeAsync() =>
        await ExplainableAlertsTestHelper.ResetAsync(_fixture);

    public Task DisposeAsync() => Task.CompletedTask;

    [Fact]
    public async Task News2Alert_IncludesScoreContributors()
    {
        var (encounterId, _) = await ExplainableAlertsTestHelper.SeedEncounterWithVitalsAsync(
            _fixture, respRate: 28, spo2: 92, systolicBp: 95, heartRate: 110,
            tempC: 38.5m, supplementalO2: 1m);

        var alert = await ExplainableAlertsTestHelper.WaitForAlertAsync(
            _fixture, encounterId, AlertType.News2Emergency);
        var body = await ExplainableAlertsTestHelper.GetAlertAsync<AlertResponse>(_fixture, alert.Id);

        body.Explanation.Should().NotBeNull();
        body.Explanation!.ScoreContributors.Should().HaveCount(7);
        body.Explanation.ScoreContributors.Should().Contain(c =>
            c.Parameter == "Respiratory Rate" && c.Points > 0);
        body.Explanation.NarrativeSummary.Should().NotBeNullOrWhiteSpace();
    }

    [Fact]
    public async Task SofaAlert_IncludesOrganContributors()
    {
        var (encounterId, patientId) = await ExplainableAlertsTestHelper.SeedSofaBaselineAsync(_fixture);
        await ExplainableAlertsTestHelper.AdvanceSofaDeltaAsync(_fixture, encounterId, patientId, delta: 2);

        var alert = await ExplainableAlertsTestHelper.WaitForAlertAsync(
            _fixture, encounterId, AlertType.SofaSepsis);
        var body = await ExplainableAlertsTestHelper.GetAlertAsync<AlertResponse>(_fixture, alert.Id);

        body.Explanation.Should().NotBeNull();
        body.Explanation!.ScoreContributors.Should().NotBeEmpty();
        body.Explanation.ScoreContributors.Should().Contain(c => c.Parameter == "Respiratory");
    }

    [Fact]
    public async Task GcsAlert_IncludesEyeVerbalMotor()
    {
        var (encounterId, patientId) = await ExplainableAlertsTestHelper.SeedEncounterAsync(_fixture);
        await ExplainableAlertsTestHelper.RecordGcsAsync(
            _fixture, encounterId, patientId, eye: 2, verbal: 2, motor: 3);

        var alert = await ExplainableAlertsTestHelper.WaitForAlertAsync(
            _fixture, encounterId, AlertType.GcsCritical);
        var body = await ExplainableAlertsTestHelper.GetAlertAsync<AlertResponse>(_fixture, alert.Id);

        body.Explanation.Should().NotBeNull();
        body.Explanation!.ScoreContributors.Should().HaveCount(3);
        body.Explanation.ScoreContributors.Should().Contain(c => c.Parameter == "Eye");
    }

    [Fact]
    public async Task TrendAlert_IncludesTrendContext()
    {
        var (encounterId, patientId) = await ExplainableAlertsTestHelper.SeedEncounterAsync(_fixture);
        await ExplainableAlertsTestHelper.SimulateRapidRespRateRiseAsync(
            _fixture, encounterId, patientId);

        var alert = await ExplainableAlertsTestHelper.WaitForAlertAsync(
            _fixture, encounterId, AlertType.RapidDeterioration);
        var body = await ExplainableAlertsTestHelper.GetAlertAsync<AlertResponse>(_fixture, alert.Id);

        body.Explanation.Should().NotBeNull();
        body.Explanation!.Trend.Should().NotBeNull();
        body.Explanation.Trend!.PercentChange.Should().BeGreaterThan(0);
        body.Explanation.Trend.Duration.Should().NotBe(default);
    }

    [Fact]
    public async Task MedicationAlert_IncludesDrugContext()
    {
        var (encounterId, patientId) = await ExplainableAlertsTestHelper.SeedEncounterAsync(_fixture);
        await ExplainableAlertsTestHelper.AdministerMedicationAsync(
            _fixture, encounterId, "acetaminophen", dose: 1000, unit: "mg");
        await ExplainableAlertsTestHelper.FeedNews2VitalsAsync(
            _fixture, encounterId, patientId,
            respRate: 25, spo2: 91, systolicBp: 95, heartRate: 72,
            tempC: 39.2m, supplementalO2: 0m);

        var alert = await ExplainableAlertsTestHelper.WaitForFirstAlertWithExplanationAsync(
            _fixture, encounterId);

        alert.Explanation.Should().NotBeNull();
        alert.Explanation!.MedicationContext.Should().NotBeNull();
        alert.Explanation.MedicationContext!.DrugName.Should().BeEquivalentTo("acetaminophen");
    }

    [Fact]
    public async Task NarrativeSummary_IsHumanReadable()
    {
        var alert = await ExplainableAlertsTestHelper.GetAnyAlertWithExplanationAsync(_fixture);

        alert.Explanation.Should().NotBeNull();
        alert.Explanation!.NarrativeSummary.Should().MatchRegex("(NEWS2|SOFA|GCS|rapid deterioration)", "i");
    }

    [Fact]
    public async Task LegacyAlert_NullExplanation_Serializes()
    {
        var legacyId = await ExplainableAlertsTestHelper.SeedLegacyAlertWithoutExplanationAsync(_fixture);
        var body = await ExplainableAlertsTestHelper.GetAlertAsync<AlertResponse>(_fixture, legacyId);

        body.Explanation.Should().BeNull();
    }

    [Fact]
    public async Task ListByEncounter_ReturnsExplanation()
    {
        var (encounterId, _) = await ExplainableAlertsTestHelper.SeedEncounterWithVitalsAsync(
            _fixture, respRate: 25, spo2: 91, systolicBp: 95, heartRate: 72,
            tempC: 37.0m, supplementalO2: 0m);

        using var client = _fixture.CreateClient();
        var response = await client.GetAsync($"/api/v1/encounters/{encounterId}/alerts");
        response.EnsureSuccessStatusCode();
        var body = await response.Content.ReadFromJsonAsync<ApiResponse<PagedResult<AlertResponse>>>(
            ExplainableAlertsTestHelper.JsonOptions);

        body!.Data!.Items.Should().NotBeEmpty();
        body.Data.Items.Should().AllSatisfy(a =>
        {
            a.Explanation.Should().NotBeNull();
            a.Explanation!.NarrativeSummary.Should().NotBeNullOrWhiteSpace();
        });
    }

    [Fact]
    public async Task ScoringOutputs_Unchanged()
    {
        var before = await ExplainableAlertsTestHelper.RunNews2HighScoreReplayAsync(_fixture);
        var after = await ExplainableAlertsTestHelper.RunNews2HighScoreReplayAsync(_fixture);

        after.TotalScore.Should().Be(before.TotalScore);
        after.AlertType.Should().Be(before.AlertType);
    }

    [Fact]
    public async Task ExplainableAlert_OutboxPayload_IncludesExplanation()
    {
        var (encounterId, _) = await ExplainableAlertsTestHelper.SeedEncounterWithVitalsAsync(
            _fixture, respRate: 25, spo2: 91, systolicBp: 95, heartRate: 72,
            tempC: 37.0m, supplementalO2: 0m);

        using var scope = _fixture.Services.CreateScope();
        var db = scope.ServiceProvider.GetRequiredService<AppDbContext>();
        var outbox = await db.OutboxEvents
            .Where(e => e.Topic == "alert.generated" && e.PartitionKey == encounterId.ToString())
            .OrderByDescending(e => e.CreatedAt)
            .FirstAsync();

        outbox.Payload.Should().Contain("explanation");
        outbox.Payload.Should().Contain("narrativeSummary");
    }
}
