using System.Collections.Concurrent;

namespace NATS.Server.Auth.Jwt;

/// <summary>
/// Resolves account JWTs by account NKey public key. The server calls
/// <see cref="FetchAsync"/> during client authentication to obtain the
/// account JWT that was previously published by an account operator.
/// </summary>
/// <remarks>
/// Reference: golang/nats-server/server/accounts.go:4035+ — AccountResolver interface
/// and MemAccResolver implementation.
/// </remarks>
public interface IAccountResolver
{
    /// <summary>
    /// Fetches the JWT for the given account NKey. Returns <c>null</c> when
    /// the NKey is not known to this resolver.
    /// </summary>
    Task<string?> FetchAsync(string accountNkey);

    /// <summary>
    /// Stores (or replaces) the JWT for the given account NKey. Callers that
    /// target a read-only resolver should check <see cref="IsReadOnly"/> first.
    /// </summary>
    Task StoreAsync(string accountNkey, string jwt);

    /// <summary>
    /// When <c>true</c>, <see cref="StoreAsync"/> is not supported and will
    /// throw <see cref="NotSupportedException"/>. Directory and URL resolvers
    /// may be read-only; in-memory resolvers are not.
    /// </summary>
    bool IsReadOnly { get; }
}

/// <summary>
/// In-memory account resolver backed by a <see cref="ConcurrentDictionary{TKey,TValue}"/>.
/// Suitable for tests and simple single-operator deployments where account JWTs
/// are provided at startup via <see cref="StoreAsync"/>.
/// </summary>
/// <remarks>
/// Reference: golang/nats-server/server/accounts.go — MemAccResolver
/// </remarks>
public sealed class MemAccountResolver : IAccountResolver
{
    private readonly ConcurrentDictionary<string, string> _accounts =
        new(StringComparer.Ordinal);

    /// <inheritdoc/>
    public bool IsReadOnly => false;

    /// <inheritdoc/>
    public Task<string?> FetchAsync(string accountNkey)
    {
        _accounts.TryGetValue(accountNkey, out var jwt);
        return Task.FromResult(jwt);
    }

    /// <inheritdoc/>
    public Task StoreAsync(string accountNkey, string jwt)
    {
        _accounts[accountNkey] = jwt;
        return Task.CompletedTask;
    }
}
