using NATS.Server.Auth;
using NATS.Server.Mqtt;
using NATS.Server.Subscriptions;

namespace NATS.Server.Mqtt.Tests;

/// <summary>
/// Tests for the MqttNatsClientAdapter and cross-protocol bridging concepts.
/// Verifies that MQTT connections can participate in the NATS SubList and
/// that topic/subject translation works end-to-end.
/// </summary>
public class MqttCrossProtocolTests
{
    [Fact]
    public void Adapter_implements_INatsClient()
    {
        using var stream = new MemoryStream();
        var listener = CreateTestListener();
        var connection = new MqttConnection(stream, listener);
        var adapter = new MqttNatsClientAdapter(connection, 42);

        adapter.Id.ShouldBe((ulong)42);
        adapter.Kind.ShouldBe(ClientKind.Client);
        adapter.ClientOpts.ShouldBeNull();
    }

    [Fact]
    public void Adapter_add_and_remove_subscription()
    {
        using var stream = new MemoryStream();
        var listener = CreateTestListener();
        var connection = new MqttConnection(stream, listener);
        var adapter = new MqttNatsClientAdapter(connection, 1);
        var account = new Account("test");
        adapter.Account = account;

        // Add subscription
        var sub = adapter.AddSubscription("sensor.temp", "sid1");
        sub.Subject.ShouldBe("sensor.temp");
        sub.Client.ShouldBe(adapter);
        adapter.Subscriptions.Count.ShouldBe(1);

        // Verify it's in the SubList
        var result = account.SubList.Match("sensor.temp");
        result.PlainSubs.ShouldContain(s => s.Sid == "sid1");

        // Remove subscription
        adapter.RemoveSubscription("sid1");
        adapter.Subscriptions.Count.ShouldBe(0);

        // Verify removed from SubList
        result = account.SubList.Match("sensor.temp");
        result.PlainSubs.ShouldNotContain(s => s.Sid == "sid1");
    }

    [Fact]
    public void Adapter_remove_all_subscriptions()
    {
        using var stream = new MemoryStream();
        var listener = CreateTestListener();
        var connection = new MqttConnection(stream, listener);
        var adapter = new MqttNatsClientAdapter(connection, 1);
        var account = new Account("test");
        adapter.Account = account;

        adapter.AddSubscription("a.b", "s1");
        adapter.AddSubscription("c.d", "s2");
        adapter.AddSubscription("e.f", "s3");
        adapter.Subscriptions.Count.ShouldBe(3);

        adapter.RemoveAllSubscriptions();
        adapter.Subscriptions.Count.ShouldBe(0);
    }

    [Fact]
    public void Adapter_queue_outbound_is_noop()
    {
        using var stream = new MemoryStream();
        var listener = CreateTestListener();
        var connection = new MqttConnection(stream, listener);
        var adapter = new MqttNatsClientAdapter(connection, 1);

        adapter.QueueOutbound(new byte[] { 1, 2, 3 }).ShouldBeTrue();
    }

    [Fact]
    public void Adapter_signal_flush_is_noop()
    {
        using var stream = new MemoryStream();
        var listener = CreateTestListener();
        var connection = new MqttConnection(stream, listener);
        var adapter = new MqttNatsClientAdapter(connection, 1);

        // Should not throw
        adapter.SignalFlush();
    }

    [Fact]
    public void Topic_mapper_integration_with_sublist()
    {
        var account = new Account("test");

        // Simulate an MQTT client subscribing to "sensor/+"
        var natsSubject = MqttTopicMapper.MqttToNats("sensor/+");
        natsSubject.ShouldBe("sensor.*");

        var sub = new Subscription
        {
            Subject = natsSubject,
            Sid = "mqtt-sub-1",
        };
        account.SubList.Insert(sub);

        // Simulate a NATS publish to "sensor.temp" — should match
        var result = account.SubList.Match("sensor.temp");
        result.PlainSubs.ShouldContain(s => s.Sid == "mqtt-sub-1");

        // "sensor.humidity" should also match
        result = account.SubList.Match("sensor.humidity");
        result.PlainSubs.ShouldContain(s => s.Sid == "mqtt-sub-1");

        // "other.temp" should NOT match
        result = account.SubList.Match("other.temp");
        result.PlainSubs.ShouldNotContain(s => s.Sid == "mqtt-sub-1");
    }

    [Fact]
    public void Topic_mapper_multilevel_wildcard_with_sublist()
    {
        var account = new Account("test");

        // MQTT subscribe to "home/#"
        var natsSubject = MqttTopicMapper.MqttToNats("home/#");
        natsSubject.ShouldBe("home.>");

        var sub = new Subscription
        {
            Subject = natsSubject,
            Sid = "mqtt-sub-2",
        };
        account.SubList.Insert(sub);

        // Should match multi-level subjects
        account.SubList.Match("home.living.light").PlainSubs
            .ShouldContain(s => s.Sid == "mqtt-sub-2");
        account.SubList.Match("home.kitchen").PlainSubs
            .ShouldContain(s => s.Sid == "mqtt-sub-2");
    }

    [Fact]
    public void Adapter_mqtt_client_id_exposed()
    {
        using var stream = new MemoryStream();
        var listener = CreateTestListener();
        var connection = new MqttConnection(stream, listener);
        var adapter = new MqttNatsClientAdapter(connection, 1);

        // ClientId comes from the underlying connection
        adapter.MqttClientId.ShouldBe(string.Empty); // not yet connected
    }

    private static MqttListener CreateTestListener()
        => new("127.0.0.1", 0);
}
