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

using NATS.Server.TestUtilities;

namespace NATS.Server.Auth.Tests;

public class NKeyIntegrationTests : IAsyncLifetime
{
    private NatsServer _server = null!;
    private int _port;
    private readonly CancellationTokenSource _cts = new();
    private Task _serverTask = null!;
    private KeyPair _userKeyPair = null!;
    private string _userSeed = null!;
    private string _userPublicKey = null!;
    public async Task InitializeAsync()
    {
        _port = TestPortAllocator.GetFreePort();
        _userKeyPair = KeyPair.CreatePair(PrefixByte.User);
        _userPublicKey = _userKeyPair.GetPublicKey();
        _userSeed = _userKeyPair.GetSeed();

        _server = new NatsServer(new NatsOptions
        {
            Port = _port,
            NKeys = [new NKeyUser { Nkey = _userPublicKey }],
        }, NullLoggerFactory.Instance);

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

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

    [Fact]
    public async Task NKey_auth_success()
    {
        await using var client = new NatsConnection(new NatsOpts
        {
            Url = $"nats://127.0.0.1:{_port}",
            AuthOpts = new NatsAuthOpts { NKey = _userPublicKey, Seed = _userSeed },
        });

        await client.ConnectAsync();
        await client.PingAsync();
    }

    [Fact]
    public async Task NKey_auth_wrong_key_fails()
    {
        // Generate a different key pair not known to the server
        var otherKp = KeyPair.CreatePair(PrefixByte.User);

        await using var client = new NatsConnection(new NatsOpts
        {
            Url = $"nats://127.0.0.1:{_port}",
            AuthOpts = new NatsAuthOpts { NKey = otherKp.GetPublicKey(), Seed = otherKp.GetSeed() },
            MaxReconnectRetry = 0,
        });

        await Should.ThrowAsync<NatsException>(async () =>
        {
            await client.ConnectAsync();
            await client.PingAsync();
        });
    }
}
