using System.Net;
using System.Net.Sockets;
using Microsoft.Extensions.Logging.Abstractions;
using NATS.Server.Configuration;

namespace NATS.Server.Tests;

public class MonitorClusterEndpointTests
{
    [Fact]
    public async Task Routez_gatewayz_leafz_accountz_return_non_stub_runtime_data()
    {
        await using var fx = await MonitorFixture.StartClusterEnabledAsync();
        (await fx.GetJsonAsync("/routez")).ShouldContain("routes");
        (await fx.GetJsonAsync("/gatewayz")).ShouldContain("gateways");
        (await fx.GetJsonAsync("/leafz")).ShouldContain("leafs");
        (await fx.GetJsonAsync("/accountz")).ShouldContain("accounts");
    }
}

internal sealed class MonitorFixture : IAsyncDisposable
{
    private readonly NatsServer _server;
    private readonly CancellationTokenSource _cts;
    private readonly HttpClient _http;
    private readonly int _monitorPort;

    private MonitorFixture(NatsServer server, CancellationTokenSource cts, HttpClient http, int monitorPort)
    {
        _server = server;
        _cts = cts;
        _http = http;
        _monitorPort = monitorPort;
    }

    public static async Task<MonitorFixture> StartClusterEnabledAsync()
    {
        var monitorPort = GetFreePort();
        var options = new NatsOptions
        {
            Host = "127.0.0.1",
            Port = 0,
            MonitorPort = monitorPort,
            Cluster = new ClusterOptions
            {
                Host = "127.0.0.1",
                Port = 0,
            },
            Gateway = new GatewayOptions
            {
                Host = "127.0.0.1",
                Port = 0,
                Name = "M",
            },
            LeafNode = new LeafNodeOptions
            {
                Host = "127.0.0.1",
                Port = 0,
            },
        };

        var server = new NatsServer(options, NullLoggerFactory.Instance);
        var cts = new CancellationTokenSource();
        _ = server.StartAsync(cts.Token);
        await server.WaitForReadyAsync();

        var http = new HttpClient();
        for (var i = 0; i < 50; i++)
        {
            try
            {
                var response = await http.GetAsync($"http://127.0.0.1:{monitorPort}/healthz");
                if (response.IsSuccessStatusCode)
                    break;
            }
            catch
            {
            }

            await Task.Delay(50);
        }

        return new MonitorFixture(server, cts, http, monitorPort);
    }

    public Task<string> GetJsonAsync(string path)
    {
        return _http.GetStringAsync($"http://127.0.0.1:{_monitorPort}{path}");
    }

    public async ValueTask DisposeAsync()
    {
        _http.Dispose();
        await _cts.CancelAsync();
        _server.Dispose();
        _cts.Dispose();
    }

    private static int GetFreePort()
    {
        using var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        socket.Bind(new IPEndPoint(IPAddress.Loopback, 0));
        return ((IPEndPoint)socket.LocalEndPoint!).Port;
    }
}
