using NATS.NKeys;
using NATS.Server.Auth;
using NATS.Server.Protocol;

namespace NATS.Server.Auth.Tests;

public class NKeyAuthenticatorTests
{
    private static (string PublicKey, string SignatureBase64) CreateSignedNonce(byte[] nonce)
    {
        var kp = KeyPair.CreatePair(PrefixByte.User);
        var publicKey = kp.GetPublicKey();
        var sig = new byte[64];
        kp.Sign(nonce, sig);
        var sigBase64 = Convert.ToBase64String(sig);
        return (publicKey, sigBase64);
    }

    private static string SignNonce(KeyPair kp, byte[] nonce)
    {
        var sig = new byte[64];
        kp.Sign(nonce, sig);
        return Convert.ToBase64String(sig);
    }

    [Fact]
    public void Returns_result_for_valid_signature()
    {
        var kp = KeyPair.CreatePair(PrefixByte.User);
        var publicKey = kp.GetPublicKey();
        var nonce = "test-nonce-123"u8.ToArray();
        var sigBase64 = SignNonce(kp, nonce);

        var nkeyUser = new NKeyUser { Nkey = publicKey };
        var auth = new NKeyAuthenticator([nkeyUser]);

        var ctx = new ClientAuthContext
        {
            Opts = new ClientOptions { Nkey = publicKey, Sig = sigBase64 },
            Nonce = nonce,
        };

        var result = auth.Authenticate(ctx);

        result.ShouldNotBeNull();
        result.Identity.ShouldBe(publicKey);
    }

    [Fact]
    public void Returns_null_for_invalid_signature()
    {
        var kp = KeyPair.CreatePair(PrefixByte.User);
        var publicKey = kp.GetPublicKey();
        var nonce = "test-nonce-123"u8.ToArray();

        var nkeyUser = new NKeyUser { Nkey = publicKey };
        var auth = new NKeyAuthenticator([nkeyUser]);

        var ctx = new ClientAuthContext
        {
            Opts = new ClientOptions { Nkey = publicKey, Sig = Convert.ToBase64String(new byte[64]) },
            Nonce = nonce,
        };

        auth.Authenticate(ctx).ShouldBeNull();
    }

    [Fact]
    public void Returns_null_for_unknown_nkey()
    {
        var kp = KeyPair.CreatePair(PrefixByte.User);
        var publicKey = kp.GetPublicKey();
        var nonce = "test-nonce-123"u8.ToArray();
        var sigBase64 = SignNonce(kp, nonce);

        var auth = new NKeyAuthenticator([]);

        var ctx = new ClientAuthContext
        {
            Opts = new ClientOptions { Nkey = publicKey, Sig = sigBase64 },
            Nonce = nonce,
        };

        auth.Authenticate(ctx).ShouldBeNull();
    }

    [Fact]
    public void Returns_null_when_no_nkey_provided()
    {
        var kp = KeyPair.CreatePair(PrefixByte.User);
        var publicKey = kp.GetPublicKey();
        var nkeyUser = new NKeyUser { Nkey = publicKey };
        var auth = new NKeyAuthenticator([nkeyUser]);

        var ctx = new ClientAuthContext
        {
            Opts = new ClientOptions(),
            Nonce = "nonce"u8.ToArray(),
        };

        auth.Authenticate(ctx).ShouldBeNull();
    }

    [Fact]
    public void Returns_permissions_from_nkey_user()
    {
        var kp = KeyPair.CreatePair(PrefixByte.User);
        var publicKey = kp.GetPublicKey();
        var nonce = "test-nonce"u8.ToArray();
        var sigBase64 = SignNonce(kp, nonce);

        var perms = new Permissions
        {
            Publish = new SubjectPermission { Allow = ["foo.>"] },
        };
        var nkeyUser = new NKeyUser { Nkey = publicKey, Permissions = perms };
        var auth = new NKeyAuthenticator([nkeyUser]);

        var ctx = new ClientAuthContext
        {
            Opts = new ClientOptions { Nkey = publicKey, Sig = sigBase64 },
            Nonce = nonce,
        };

        var result = auth.Authenticate(ctx);

        result.ShouldNotBeNull();
        result.Permissions.ShouldBe(perms);
    }
}
