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

namespace StellaOps.Authority.Storage.Mongo.Stores;

internal sealed class AuthorityUserStore : IAuthorityUserStore
{
    private readonly IMongoCollection<AuthorityUserDocument> collection;
    private readonly TimeProvider clock;
    private readonly ILogger<AuthorityUserStore> logger;

    public AuthorityUserStore(
        IMongoCollection<AuthorityUserDocument> collection,
        TimeProvider clock,
        ILogger<AuthorityUserStore> 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<AuthorityUserDocument?> FindBySubjectIdAsync(string subjectId, CancellationToken cancellationToken)
    {
        if (string.IsNullOrWhiteSpace(subjectId))
        {
            return null;
        }

        return await collection
            .Find(u => u.SubjectId == subjectId)
            .FirstOrDefaultAsync(cancellationToken)
            .ConfigureAwait(false);
    }

    public async ValueTask<AuthorityUserDocument?> FindByNormalizedUsernameAsync(string normalizedUsername, CancellationToken cancellationToken)
    {
        if (string.IsNullOrWhiteSpace(normalizedUsername))
        {
            return null;
        }

        var normalised = normalizedUsername.Trim();

        return await collection
            .Find(u => u.NormalizedUsername == normalised)
            .FirstOrDefaultAsync(cancellationToken)
            .ConfigureAwait(false);
    }

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

        document.UpdatedAt = clock.GetUtcNow();

        var filter = Builders<AuthorityUserDocument>.Filter.Eq(u => u.SubjectId, document.SubjectId);
        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 user {SubjectId}.", document.SubjectId);
        }
    }

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

        var normalised = subjectId.Trim();
        var result = await collection.DeleteOneAsync(u => u.SubjectId == normalised, cancellationToken).ConfigureAwait(false);
        return result.DeletedCount > 0;
    }
}
