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

/// <summary>
/// Integration tests for correction supersession via the full HTTP pipeline.
/// </summary>
[Collection("Database")]
public class CorrectionSupersessionTests : IAsyncLifetime
{
    private readonly ApiFixture _fixture;

    public CorrectionSupersessionTests(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);
    }

    public Task DisposeAsync() => Task.CompletedTask;

    [Fact]
    public async Task WrongPotassium_CorrectionBatch_SupersedesOriginal()
    {
        // Act 1: promote original batch with wrong potassium (3.5)
        var (originalBatchId, patientId) =
            await CorrectionPipelineHelper.PromoteLabBatchAsync(_fixture, potassiumValue: 3.5m);

        using var scope = _fixture.Services.CreateScope();
        var db = scope.ServiceProvider.GetRequiredService<AppDbContext>();

        var liveObsAfterOriginal = await db.LiveObservations
            .Where(o => o.SourceBatchId == originalBatchId)
            .ToListAsync();

        liveObsAfterOriginal.Should().HaveCount(2);
        liveObsAfterOriginal.First(o => o.ObservationCode == "K").Value.Should().Be(3.5m);

        // Act 2: promote correction batch with correct potassium (5.3)
        var correctionBatchId = await CorrectionPipelineHelper.PromoteCorrectionBatchAsync(
            _fixture, originalBatchId, patientId, potassiumValue: 5.3m);

        db.ChangeTracker.Clear();

        // Assert: original observations superseded, correction observations live
        var originalObs = await db.LiveObservations
            .Where(o => o.SourceBatchId == originalBatchId)
            .ToListAsync();
        originalObs.Should().AllSatisfy(o =>
        {
            o.IsSuperseded.Should().BeTrue();
            o.SupersededByBatchId.Should().Be(correctionBatchId);
        });
        originalObs.First(o => o.ObservationCode == "K").Value.Should().Be(3.5m,
            "original erroneous value is preserved for audit");

        var correctionObs = await db.LiveObservations
            .Where(o => o.SourceBatchId == correctionBatchId && !o.IsSuperseded)
            .ToListAsync();
        correctionObs.Should().HaveCount(2);
        correctionObs.First(o => o.ObservationCode == "K").Value.Should().Be(5.3m);

        var allObs = await db.LiveObservations.Where(o => o.PatientId == patientId).ToListAsync();
        allObs.Should().HaveCount(4, "2 original (superseded) + 2 correction (active)");

        var originalEvents = await db.DigitizationEvents
            .Where(e => e.BatchId == originalBatchId)
            .ToListAsync();
        originalEvents.Should().Contain(e => e.EventType == DigitizationEventType.Superseded);
    }

    [Fact]
    public async Task CannotSupersede_NonPromotedBatch()
    {
        // Arrange: batch still in PendingVerification
        using var scope = _fixture.Services.CreateScope();
        var db = scope.ServiceProvider.GetRequiredService<AppDbContext>();
        var entry1Id = await BatchSeedHelper.UserIdAsync(db, "entry1");
        var pendingBatch = await BatchSeedHelper.SeedBatchInPendingVerificationAsync(db, entry1Id);

        // Act: attempt correction upload against non-promoted batch
        var client = await AuthHelper.LoginAsync(_fixture, "intake1");
        var fileContent = new ByteArrayContent([0x25, 0x50, 0x44, 0x46]);
        fileContent.Headers.ContentType =
            new System.Net.Http.Headers.MediaTypeHeaderValue("application/pdf");
        var formData = new MultipartFormDataContent
        {
            { fileContent, "file", "test.pdf" },
            { new StringContent("LAB_RESULTS"), "batchType" },
            { new StringContent("BACKFILL"), "track" },
            { new StringContent(pendingBatch.Id.ToString()), "supersedesBatchId" }
        };

        var response = await client.PostAsync("/api/v1/digitization-batches", formData);

        // Assert
        response.StatusCode.Should().Be(HttpStatusCode.UnprocessableEntity);
        var body = await response.Content.ReadAsStringAsync();
        body.Should().Contain("Only promoted batches can be superseded");
    }

    [Fact]
    public async Task CannotSupersede_AlreadySupersededBatch()
    {
        // Arrange: promote original + one correction (supersedes original)
        var (originalBatchId, patientId) =
            await CorrectionPipelineHelper.PromoteLabBatchAsync(_fixture, potassiumValue: 100m);
        await CorrectionPipelineHelper.PromoteCorrectionBatchAsync(
            _fixture, originalBatchId, patientId, potassiumValue: 102m);

        // Act: attempt second correction against the already-superseded original
        var client = await AuthHelper.LoginAsync(_fixture, "intake1");
        var fileContent = new ByteArrayContent([0x25, 0x50, 0x44, 0x46]);
        fileContent.Headers.ContentType =
            new System.Net.Http.Headers.MediaTypeHeaderValue("application/pdf");
        var formData = new MultipartFormDataContent
        {
            { fileContent, "file", "test2.pdf" },
            { new StringContent("LAB_RESULTS"), "batchType" },
            { new StringContent("BACKFILL"), "track" },
            { new StringContent(patientId.ToString()), "patientId" },
            { new StringContent(originalBatchId.ToString()), "supersedesBatchId" }
        };

        var response = await client.PostAsync("/api/v1/digitization-batches", formData);

        // Assert
        response.StatusCode.Should().Be(HttpStatusCode.Conflict);
        var body = await response.Content.ReadAsStringAsync();
        body.Should().Contain("already been superseded");
    }

    [Fact]
    public async Task PatientDigitizationHistory_ShowsFullCorrectionChain()
    {
        // Arrange: promote original + correction
        var (originalBatchId, patientId) =
            await CorrectionPipelineHelper.PromoteLabBatchAsync(_fixture, potassiumValue: 3.5m);
        var correctionBatchId = await CorrectionPipelineHelper.PromoteCorrectionBatchAsync(
            _fixture, originalBatchId, patientId, potassiumValue: 5.3m);

        // Act
        var client = await AuthHelper.LoginAsync(_fixture, "admin1");
        var response = await client.GetAsync(
            $"/api/v1/patients/{patientId}/digitization-history");

        // Assert
        response.StatusCode.Should().Be(HttpStatusCode.OK);
        var result = await response.Content
            .ReadFromJsonAsync<ApiResponse<PatientDigitizationHistoryResponse>>();

        result!.Data!.TotalBatches.Should().Be(2);
        result.Data.SupersededBatches.Should().Be(1);

        var originalEntry = result.Data.Entries.First(e => e.BatchId == originalBatchId);
        originalEntry.HasBeenSuperseded.Should().BeTrue();
        originalEntry.SupersededByBatchId.Should().Be(correctionBatchId);

        var correctionEntry = result.Data.Entries.First(e => e.BatchId == correctionBatchId);
        correctionEntry.IsCorrection.Should().BeTrue();
        correctionEntry.SupersedesBatchId.Should().Be(originalBatchId);
    }

    [Fact]
    public async Task PatientHistory_ReturnsNotFound_ForUnknownPatient()
    {
        var client = await AuthHelper.LoginAsync(_fixture, "admin1");
        var response = await client.GetAsync(
            $"/api/v1/patients/{Guid.NewGuid()}/digitization-history");

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