using System.Net;
using System.Net.Sockets;
using Microsoft.Extensions.Logging.Abstractions;
using NATS.Client.Core;
using NATS.Server.Auth;

using NATS.Server.TestUtilities;

namespace NATS.Server.Auth.Tests;

public class AccountIsolationTests : IAsyncLifetime
{
    private NatsServer _server = null!;
    private int _port;
    private readonly CancellationTokenSource _cts = new();
    private Task _serverTask = null!;
    public async Task InitializeAsync()
    {
        _port = TestPortAllocator.GetFreePort();
        _server = new NatsServer(new NatsOptions
        {
            Port = _port,
            Users =
            [
                new User { Username = "alice", Password = "pass", Account = "acct-a" },
                new User { Username = "bob", Password = "pass", Account = "acct-b" },
                new User { Username = "charlie", Password = "pass", Account = "acct-a" },
            ],
        }, NullLoggerFactory.Instance);

        _serverTask = _server.StartAsync(_cts.Token);
        await _server.WaitForReadyAsync();
    }

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

    [Fact]
    public async Task Same_account_receives_messages()
    {
        // Alice and Charlie are in acct-a
        await using var alice = new NatsConnection(new NatsOpts
        {
            Url = $"nats://alice:pass@127.0.0.1:{_port}",
        });
        await using var charlie = new NatsConnection(new NatsOpts
        {
            Url = $"nats://charlie:pass@127.0.0.1:{_port}",
        });

        await alice.ConnectAsync();
        await charlie.ConnectAsync();

        await using var sub = await charlie.SubscribeCoreAsync<string>("test.subject");
        await charlie.PingAsync();

        await alice.PublishAsync("test.subject", "from-alice");

        using var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(5));
        var msg = await sub.Msgs.ReadAsync(timeout.Token);
        msg.Data.ShouldBe("from-alice");
    }

    [Fact]
    public async Task Different_account_does_not_receive_messages()
    {
        // Alice is in acct-a, Bob is in acct-b
        await using var alice = new NatsConnection(new NatsOpts
        {
            Url = $"nats://alice:pass@127.0.0.1:{_port}",
        });
        await using var bob = new NatsConnection(new NatsOpts
        {
            Url = $"nats://bob:pass@127.0.0.1:{_port}",
        });

        await alice.ConnectAsync();
        await bob.ConnectAsync();

        await using var sub = await bob.SubscribeCoreAsync<string>("test.subject");
        await bob.PingAsync();

        await alice.PublishAsync("test.subject", "from-alice");

        // Bob should NOT receive this — wait briefly then verify nothing arrived
        using var timeout = new CancellationTokenSource(TimeSpan.FromMilliseconds(500));
        try
        {
            await sub.Msgs.ReadAsync(timeout.Token);
            throw new Exception("Bob should not have received a message from a different account");
        }
        catch (OperationCanceledException)
        {
            // Expected — no message received means accounts are properly isolated
            return;
        }
    }
}
