using System.Text.Json;
using FluentAssertions;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using StackExchange.Redis;
using VigilCare.Simulation;

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

    public ClinicalRefactorEndToEndTests(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 redis = scope.ServiceProvider.GetRequiredService<IConnectionMultiplexer>();
        await DataSeeder.SeedThresholdsOnlyAsync(db, redis);
    }

    public Task DisposeAsync() => Task.CompletedTask;

    [Fact]
    public async Task SofaProgressionScenario_EndToEnd()
    {
        var scenario = ScenarioReplayHelper.Load("sepsis-sofa-progression-01.json");
        var (_, encounterId) = await ScenarioReplayHelper.ReplayObservationsAsync(
            _client, scenario, _fixture.Services);

        await ScenarioReplayHelper.WaitForAlertTypeAsync(
            _fixture.Services, encounterId, AlertType.QsofaScreen, TimeSpan.FromSeconds(60));

        await ScenarioReplayHelper.WaitForAlertTypeAsync(
            _fixture.Services, encounterId, AlertType.SofaSepsis, TimeSpan.FromSeconds(60));

        SepsisBundle? bundle = null;
        var deadline = DateTime.UtcNow + TimeSpan.FromSeconds(10);
        while (DateTime.UtcNow < deadline)
        {
            using var scope = _fixture.Services.CreateScope();
            var db = scope.ServiceProvider.GetRequiredService<AppDbContext>();
            bundle = await db.SepsisBundles.FirstOrDefaultAsync(b => b.EncounterId == encounterId);
            if (bundle is not null) break;
            await Task.Delay(500);
        }
        bundle.Should().NotBeNull("expected sepsis bundle for encounter after SOFA_SEPSIS alert");
        bundle!.TriggeringAlertType.Should().Be("SOFA_SEPSIS");
    }

    [Fact]
    public async Task GcsDeclineScenario_EndToEnd()
    {
        var scenario = ScenarioReplayHelper.Load("neurological-decline-gcs-01.json");
        var (_, encounterId) = await ScenarioReplayHelper.ReplayObservationsAsync(
            _client, scenario, _fixture.Services);

        await ScenarioReplayHelper.WaitForAlertTypeAsync(
            _fixture.Services, encounterId, AlertType.GcsWarning, TimeSpan.FromSeconds(60));

        await ScenarioReplayHelper.WaitForAlertTypeAsync(
            _fixture.Services, encounterId, AlertType.GcsCritical, TimeSpan.FromSeconds(60));

        await ScenarioReplayHelper.AssertNoAlertTypeAsync(
            _fixture.Services, encounterId, AlertType.SofaSepsis, TimeSpan.FromSeconds(5));

        await ScenarioReplayHelper.AssertNoAlertTypeAsync(
            _fixture.Services, encounterId, AlertTypeExtensions.FromDbString("SEPSIS_WARNING"), TimeSpan.FromSeconds(1));
    }

    [Fact]
    public async Task PartialSofaScenario_StalenessFlags()
    {
        var scenario = ScenarioReplayHelper.Load("sofa-partial-spo2-fallback-01.json");
        var (_, encounterId) = await ScenarioReplayHelper.ReplayObservationsAsync(
            _client, scenario, _fixture.Services);

        var jsonOpts = new JsonSerializerOptions { PropertyNameCaseInsensitive = true };
        var sofa = await ScenarioReplayHelper.WaitForSofaScoreAsync(
            _fixture.Services, encounterId, TimeSpan.FromSeconds(60),
            s =>
            {
                if (string.IsNullOrEmpty(s.StalenessFlags)) return false;
                var f = JsonSerializer.Deserialize<SofaStalenessFlags>(s.StalenessFlags, jsonOpts);
                return f?.UsedSpO2Fallback == true;
            });

        var flags = JsonSerializer.Deserialize<SofaStalenessFlags>(sofa.StalenessFlags!, jsonOpts);
        flags!.UsedSpO2Fallback.Should().BeTrue("expected SpO2/FiO2 proxy for respiratory SOFA");
    }

    [Theory]
    [InlineData("stable-baseline-01.json")]
    [InlineData("cardiac-arrest-post-mi-01.json")]
    [InlineData("post-op-hemorrhage-01.json")]
    [InlineData("respiratory-failure-asthma-01.json")]
    [InlineData("dka-electrolyte-01.json")]
    [InlineData("medication-false-alarm-01.json")]
    [InlineData("hypothermia-elderly-01.json")]
    [InlineData("uti-sepsis-elderly-01.json")]
    public void ExistingScenarios_ValidateWithoutErrors(string fileName)
    {
        var scenario = ScenarioReplayHelper.Load(fileName);
        var errors = ScenarioValidator.Validate(scenario);
        errors.Should().BeEmpty(string.Join("; ", errors));
    }

    [Fact]
    public async Task UtiSepsisScenario_NowUsesSofa()
    {
        var scenario = ScenarioReplayHelper.Load("uti-sepsis-elderly-01.json");
        var (_, encounterId) = await ScenarioReplayHelper.ReplayObservationsAsync(
            _client, scenario, _fixture.Services);

        await ScenarioReplayHelper.WaitForAlertTypeAsync(
            _fixture.Services, encounterId, AlertType.SofaSepsis, TimeSpan.FromSeconds(60));

        await ScenarioReplayHelper.AssertNoAlertTypeAsync(
            _fixture.Services, encounterId, AlertTypeExtensions.FromDbString("SEPSIS_WARNING"), TimeSpan.FromSeconds(5));
    }
}