using NATS.Client.Core;
using NATS.E2E.Tests.Infrastructure;

namespace NATS.E2E.Tests;

[Collection("E2E")]
public class BasicTests(NatsServerFixture fixture)
{
    [Fact]
    public async Task ConnectAndPing()
    {
        await using var client = fixture.CreateClient();
        await client.ConnectAsync();

        await client.PingAsync();

        client.ConnectionState.ShouldBe(NatsConnectionState.Open);
    }

    [Fact]
    public async Task PubSub()
    {
        await using var pub = fixture.CreateClient();
        await using var sub = fixture.CreateClient();

        await pub.ConnectAsync();
        await sub.ConnectAsync();

        await using var subscription = await sub.SubscribeCoreAsync<string>("e2e.test.pubsub");
        await sub.PingAsync(); // Flush to ensure subscription is active

        await pub.PublishAsync("e2e.test.pubsub", "hello e2e");

        using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(10));
        var msg = await subscription.Msgs.ReadAsync(cts.Token);

        msg.Data.ShouldBe("hello e2e");
    }

    [Fact]
    public async Task RequestReply()
    {
        await using var requester = fixture.CreateClient();
        await using var responder = fixture.CreateClient();

        await requester.ConnectAsync();
        await responder.ConnectAsync();

        await using var subscription = await responder.SubscribeCoreAsync<string>("e2e.test.rpc");
        await responder.PingAsync(); // Flush to ensure subscription is active

        // Background task to read and reply
        var responderTask = Task.Run(async () =>
        {
            using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(10));
            var msg = await subscription.Msgs.ReadAsync(cts.Token);
            await responder.PublishAsync(msg.ReplyTo!, $"reply: {msg.Data}");
        });

        using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(10));
        var reply = await requester.RequestAsync<string, string>("e2e.test.rpc", "ping", cancellationToken: cts.Token);

        reply.Data.ShouldBe("reply: ping");

        await responderTask; // Ensure no exceptions in the responder
    }
}
