using NATS.Client.Core;

namespace NATS.Server.Benchmark.Tests.Infrastructure;

/// <summary>
/// Starts both a Go and .NET NATS server with JetStream enabled for benchmark testing.
/// Shared across all tests in the "Benchmark-JetStream" collection.
/// </summary>
public sealed class JetStreamServerPairFixture : IAsyncLifetime
{
    private GoServerProcess? _goServer;
    private DotNetServerProcess? _dotNetServer;
    private string? _goStoreDir;
    private string? _dotNetStoreDir;

    public int GoPort => _goServer?.Port ?? throw new InvalidOperationException("Go server not started");
    public int DotNetPort => _dotNetServer?.Port ?? throw new InvalidOperationException(".NET server not started");
    public bool GoAvailable => _goServer is not null;

    public async Task InitializeAsync()
    {
        _dotNetStoreDir = Path.Combine(Path.GetTempPath(), "nats-bench-dotnet-js-" + Guid.NewGuid().ToString("N")[..8]);
        Directory.CreateDirectory(_dotNetStoreDir);

        var dotNetConfig = $$"""
            jetstream {
                store_dir: "{{_dotNetStoreDir}}"
                max_mem_store: 256mb
                max_file_store: 1gb
            }
            """;

        _dotNetServer = new DotNetServerProcess(dotNetConfig);
        var dotNetTask = _dotNetServer.StartAsync();

        if (GoServerProcess.IsAvailable())
        {
            _goStoreDir = Path.Combine(Path.GetTempPath(), "nats-bench-go-js-" + Guid.NewGuid().ToString("N")[..8]);
            Directory.CreateDirectory(_goStoreDir);

            var goConfig = $$"""
                jetstream {
                    store_dir: "{{_goStoreDir}}"
                    max_mem_store: 256mb
                    max_file_store: 1gb
                }
                """;

            _goServer = new GoServerProcess(goConfig);
            await Task.WhenAll(dotNetTask, _goServer.StartAsync());
        }
        else
        {
            await dotNetTask;
        }
    }

    public async Task DisposeAsync()
    {
        if (_goServer is not null)
            await _goServer.DisposeAsync();
        if (_dotNetServer is not null)
            await _dotNetServer.DisposeAsync();

        CleanupDir(_goStoreDir);
        CleanupDir(_dotNetStoreDir);
    }

    public NatsConnection CreateGoClient()
        => new(new NatsOpts { Url = $"nats://127.0.0.1:{GoPort}" });

    public NatsConnection CreateDotNetClient()
        => new(new NatsOpts { Url = $"nats://127.0.0.1:{DotNetPort}" });

    private static void CleanupDir(string? dir)
    {
        if (dir is not null && Directory.Exists(dir))
        {
            try { Directory.Delete(dir, recursive: true); }
            catch { /* best-effort cleanup */ }
        }
    }
}
