using NATS.NKeys;

namespace NATS.Server.Auth;

/// <summary>
/// Authenticates clients using NKey (Ed25519) public-key signature verification.
/// The server sends a random nonce in the INFO message. The client signs the nonce
/// with their private key and sends the public key + base64-encoded signature in CONNECT.
/// The server verifies the signature against the registered NKey users.
/// </summary>
/// <remarks>
/// Reference: golang/nats-server/server/auth.go — checkNKeyAuth
/// </remarks>
public sealed class NKeyAuthenticator(IEnumerable<NKeyUser> nkeyUsers) : IAuthenticator
{
    private readonly Dictionary<string, NKeyUser> _nkeys = nkeyUsers.ToDictionary(
        u => u.Nkey,
        u => u,
        StringComparer.Ordinal);

    public AuthResult? Authenticate(ClientAuthContext context)
    {
        var clientNkey = context.Opts.Nkey;
        if (string.IsNullOrEmpty(clientNkey))
            return null;

        if (!_nkeys.TryGetValue(clientNkey, out var nkeyUser))
            return null;

        var clientSig = context.Opts.Sig;
        if (string.IsNullOrEmpty(clientSig))
            return null;

        try
        {
            // Decode base64 signature (handle both standard and URL-safe base64)
            byte[] sigBytes;
            try
            {
                sigBytes = Convert.FromBase64String(clientSig);
            }
            catch (FormatException)
            {
                // Try URL-safe base64 by converting to standard base64
                var padded = clientSig.Replace('-', '+').Replace('_', '/');
                padded = padded.PadRight(padded.Length + (4 - padded.Length % 4) % 4, '=');
                sigBytes = Convert.FromBase64String(padded);
            }

            var kp = KeyPair.FromPublicKey(clientNkey);
            if (!kp.Verify(context.Nonce, sigBytes))
                return null;
        }
        catch
        {
            return null;
        }

        return new AuthResult
        {
            Identity = clientNkey,
            AccountName = nkeyUser.Account,
            Permissions = nkeyUser.Permissions,
        };
    }
}
