using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Nexus.Api.Data;
using Nexus.Api.Services;
using Testcontainers.PostgreSql;
using Xunit;

namespace Nexus.Api.Tests;

[Collection(DockerIntegrationTestEnvironment.CollectionName)]
public sealed class PostgresOpenClawOperationAuditStoreIntegrationTests
{
    [PostgreSqlIntegrationFact]
    [Trait("Category", "DockerIntegration")]
    public async Task Concurrent_store_instances_create_one_claim_and_one_in_doubt_result()
    {
        await using var postgres = new PostgreSqlBuilder("postgres:17-alpine")
            .WithDatabase("nexus_claims")
            .WithUsername("nexus_test")
            .WithPassword("nexus_test_password")
            .Build();
        await postgres.StartAsync();
        var connectionString = postgres.GetConnectionString();

        await using (var db = CreateDatabase(connectionString))
            await db.Database.MigrateAsync();

        await using var firstProvider = CreateProvider(connectionString);
        await using var secondProvider = CreateProvider(connectionString);
        var first = CreateStore(firstProvider);
        var second = CreateStore(secondProvider);
        var context = OpenClawInvocationContext.Create(
            "owner",
            "postgres-race-key",
            "postgres-race-correlation");
        var operation = new OpenClawOperationDescriptor(
            "cron.run",
            "cron-job",
            "job-1",
            OpenClawInvocationContextFactory.Hash("same-intent"));

        var results = await Task.WhenAll(
            first.ClaimAsync(context, operation),
            second.ClaimAsync(context, operation));

        Assert.Single(
            results,
            item => item.Disposition ==
                    OpenClawOperationClaimDisposition.Started);
        Assert.Single(
            results,
            item => item.Disposition ==
                    OpenClawOperationClaimDisposition.InDoubt);

        await using var verification = CreateDatabase(connectionString);
        Assert.Single(await verification.OperationClaims.ToArrayAsync());
    }

    private static NexusDbContext CreateDatabase(string connectionString)
        => new(new DbContextOptionsBuilder<NexusDbContext>()
            .UseNpgsql(connectionString)
            .Options);

    private static ServiceProvider CreateProvider(string connectionString)
    {
        var services = new ServiceCollection();
        services.AddDbContext<NexusDbContext>(options =>
            options.UseNpgsql(connectionString));
        return services.BuildServiceProvider();
    }

    private static PostgresOpenClawOperationAuditStore CreateStore(
        ServiceProvider provider)
        => new(
            provider.GetRequiredService<IServiceScopeFactory>(),
            Options.Create(new GatewayConnectorOptions()));
}
