using System.Text;
using NATS.NKeys;
using NATS.Server.Auth;
using NATS.Server.Auth.Jwt;
using NATS.Server.Protocol;

namespace NATS.Server.Tests;

public class JwtAuthenticatorTests
{
    private static string Base64UrlEncode(string input) =>
        Base64UrlEncode(Encoding.UTF8.GetBytes(input));

    private static string Base64UrlEncode(byte[] input) =>
        Convert.ToBase64String(input).TrimEnd('=').Replace('+', '-').Replace('/', '_');

    private static string BuildSignedToken(string payloadJson, KeyPair signingKey)
    {
        var header = Base64UrlEncode("""{"typ":"JWT","alg":"ed25519-nkey"}""");
        var payload = Base64UrlEncode(payloadJson);
        var signingInput = Encoding.UTF8.GetBytes($"{header}.{payload}");
        var sig = new byte[64];
        signingKey.Sign(signingInput, sig);
        return $"{header}.{payload}.{Base64UrlEncode(sig)}";
    }

    private static string SignNonce(KeyPair kp, byte[] nonce)
    {
        var sig = new byte[64];
        kp.Sign(nonce, sig);
        return Convert.ToBase64String(sig).TrimEnd('=').Replace('+', '-').Replace('/', '_');
    }

    [Fact]
    public async Task Valid_bearer_jwt_returns_auth_result()
    {
        var operatorKp = KeyPair.CreatePair(PrefixByte.Operator);
        var accountKp = KeyPair.CreatePair(PrefixByte.Account);
        var userKp = KeyPair.CreatePair(PrefixByte.User);

        var operatorPub = operatorKp.GetPublicKey();
        var accountPub = accountKp.GetPublicKey();
        var userPub = userKp.GetPublicKey();

        var accountPayload = $$"""
        {
            "sub":"{{accountPub}}",
            "iss":"{{operatorPub}}",
            "iat":1700000000,
            "nats":{"type":"account","version":2}
        }
        """;
        var accountJwt = BuildSignedToken(accountPayload, operatorKp);

        var userPayload = $$"""
        {
            "sub":"{{userPub}}",
            "iss":"{{accountPub}}",
            "iat":1700000000,
            "nats":{
                "type":"user","version":2,
                "bearer_token":true,
                "issuer_account":"{{accountPub}}"
            }
        }
        """;
        var userJwt = BuildSignedToken(userPayload, accountKp);

        var resolver = new MemAccountResolver();
        await resolver.StoreAsync(accountPub, accountJwt);

        var auth = new JwtAuthenticator([operatorPub], resolver);

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

        var result = auth.Authenticate(ctx);

        result.ShouldNotBeNull();
        result.Identity.ShouldBe(userPub);
        result.AccountName.ShouldBe(accountPub);
    }

    [Fact]
    public async Task Valid_jwt_with_nonce_signature_returns_auth_result()
    {
        var operatorKp = KeyPair.CreatePair(PrefixByte.Operator);
        var accountKp = KeyPair.CreatePair(PrefixByte.Account);
        var userKp = KeyPair.CreatePair(PrefixByte.User);

        var operatorPub = operatorKp.GetPublicKey();
        var accountPub = accountKp.GetPublicKey();
        var userPub = userKp.GetPublicKey();

        var accountPayload = $$"""
        {
            "sub":"{{accountPub}}",
            "iss":"{{operatorPub}}",
            "iat":1700000000,
            "nats":{"type":"account","version":2}
        }
        """;
        var accountJwt = BuildSignedToken(accountPayload, operatorKp);

        var userPayload = $$"""
        {
            "sub":"{{userPub}}",
            "iss":"{{accountPub}}",
            "iat":1700000000,
            "nats":{
                "type":"user","version":2,
                "issuer_account":"{{accountPub}}"
            }
        }
        """;
        var userJwt = BuildSignedToken(userPayload, accountKp);

        var resolver = new MemAccountResolver();
        await resolver.StoreAsync(accountPub, accountJwt);

        var auth = new JwtAuthenticator([operatorPub], resolver);

        var nonce = "test-nonce-data"u8.ToArray();
        var sig = SignNonce(userKp, nonce);

        var ctx = new ClientAuthContext
        {
            Opts = new ClientOptions { JWT = userJwt, Nkey = userPub, Sig = sig },
            Nonce = nonce,
        };

        var result = auth.Authenticate(ctx);

        result.ShouldNotBeNull();
        result.Identity.ShouldBe(userPub);
        result.AccountName.ShouldBe(accountPub);
    }

    [Fact]
    public void No_jwt_returns_null()
    {
        var operatorKp = KeyPair.CreatePair(PrefixByte.Operator);
        var resolver = new MemAccountResolver();
        var auth = new JwtAuthenticator([operatorKp.GetPublicKey()], resolver);

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

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

    [Fact]
    public void Non_jwt_string_returns_null()
    {
        var operatorKp = KeyPair.CreatePair(PrefixByte.Operator);
        var resolver = new MemAccountResolver();
        var auth = new JwtAuthenticator([operatorKp.GetPublicKey()], resolver);

        var ctx = new ClientAuthContext
        {
            Opts = new ClientOptions { JWT = "not-a-jwt" },
            Nonce = "nonce"u8.ToArray(),
        };

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

    [Fact]
    public async Task Expired_jwt_returns_null()
    {
        var operatorKp = KeyPair.CreatePair(PrefixByte.Operator);
        var accountKp = KeyPair.CreatePair(PrefixByte.Account);
        var userKp = KeyPair.CreatePair(PrefixByte.User);

        var operatorPub = operatorKp.GetPublicKey();
        var accountPub = accountKp.GetPublicKey();
        var userPub = userKp.GetPublicKey();

        var accountPayload = $$"""
        {
            "sub":"{{accountPub}}",
            "iss":"{{operatorPub}}",
            "iat":1700000000,
            "nats":{"type":"account","version":2}
        }
        """;
        var accountJwt = BuildSignedToken(accountPayload, operatorKp);

        // Expired in 2020
        var userPayload = $$"""
        {
            "sub":"{{userPub}}",
            "iss":"{{accountPub}}",
            "iat":1500000000,
            "exp":1600000000,
            "nats":{
                "type":"user","version":2,
                "bearer_token":true,
                "issuer_account":"{{accountPub}}"
            }
        }
        """;
        var userJwt = BuildSignedToken(userPayload, accountKp);

        var resolver = new MemAccountResolver();
        await resolver.StoreAsync(accountPub, accountJwt);

        var auth = new JwtAuthenticator([operatorPub], resolver);

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

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

    [Fact]
    public async Task Revoked_user_returns_null()
    {
        var operatorKp = KeyPair.CreatePair(PrefixByte.Operator);
        var accountKp = KeyPair.CreatePair(PrefixByte.Account);
        var userKp = KeyPair.CreatePair(PrefixByte.User);

        var operatorPub = operatorKp.GetPublicKey();
        var accountPub = accountKp.GetPublicKey();
        var userPub = userKp.GetPublicKey();

        // Account JWT with revocation for user
        var accountPayload = $$"""
        {
            "sub":"{{accountPub}}",
            "iss":"{{operatorPub}}",
            "iat":1700000000,
            "nats":{
                "type":"account","version":2,
                "revocations":{
                    "{{userPub}}":1700000001
                }
            }
        }
        """;
        var accountJwt = BuildSignedToken(accountPayload, operatorKp);

        // User JWT issued at 1700000000 (before revocation time 1700000001)
        var userPayload = $$"""
        {
            "sub":"{{userPub}}",
            "iss":"{{accountPub}}",
            "iat":1700000000,
            "nats":{
                "type":"user","version":2,
                "bearer_token":true,
                "issuer_account":"{{accountPub}}"
            }
        }
        """;
        var userJwt = BuildSignedToken(userPayload, accountKp);

        var resolver = new MemAccountResolver();
        await resolver.StoreAsync(accountPub, accountJwt);

        var auth = new JwtAuthenticator([operatorPub], resolver);

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

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

    [Fact]
    public async Task Untrusted_operator_returns_null()
    {
        var operatorKp = KeyPair.CreatePair(PrefixByte.Operator);
        var accountKp = KeyPair.CreatePair(PrefixByte.Account);
        var userKp = KeyPair.CreatePair(PrefixByte.User);

        var operatorPub = operatorKp.GetPublicKey();
        var accountPub = accountKp.GetPublicKey();
        var userPub = userKp.GetPublicKey();

        var accountPayload = $$"""
        {
            "sub":"{{accountPub}}",
            "iss":"{{operatorPub}}",
            "iat":1700000000,
            "nats":{"type":"account","version":2}
        }
        """;
        var accountJwt = BuildSignedToken(accountPayload, operatorKp);

        var userPayload = $$"""
        {
            "sub":"{{userPub}}",
            "iss":"{{accountPub}}",
            "iat":1700000000,
            "nats":{
                "type":"user","version":2,
                "bearer_token":true,
                "issuer_account":"{{accountPub}}"
            }
        }
        """;
        var userJwt = BuildSignedToken(userPayload, accountKp);

        var resolver = new MemAccountResolver();
        await resolver.StoreAsync(accountPub, accountJwt);

        // Use a different trusted key that doesn't match the operator
        var otherOperator = KeyPair.CreatePair(PrefixByte.Operator).GetPublicKey();
        var auth = new JwtAuthenticator([otherOperator], resolver);

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

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

    [Fact]
    public void Unknown_account_returns_null()
    {
        var operatorKp = KeyPair.CreatePair(PrefixByte.Operator);
        var accountKp = KeyPair.CreatePair(PrefixByte.Account);
        var userKp = KeyPair.CreatePair(PrefixByte.User);

        var operatorPub = operatorKp.GetPublicKey();
        var accountPub = accountKp.GetPublicKey();
        var userPub = userKp.GetPublicKey();

        var userPayload = $$"""
        {
            "sub":"{{userPub}}",
            "iss":"{{accountPub}}",
            "iat":1700000000,
            "nats":{
                "type":"user","version":2,
                "bearer_token":true,
                "issuer_account":"{{accountPub}}"
            }
        }
        """;
        var userJwt = BuildSignedToken(userPayload, accountKp);

        // Don't store the account JWT in the resolver
        var resolver = new MemAccountResolver();
        var auth = new JwtAuthenticator([operatorPub], resolver);

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

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

    [Fact]
    public async Task Non_bearer_without_sig_returns_null()
    {
        var operatorKp = KeyPair.CreatePair(PrefixByte.Operator);
        var accountKp = KeyPair.CreatePair(PrefixByte.Account);
        var userKp = KeyPair.CreatePair(PrefixByte.User);

        var operatorPub = operatorKp.GetPublicKey();
        var accountPub = accountKp.GetPublicKey();
        var userPub = userKp.GetPublicKey();

        var accountPayload = $$"""
        {
            "sub":"{{accountPub}}",
            "iss":"{{operatorPub}}",
            "iat":1700000000,
            "nats":{"type":"account","version":2}
        }
        """;
        var accountJwt = BuildSignedToken(accountPayload, operatorKp);

        // Non-bearer user JWT
        var userPayload = $$"""
        {
            "sub":"{{userPub}}",
            "iss":"{{accountPub}}",
            "iat":1700000000,
            "nats":{
                "type":"user","version":2,
                "issuer_account":"{{accountPub}}"
            }
        }
        """;
        var userJwt = BuildSignedToken(userPayload, accountKp);

        var resolver = new MemAccountResolver();
        await resolver.StoreAsync(accountPub, accountJwt);

        var auth = new JwtAuthenticator([operatorPub], resolver);

        var ctx = new ClientAuthContext
        {
            Opts = new ClientOptions { JWT = userJwt }, // No Sig provided
            Nonce = "nonce"u8.ToArray(),
        };

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

    [Fact]
    public async Task Jwt_with_permissions_returns_permissions()
    {
        var operatorKp = KeyPair.CreatePair(PrefixByte.Operator);
        var accountKp = KeyPair.CreatePair(PrefixByte.Account);
        var userKp = KeyPair.CreatePair(PrefixByte.User);

        var operatorPub = operatorKp.GetPublicKey();
        var accountPub = accountKp.GetPublicKey();
        var userPub = userKp.GetPublicKey();

        var accountPayload = $$"""
        {
            "sub":"{{accountPub}}",
            "iss":"{{operatorPub}}",
            "iat":1700000000,
            "nats":{"type":"account","version":2}
        }
        """;
        var accountJwt = BuildSignedToken(accountPayload, operatorKp);

        var userPayload = $$"""
        {
            "sub":"{{userPub}}",
            "iss":"{{accountPub}}",
            "iat":1700000000,
            "nats":{
                "type":"user","version":2,
                "bearer_token":true,
                "issuer_account":"{{accountPub}}",
                "pub":{"allow":["foo.>","bar.*"]}
            }
        }
        """;
        var userJwt = BuildSignedToken(userPayload, accountKp);

        var resolver = new MemAccountResolver();
        await resolver.StoreAsync(accountPub, accountJwt);

        var auth = new JwtAuthenticator([operatorPub], resolver);

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

        var result = auth.Authenticate(ctx);

        result.ShouldNotBeNull();
        result.Permissions.ShouldNotBeNull();
        result.Permissions.Publish.ShouldNotBeNull();
        result.Permissions.Publish.Allow.ShouldNotBeNull();
        result.Permissions.Publish.Allow.ShouldContain("foo.>");
        result.Permissions.Publish.Allow.ShouldContain("bar.*");
    }

    [Fact]
    public async Task Signing_key_based_user_jwt_succeeds()
    {
        var operatorKp = KeyPair.CreatePair(PrefixByte.Operator);
        var accountKp = KeyPair.CreatePair(PrefixByte.Account);
        var signingKp = KeyPair.CreatePair(PrefixByte.Account);
        var userKp = KeyPair.CreatePair(PrefixByte.User);

        var operatorPub = operatorKp.GetPublicKey();
        var accountPub = accountKp.GetPublicKey();
        var signingPub = signingKp.GetPublicKey();
        var userPub = userKp.GetPublicKey();

        // Account JWT with signing key
        var accountPayload = $$"""
        {
            "sub":"{{accountPub}}",
            "iss":"{{operatorPub}}",
            "iat":1700000000,
            "nats":{
                "type":"account","version":2,
                "signing_keys":["{{signingPub}}"]
            }
        }
        """;
        var accountJwt = BuildSignedToken(accountPayload, operatorKp);

        // User JWT issued by the signing key
        var userPayload = $$"""
        {
            "sub":"{{userPub}}",
            "iss":"{{signingPub}}",
            "iat":1700000000,
            "nats":{
                "type":"user","version":2,
                "bearer_token":true,
                "issuer_account":"{{accountPub}}"
            }
        }
        """;
        var userJwt = BuildSignedToken(userPayload, signingKp);

        var resolver = new MemAccountResolver();
        await resolver.StoreAsync(accountPub, accountJwt);

        var auth = new JwtAuthenticator([operatorPub], resolver);

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

        var result = auth.Authenticate(ctx);

        result.ShouldNotBeNull();
        result.Identity.ShouldBe(userPub);
        result.AccountName.ShouldBe(accountPub);
    }

    [Fact]
    public async Task Wildcard_revocation_returns_null()
    {
        var operatorKp = KeyPair.CreatePair(PrefixByte.Operator);
        var accountKp = KeyPair.CreatePair(PrefixByte.Account);
        var userKp = KeyPair.CreatePair(PrefixByte.User);

        var operatorPub = operatorKp.GetPublicKey();
        var accountPub = accountKp.GetPublicKey();
        var userPub = userKp.GetPublicKey();

        // Account JWT with wildcard revocation
        var accountPayload = $$"""
        {
            "sub":"{{accountPub}}",
            "iss":"{{operatorPub}}",
            "iat":1700000000,
            "nats":{
                "type":"account","version":2,
                "revocations":{
                    "*":1700000001
                }
            }
        }
        """;
        var accountJwt = BuildSignedToken(accountPayload, operatorKp);

        // User JWT issued at 1700000000 (before wildcard revocation)
        var userPayload = $$"""
        {
            "sub":"{{userPub}}",
            "iss":"{{accountPub}}",
            "iat":1700000000,
            "nats":{
                "type":"user","version":2,
                "bearer_token":true,
                "issuer_account":"{{accountPub}}"
            }
        }
        """;
        var userJwt = BuildSignedToken(userPayload, accountKp);

        var resolver = new MemAccountResolver();
        await resolver.StoreAsync(accountPub, accountJwt);

        var auth = new JwtAuthenticator([operatorPub], resolver);

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

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

    // =========================================================================
    // allowed_connection_types tests
    // =========================================================================

    [Fact]
    public async Task Allowed_connection_types_allows_standard_context()
    {
        var operatorKp = KeyPair.CreatePair(PrefixByte.Operator);
        var accountKp = KeyPair.CreatePair(PrefixByte.Account);
        var userKp = KeyPair.CreatePair(PrefixByte.User);

        var operatorPub = operatorKp.GetPublicKey();
        var accountPub = accountKp.GetPublicKey();
        var userPub = userKp.GetPublicKey();

        var accountPayload = $$"""
        {
            "sub":"{{accountPub}}",
            "iss":"{{operatorPub}}",
            "iat":1700000000,
            "nats":{"type":"account","version":2}
        }
        """;
        var accountJwt = BuildSignedToken(accountPayload, operatorKp);

        var userPayload = $$"""
        {
            "sub":"{{userPub}}",
            "iss":"{{accountPub}}",
            "iat":1700000000,
            "nats":{
                "type":"user","version":2,
                "bearer_token":true,
                "issuer_account":"{{accountPub}}",
                "allowed_connection_types":["STANDARD"]
            }
        }
        """;
        var userJwt = BuildSignedToken(userPayload, accountKp);

        var resolver = new MemAccountResolver();
        await resolver.StoreAsync(accountPub, accountJwt);

        var auth = new JwtAuthenticator([operatorPub], resolver);

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

        var result = auth.Authenticate(ctx);

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

    [Fact]
    public async Task Allowed_connection_types_rejects_mqtt_only_for_standard_context()
    {
        var operatorKp = KeyPair.CreatePair(PrefixByte.Operator);
        var accountKp = KeyPair.CreatePair(PrefixByte.Account);
        var userKp = KeyPair.CreatePair(PrefixByte.User);

        var operatorPub = operatorKp.GetPublicKey();
        var accountPub = accountKp.GetPublicKey();
        var userPub = userKp.GetPublicKey();

        var accountPayload = $$"""
        {
            "sub":"{{accountPub}}",
            "iss":"{{operatorPub}}",
            "iat":1700000000,
            "nats":{"type":"account","version":2}
        }
        """;
        var accountJwt = BuildSignedToken(accountPayload, operatorKp);

        // User JWT only allows MQTT connections
        var userPayload = $$"""
        {
            "sub":"{{userPub}}",
            "iss":"{{accountPub}}",
            "iat":1700000000,
            "nats":{
                "type":"user","version":2,
                "bearer_token":true,
                "issuer_account":"{{accountPub}}",
                "allowed_connection_types":["MQTT"]
            }
        }
        """;
        var userJwt = BuildSignedToken(userPayload, accountKp);

        var resolver = new MemAccountResolver();
        await resolver.StoreAsync(accountPub, accountJwt);

        var auth = new JwtAuthenticator([operatorPub], resolver);

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

        // Should reject: STANDARD is not in allowed_connection_types
        auth.Authenticate(ctx).ShouldBeNull();
    }

    [Fact]
    public async Task Allowed_connection_types_allows_known_even_with_unknown_values()
    {
        var operatorKp = KeyPair.CreatePair(PrefixByte.Operator);
        var accountKp = KeyPair.CreatePair(PrefixByte.Account);
        var userKp = KeyPair.CreatePair(PrefixByte.User);

        var operatorPub = operatorKp.GetPublicKey();
        var accountPub = accountKp.GetPublicKey();
        var userPub = userKp.GetPublicKey();

        var accountPayload = $$"""
        {
            "sub":"{{accountPub}}",
            "iss":"{{operatorPub}}",
            "iat":1700000000,
            "nats":{"type":"account","version":2}
        }
        """;
        var accountJwt = BuildSignedToken(accountPayload, operatorKp);

        // User JWT allows STANDARD and an unknown type
        var userPayload = $$"""
        {
            "sub":"{{userPub}}",
            "iss":"{{accountPub}}",
            "iat":1700000000,
            "nats":{
                "type":"user","version":2,
                "bearer_token":true,
                "issuer_account":"{{accountPub}}",
                "allowed_connection_types":["STANDARD","SOME_NEW_TYPE"]
            }
        }
        """;
        var userJwt = BuildSignedToken(userPayload, accountKp);

        var resolver = new MemAccountResolver();
        await resolver.StoreAsync(accountPub, accountJwt);

        var auth = new JwtAuthenticator([operatorPub], resolver);

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

        var result = auth.Authenticate(ctx);

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

    [Fact]
    public async Task Allowed_connection_types_rejects_when_only_unknown_values_present()
    {
        var operatorKp = KeyPair.CreatePair(PrefixByte.Operator);
        var accountKp = KeyPair.CreatePair(PrefixByte.Account);
        var userKp = KeyPair.CreatePair(PrefixByte.User);

        var operatorPub = operatorKp.GetPublicKey();
        var accountPub = accountKp.GetPublicKey();
        var userPub = userKp.GetPublicKey();

        var accountPayload = $$"""
        {
            "sub":"{{accountPub}}",
            "iss":"{{operatorPub}}",
            "iat":1700000000,
            "nats":{"type":"account","version":2}
        }
        """;
        var accountJwt = BuildSignedToken(accountPayload, operatorKp);

        // User JWT only allows an unknown connection type
        var userPayload = $$"""
        {
            "sub":"{{userPub}}",
            "iss":"{{accountPub}}",
            "iat":1700000000,
            "nats":{
                "type":"user","version":2,
                "bearer_token":true,
                "issuer_account":"{{accountPub}}",
                "allowed_connection_types":["SOME_NEW_TYPE"]
            }
        }
        """;
        var userJwt = BuildSignedToken(userPayload, accountKp);

        var resolver = new MemAccountResolver();
        await resolver.StoreAsync(accountPub, accountJwt);

        var auth = new JwtAuthenticator([operatorPub], resolver);

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

        // Should reject: STANDARD is not in allowed_connection_types
        auth.Authenticate(ctx).ShouldBeNull();
    }

    [Fact]
    public async Task Allowed_connection_types_is_case_insensitive_for_input_values()
    {
        var operatorKp = KeyPair.CreatePair(PrefixByte.Operator);
        var accountKp = KeyPair.CreatePair(PrefixByte.Account);
        var userKp = KeyPair.CreatePair(PrefixByte.User);

        var operatorPub = operatorKp.GetPublicKey();
        var accountPub = accountKp.GetPublicKey();
        var userPub = userKp.GetPublicKey();

        var accountPayload = $$"""
        {
            "sub":"{{accountPub}}",
            "iss":"{{operatorPub}}",
            "iat":1700000000,
            "nats":{"type":"account","version":2}
        }
        """;
        var accountJwt = BuildSignedToken(accountPayload, operatorKp);

        // User JWT allows "standard" (lowercase)
        var userPayload = $$"""
        {
            "sub":"{{userPub}}",
            "iss":"{{accountPub}}",
            "iat":1700000000,
            "nats":{
                "type":"user","version":2,
                "bearer_token":true,
                "issuer_account":"{{accountPub}}",
                "allowed_connection_types":["standard"]
            }
        }
        """;
        var userJwt = BuildSignedToken(userPayload, accountKp);

        var resolver = new MemAccountResolver();
        await resolver.StoreAsync(accountPub, accountJwt);

        var auth = new JwtAuthenticator([operatorPub], resolver);

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

        // Should allow: case-insensitive match of "standard" == "STANDARD"
        var result = auth.Authenticate(ctx);

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