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

namespace NATS.Server.LeafNodes.Tests.LeafNodes;

public class LeafConnectionAndRemoteConfigParityBatch1Tests
{
    [Fact]
    public async Task LeafConnection_role_helpers_reflect_connection_flags()
    {
        await using var connection = CreateConnection();

        connection.IsSolicitedLeafNode().ShouldBeFalse();
        connection.IsSpokeLeafNode().ShouldBeFalse();
        connection.IsHubLeafNode().ShouldBeTrue();
        connection.IsIsolatedLeafNode().ShouldBeFalse();

        connection.IsSolicited = true;
        connection.IsSpoke = true;
        connection.Isolated = true;

        connection.IsSolicitedLeafNode().ShouldBeTrue();
        connection.IsSpokeLeafNode().ShouldBeTrue();
        connection.IsHubLeafNode().ShouldBeFalse();
        connection.IsIsolatedLeafNode().ShouldBeTrue();
    }

    [Fact]
    public void RemoteLeafOptions_pick_next_url_round_robins()
    {
        var remote = new RemoteLeafOptions
        {
            Urls =
            [
                "nats://127.0.0.1:7422",
                "nats://127.0.0.1:7423",
                "nats://127.0.0.1:7424",
            ],
        };

        remote.PickNextUrl().ShouldBe("nats://127.0.0.1:7422");
        remote.GetCurrentUrl().ShouldBe("nats://127.0.0.1:7422");
        remote.PickNextUrl().ShouldBe("nats://127.0.0.1:7423");
        remote.PickNextUrl().ShouldBe("nats://127.0.0.1:7424");
        remote.PickNextUrl().ShouldBe("nats://127.0.0.1:7422");
    }

    [Fact]
    public void RemoteLeafOptions_pick_next_url_without_entries_throws()
    {
        var remote = new RemoteLeafOptions();
        Should.Throw<InvalidOperationException>(() => remote.PickNextUrl());
    }

    [Fact]
    public void RemoteLeafOptions_saves_tls_hostname_and_user_password_from_url()
    {
        var remote = new RemoteLeafOptions();

        remote.SaveTlsHostname("nats://leaf.example.com:7422");
        remote.TlsName.ShouldBe("leaf.example.com");

        remote.SaveUserPassword("nats://demo:secret@leaf.example.com:7422");
        remote.Username.ShouldBe("demo");
        remote.Password.ShouldBe("secret");
    }

    [Fact]
    public void RemoteLeafOptions_connect_delay_round_trips()
    {
        var remote = new RemoteLeafOptions();
        remote.GetConnectDelay().ShouldBe(TimeSpan.Zero);

        remote.SetConnectDelay(TimeSpan.FromSeconds(30));
        remote.GetConnectDelay().ShouldBe(TimeSpan.FromSeconds(30));
    }

    [Fact]
    public void RemoteLeafNodeStillValid_checks_configured_and_disabled_remotes()
    {
        var manager = new LeafNodeManager(
            new LeafNodeOptions
            {
                Host = "127.0.0.1",
                Port = 0,
                Remotes = ["127.0.0.1:7422"],
                RemoteLeaves =
                [
                    new RemoteLeafOptions
                    {
                        Urls = ["nats://127.0.0.1:7423"],
                    },
                ],
            },
            new ServerStats(),
            "S1",
            _ => { },
            _ => { },
            NullLogger<LeafNodeManager>.Instance);

        manager.RemoteLeafNodeStillValid("127.0.0.1:7422").ShouldBeTrue();
        manager.RemoteLeafNodeStillValid("nats://127.0.0.1:7423").ShouldBeTrue();
        manager.RemoteLeafNodeStillValid("127.0.0.1:7999").ShouldBeFalse();

        manager.DisableLeafConnect("127.0.0.1:7422");
        manager.RemoteLeafNodeStillValid("127.0.0.1:7422").ShouldBeFalse();
    }

    [Fact]
    public void LeafNode_delay_constants_match_go_defaults()
    {
        LeafNodeManager.LeafNodeReconnectDelayAfterLoopDetected.ShouldBe(TimeSpan.FromSeconds(30));
        LeafNodeManager.LeafNodeReconnectAfterPermViolation.ShouldBe(TimeSpan.FromSeconds(30));
        LeafNodeManager.LeafNodeReconnectDelayAfterClusterNameSame.ShouldBe(TimeSpan.FromSeconds(30));
        LeafNodeManager.LeafNodeWaitBeforeClose.ShouldBe(TimeSpan.FromSeconds(5));
    }

    private static LeafConnection CreateConnection()
    {
        var listener = new TcpListener(IPAddress.Loopback, 0);
        listener.Start();
        var endpoint = (IPEndPoint)listener.LocalEndpoint;

        var client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        client.Connect(endpoint);

        var server = listener.AcceptSocket();
        server.Dispose();
        listener.Stop();

        return new LeafConnection(client);
    }
}
