using Shouldly;
using ZB.MOM.NatsNet.Server;

namespace ZB.MOM.NatsNet.Server.Tests.ImplBacklog;

public sealed partial class LeafNodeHandlerTests
{
    [Fact] // T:1908
    public void LeafNodeTLSWithCerts_ShouldSucceed()
    {
        var options = new ServerOptions();
        var errors = new List<Exception>();
        var warnings = new List<Exception>();

        var parseError = ServerOptions.ParseLeafNodes(
            Map(
                ("listen", "127.0.0.1:7422"),
                ("tls", Map(("verify", true), ("map", true), ("timeout", 2L)))),
            options,
            errors,
            warnings);

        parseError.ShouldBeNull();
        errors.ShouldBeEmpty();
        options.LeafNode.Port.ShouldBe(7422);
        options.LeafNode.TlsConfig.ShouldNotBeNull();
        options.LeafNode.TlsTimeout.ShouldBe(2d);
        options.LeafNode.TlsMap.ShouldBeTrue();
    }

    [Fact] // T:1909
    public void LeafNodeTLSRemoteWithNoCerts_ShouldSucceed()
    {
        var errors = new List<Exception>();
        var warnings = new List<Exception>();

        var remotes = ServerOptions.ParseRemoteLeafNodes(
            Arr(
                Map(
                    ("url", "nats://localhost:7422"),
                    ("tls", Map(("timeout", 5L))))),
            errors,
            warnings);

        errors.ShouldBeEmpty();
        remotes.Count.ShouldBe(1);
        remotes[0].TlsTimeout.ShouldBe(5d);

        remotes = ServerOptions.ParseRemoteLeafNodes(
            Arr(
                Map(
                    ("url", "nats://localhost:7422"),
                    ("tls", Map()))),
            errors,
            warnings);

        remotes.Count.ShouldBe(1);
        remotes[0].TlsTimeout.ShouldBeGreaterThan(0d);
    }

    [Fact] // T:1932
    public void LeafNodeOriginClusterInfo_ShouldSucceed()
    {
        var errors = new List<Exception>();
        var warnings = new List<Exception>();
        var remotes = ServerOptions.ParseRemoteLeafNodes(
            Arr(
                Map(
                    ("url", "nats://127.0.0.1:7422"),
                    ("account", "A"),
                    ("first_info_timeout", "4s"))),
            errors,
            warnings);

        errors.ShouldBeEmpty();
        remotes.Count.ShouldBe(1);
        remotes[0].LocalAccount.ShouldBe("A");
        remotes[0].FirstInfoTimeout.ShouldBe(TimeSpan.FromSeconds(4));
    }

    [Fact] // T:1939
    public void LeafNodeTLSConfigReload_ShouldSucceed()
    {
        var options = new ServerOptions();
        var errors = new List<Exception>();
        var warnings = new List<Exception>();

        var parseError = ServerOptions.ParseLeafNodes(
            Map(("tls", Map(("verify", true), ("timeout", 2L)))),
            options,
            errors,
            warnings);

        parseError.ShouldBeNull();
        errors.ShouldBeEmpty();
        options.LeafNode.TlsTimeout.ShouldBe(2d);

        parseError = ServerOptions.ParseLeafNodes(
            Map(("tls", Map(("verify", true), ("timeout", 5L)))),
            options,
            errors,
            warnings);

        parseError.ShouldBeNull();
        errors.ShouldBeEmpty();
        options.LeafNode.TlsTimeout.ShouldBe(5d);
    }

    [Fact] // T:1943
    public void LeafNodeWSRemoteCompressAndMaskingOptions_ShouldSucceed()
    {
        var errors = new List<Exception>();
        var warnings = new List<Exception>();

        var remotes = ServerOptions.ParseRemoteLeafNodes(
            Arr(
                Map(
                    ("url", "ws://127.0.0.1:7422"),
                    ("ws_compression", true),
                    ("ws_no_masking", true))),
            errors,
            warnings);

        errors.ShouldBeEmpty();
        remotes.Count.ShouldBe(1);
        remotes[0].Websocket.Compression.ShouldBeTrue();
        remotes[0].Websocket.NoMasking.ShouldBeTrue();
    }

    private static Dictionary<string, object?> Map(params (string Key, object? Value)[] entries)
    {
        var map = new Dictionary<string, object?>(StringComparer.OrdinalIgnoreCase);
        foreach (var (key, value) in entries)
            map[key] = value;
        return map;
    }

    private static List<object?> Arr(params object?[] entries) => [.. entries];
}
