using System.Security.Cryptography.X509Certificates;

namespace NATS.Server.Auth;

/// <summary>
/// Authenticates clients by mapping TLS certificate subject DN to configured users.
/// Corresponds to Go server/auth.go checkClientTLSCertSubject.
/// </summary>
public sealed class TlsMapAuthenticator : IAuthenticator
{
    private readonly Dictionary<string, User> _usersByDn;
    private readonly Dictionary<string, User> _usersByCn;

    public TlsMapAuthenticator(IReadOnlyList<User> users)
    {
        _usersByDn = new Dictionary<string, User>(StringComparer.OrdinalIgnoreCase);
        _usersByCn = new Dictionary<string, User>(StringComparer.OrdinalIgnoreCase);
        foreach (var user in users)
        {
            _usersByDn[user.Username] = user;
            _usersByCn[user.Username] = user;
        }
    }

    public AuthResult? Authenticate(ClientAuthContext context)
    {
        var cert = context.ClientCertificate;
        if (cert == null)
            return null;

        var dn = cert.SubjectName;
        var dnString = dn.Name; // RFC 2253 format

        // Try exact DN match first
        if (_usersByDn.TryGetValue(dnString, out var user))
            return BuildResult(user);

        // Try CN extraction
        var cn = ExtractCn(dn);
        if (cn != null && _usersByCn.TryGetValue(cn, out user))
            return BuildResult(user);

        return null;
    }

    private static string? ExtractCn(X500DistinguishedName dn)
    {
        var dnString = dn.Name;
        foreach (var rdn in dnString.Split(',', StringSplitOptions.TrimEntries))
        {
            if (rdn.StartsWith("CN=", StringComparison.OrdinalIgnoreCase))
                return rdn[3..];
        }
        return null;
    }

    private static AuthResult BuildResult(User user)
    {
        return new AuthResult
        {
            Identity = user.Username,
            AccountName = user.Account,
            Permissions = user.Permissions,
            Expiry = user.ConnectionDeadline,
        };
    }
}
