using DodoSSH.Infrastructure;
using Microsoft.EntityFrameworkCore;
using Testcontainers.PostgreSql;
using Xunit;

namespace DodoSSH.Infrastructure.Tests;

/// <summary>
/// One PostgreSQL container per test assembly, migrated once.
/// </summary>
/// <remarks>
/// A container per test would dominate the runtime. Tests that write must therefore use distinct
/// identifiers rather than assuming an empty database.
/// </remarks>
public sealed class PostgresFixture : IAsyncLifetime
{
    // Image passed to the constructor: the parameterless overload is obsolete in
    // Testcontainers 4.13 and pinning the tag here keeps the test image in step with the one
    // deploy/docker-compose.dev.yml uses.
    private readonly PostgreSqlContainer container = new PostgreSqlBuilder("postgres:18-alpine")
        .WithDatabase("dodossh")
        .WithUsername("postgres")
        .WithPassword("test")
        .Build();

    /// <summary>Connection string for the running container.</summary>
    public string ConnectionString => container.GetConnectionString();

    /// <inheritdoc />
    public async ValueTask InitializeAsync()
    {
        await container.StartAsync();

        await using var context = CreateContext();
        await context.Database.MigrateAsync();
    }

    /// <inheritdoc />
    public async ValueTask DisposeAsync() => await container.DisposeAsync();

    /// <summary>Creates a context against the container.</summary>
    public DodoDbContext CreateContext()
    {
        var options = new DbContextOptionsBuilder<DodoDbContext>()
            .UseNpgsql(ConnectionString, npgsql =>
                npgsql.MigrationsHistoryTable("__EFMigrationsHistory", DodoDbContext.SchemaName))
            .UseSnakeCaseNamingConvention()
            .Options;

        return new DodoDbContext(options);
    }
}

/// <summary>Shares one container across every test class in the assembly.</summary>
[CollectionDefinition(Name)]
public sealed class PostgresCollection : ICollectionFixture<PostgresFixture>
{
    /// <summary>Collection name.</summary>
    public const string Name = "postgres";
}
