using Microsoft.Extensions.Logging;
using MongoDB.Driver;
using StellaOps.Authority.Storage.Mongo.Documents;

namespace StellaOps.Authority.Storage.Mongo.Stores;

internal sealed class AuthorityClientStore : IAuthorityClientStore
{
    private readonly IMongoCollection<AuthorityClientDocument> collection;
    private readonly TimeProvider clock;
    private readonly ILogger<AuthorityClientStore> logger;

    public AuthorityClientStore(
        IMongoCollection<AuthorityClientDocument> collection,
        TimeProvider clock,
        ILogger<AuthorityClientStore> logger)
    {
        this.collection = collection ?? throw new ArgumentNullException(nameof(collection));
        this.clock = clock ?? throw new ArgumentNullException(nameof(clock));
        this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
    }

    public async ValueTask<AuthorityClientDocument?> FindByClientIdAsync(string clientId, CancellationToken cancellationToken)
    {
        if (string.IsNullOrWhiteSpace(clientId))
        {
            return null;
        }

        var id = clientId.Trim();
        return await collection.Find(c => c.ClientId == id)
            .FirstOrDefaultAsync(cancellationToken)
            .ConfigureAwait(false);
    }

    public async ValueTask UpsertAsync(AuthorityClientDocument document, CancellationToken cancellationToken)
    {
        ArgumentNullException.ThrowIfNull(document);

        document.UpdatedAt = clock.GetUtcNow();

        var filter = Builders<AuthorityClientDocument>.Filter.Eq(c => c.ClientId, document.ClientId);
        var options = new ReplaceOptions { IsUpsert = true };

        var result = await collection.ReplaceOneAsync(filter, document, options, cancellationToken).ConfigureAwait(false);

        if (result.UpsertedId is not null)
        {
            logger.LogInformation("Inserted Authority client {ClientId}.", document.ClientId);
        }
    }

    public async ValueTask<bool> DeleteByClientIdAsync(string clientId, CancellationToken cancellationToken)
    {
        if (string.IsNullOrWhiteSpace(clientId))
        {
            return false;
        }

        var id = clientId.Trim();
        var result = await collection.DeleteOneAsync(c => c.ClientId == id, cancellationToken).ConfigureAwait(false);
        return result.DeletedCount > 0;
    }
}
