using JSMR.Infrastructure.Data;
using Microsoft.EntityFrameworkCore;
using MySqlConnector;
using Testcontainers.MariaDb;

namespace JSMR.Tests.Fixtures;

public class MariaDbFixture : IAsyncLifetime
{
    const int MajorVersion = 10;
    const int MinorVersion = 11;
    const int Build = 6;

    public MariaDbContainer? MariaDbContainer { get; private set; }

    public string ConnectionString { get; private set; } = default!;

    public async ValueTask InitializeAsync()
    {
        MariaDbContainer = new MariaDbBuilder($"mariadb:{MajorVersion}.{MinorVersion}.{Build}")
            //.WithImage($"mariadb:{MajorVersion}.{MinorVersion}.{Build}")
            .Build();

        await MariaDbContainer.StartAsync();

        ConnectionString = MariaDbContainer.GetConnectionString();

        await using AppDbContext context = CreateDbContext();
        await context.Database.EnsureCreatedAsync();
        //await context.Database.MigrateAsync(); // Testing
        await OnInitializedAsync(context);
    }

    protected virtual Task OnInitializedAsync(AppDbContext context)
    {
        return Task.FromResult(Task.CompletedTask);
    }

    public async ValueTask DisposeAsync()
    {
        if (MariaDbContainer is not null)
        {
            await MariaDbContainer.StopAsync();
            await MariaDbContainer.DisposeAsync();
        }

        GC.SuppressFinalize(this);
    }

    public AppDbContext CreateDbContext()
    {
        MySqlServerVersion serverVersion = new(new Version(MajorVersion, MinorVersion, Build));

        DbContextOptions<AppDbContext> options = new DbContextOptionsBuilder<AppDbContext>()
            .UseMySql(ConnectionString, serverVersion,
                o => o.EnableRetryOnFailure())
            .EnableSensitiveDataLogging()
            .Options;

        return new AppDbContext(options);
    }

    public async Task ResetAsync()
    {
        await using AppDbContext context = CreateDbContext();

        await context.Database.EnsureDeletedAsync();
        await context.Database.EnsureCreatedAsync();
    }
}


public static class MariaTestDb
{
    public static async Task<AppDbContext> CreateIsolatedAsync(string rootConnectionString, Func<AppDbContext, Task>? seed = null)
    {
        string databaseName = $"t_{DateTime.UtcNow:yyyyMMddHHmmss}_{Guid.NewGuid():N}";

        await CreateDatabaseAsync(rootConnectionString, databaseName);

        MySqlConnectionStringBuilder connectionStringBuilder = new(rootConnectionString)
        {
            Database = databaseName
        };

        AppDbContext dbContext = CreateDbContext(connectionStringBuilder.ConnectionString);
        await dbContext.Database.EnsureCreatedAsync();

        if (seed != null)
            await seed(dbContext);

        return dbContext;
    }

    private static async Task CreateDatabaseAsync(string rootConnectionString, string databaseName)
    {
        await using MySqlConnection connection = new(rootConnectionString);

        await connection.OpenAsync();

        await using MySqlCommand command = connection.CreateCommand();
        command.CommandText = $"CREATE DATABASE `{databaseName}` CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;";

        await command.ExecuteNonQueryAsync();
    }

    private static AppDbContext CreateDbContext(string connectionString)
    {
        DbContextOptions<AppDbContext> options = new DbContextOptionsBuilder<AppDbContext>()
            .UseMySql(connectionString, ServerVersion.AutoDetect(connectionString),
                o => o.EnableRetryOnFailure())
            .EnableSensitiveDataLogging()
            .Options;

        return new AppDbContext(options);
    }
}