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

/// <summary>
/// Integration tests for concurrent operations: duplicate batch creation and
/// parallel assignment attempts.
/// </summary>
[Collection("Database")]
public class ConcurrencyTests : IAsyncLifetime
{
    private readonly ApiFixture _fixture;

    public ConcurrencyTests(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;

    /// <summary>
    /// Two parallel assignment requests for the same batch — exactly one should succeed,
    /// the other should get 409 BATCH_ALREADY_ASSIGNED.
    /// </summary>
    [Fact]
    public async Task ParallelAssign_SameBatch_ExactlyOneSucceeds()
    {
        // Arrange: upload a batch
        var intakeClient = await AuthHelper.LoginAsync(_fixture, "intake1");

        var fileContent = new ByteArrayContent(
            System.Text.Encoding.ASCII.GetBytes($"%PDF-1.4\n%%EOF\n%test-{Guid.NewGuid()}"));
        fileContent.Headers.ContentType =
            new System.Net.Http.Headers.MediaTypeHeaderValue("application/pdf");

        var formData = new MultipartFormDataContent
        {
            { fileContent, "file", "test.pdf" },
            { new StringContent("VITALS_SHEET"), "batchType" }
        };

        var uploadResponse = await intakeClient.PostAsync("/api/v1/digitization-batches", formData);
        uploadResponse.EnsureSuccessStatusCode();
        var uploadBody = await uploadResponse.Content.ReadFromJsonAsync<JsonElement>();
        var batchId = uploadBody.GetProperty("data").GetProperty("id").GetGuid();

        // Get user IDs
        using var scope = _fixture.Services.CreateScope();
        var db = scope.ServiceProvider.GetRequiredService<AppDbContext>();
        var entry1Id = await BatchSeedHelper.UserIdAsync(db, "entry1");
        var entry2Id = await BatchSeedHelper.UserIdAsync(db, "entry2");

        // Act: parallel assignment
        var task1 = intakeClient.PatchAsJsonAsync(
            $"/api/v1/digitization-batches/{batchId}/assign",
            new { entryClerkUserId = entry1Id });

        var task2 = intakeClient.PatchAsJsonAsync(
            $"/api/v1/digitization-batches/{batchId}/assign",
            new { entryClerkUserId = entry2Id });

        var results = await Task.WhenAll(task1, task2);

        // Assert: exactly one 200 and one 409
        var statuses = results.Select(r => r.StatusCode).OrderBy(s => s).ToList();
        statuses.Should().Contain(HttpStatusCode.OK);
        statuses.Should().Contain(HttpStatusCode.Conflict);
    }

    /// <summary>
    /// Two uploads with the same file content for the same patient — the Redis dedup
    /// guard should ensure exactly one succeeds.
    /// </summary>
    [Fact]
    public async Task ParallelUpload_SameSha256_ExactlyOneSucceeds()
    {
        // Arrange: create a patient first via a promoted batch
        var batchId = await BatchPipelineHelper.CreateAndVerifyBatchAsync(_fixture);
        var approverClient = await AuthHelper.LoginAsync(_fixture, "approver1");
        approverClient.DefaultRequestHeaders.Add("Idempotency-Key", Guid.NewGuid().ToString());
        var approveResp = await approverClient.PostAsJsonAsync(
            $"/api/v1/digitization-batches/{batchId}/approve",
            new ApproveRequest());
        approveResp.EnsureSuccessStatusCode();
        approverClient.DefaultRequestHeaders.Remove("Idempotency-Key");

        var approveBody = await approveResp.Content.ReadFromJsonAsync<JsonElement>();
        var patientId = approveBody.GetProperty("data").GetProperty("patientId").GetGuid();

        // Same file content for both uploads
        var fileBytes = System.Text.Encoding.ASCII.GetBytes(
            $"%PDF-1.4\n%%EOF\n%duplicate-test-{Guid.NewGuid()}");

        var intakeClient = await AuthHelper.LoginAsync(_fixture, "intake1");

        // Act: two parallel uploads with same content + same patient
        var task1 = UploadWithBytes(intakeClient, fileBytes, patientId);
        var task2 = UploadWithBytes(intakeClient, fileBytes, patientId);

        var results = await Task.WhenAll(task1, task2);

        // Assert: exactly one success and one conflict
        var statuses = results.Select(r => r.StatusCode).OrderBy(s => s).ToList();
        statuses.Should().Contain(HttpStatusCode.Created);
        statuses.Should().Contain(HttpStatusCode.Conflict);
    }

    private static async Task<HttpResponseMessage> UploadWithBytes(
        HttpClient client, byte[] fileBytes, Guid patientId)
    {
        var fileContent = new ByteArrayContent(fileBytes);
        fileContent.Headers.ContentType =
            new System.Net.Http.Headers.MediaTypeHeaderValue("application/pdf");

        var formData = new MultipartFormDataContent
        {
            { fileContent, "file", "duplicate.pdf" },
            { new StringContent("VITALS_SHEET"), "batchType" },
            { new StringContent(patientId.ToString()), "patientId" }
        };

        return await client.PostAsync("/api/v1/digitization-batches", formData);
    }
}
