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

namespace StellaOps.Authority.Storage.Mongo.Stores;

internal sealed class AuthorityScopeStore : IAuthorityScopeStore
{
    private readonly IMongoCollection<AuthorityScopeDocument> collection;
    private readonly TimeProvider clock;
    private readonly ILogger<AuthorityScopeStore> logger;

    public AuthorityScopeStore(
        IMongoCollection<AuthorityScopeDocument> collection,
        TimeProvider clock,
        ILogger<AuthorityScopeStore> 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<AuthorityScopeDocument?> FindByNameAsync(string name, CancellationToken cancellationToken)
    {
        if (string.IsNullOrWhiteSpace(name))
        {
            return null;
        }

        var normalized = name.Trim();
        return await collection.Find(s => s.Name == normalized)
            .FirstOrDefaultAsync(cancellationToken)
            .ConfigureAwait(false);
    }

    public async ValueTask<IReadOnlyList<AuthorityScopeDocument>> ListAsync(CancellationToken cancellationToken)
    {
        var cursor = await collection.FindAsync(FilterDefinition<AuthorityScopeDocument>.Empty, cancellationToken: cancellationToken).ConfigureAwait(false);
        return await cursor.ToListAsync(cancellationToken).ConfigureAwait(false);
    }

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

        document.UpdatedAt = clock.GetUtcNow();

        var filter = Builders<AuthorityScopeDocument>.Filter.Eq(s => s.Name, document.Name);
        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 scope {ScopeName}.", document.Name);
        }
    }

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

        var normalized = name.Trim();
        var result = await collection.DeleteOneAsync(s => s.Name == normalized, cancellationToken).ConfigureAwait(false);
        return result.DeletedCount > 0;
    }
}
