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

namespace StellaOps.Authority.Storage.Mongo.Stores;

internal sealed class AuthorityTokenStore : IAuthorityTokenStore
{
    private readonly IMongoCollection<AuthorityTokenDocument> collection;
    private readonly ILogger<AuthorityTokenStore> logger;

    public AuthorityTokenStore(
        IMongoCollection<AuthorityTokenDocument> collection,
        ILogger<AuthorityTokenStore> logger)
    {
        this.collection = collection ?? throw new ArgumentNullException(nameof(collection));
        this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
    }

    public async ValueTask InsertAsync(AuthorityTokenDocument document, CancellationToken cancellationToken)
    {
        ArgumentNullException.ThrowIfNull(document);

        await collection.InsertOneAsync(document, cancellationToken: cancellationToken).ConfigureAwait(false);
        logger.LogDebug("Inserted Authority token {TokenId}.", document.TokenId);
    }

    public async ValueTask<AuthorityTokenDocument?> FindByTokenIdAsync(string tokenId, CancellationToken cancellationToken)
    {
        if (string.IsNullOrWhiteSpace(tokenId))
        {
            return null;
        }

        var id = tokenId.Trim();
        return await collection.Find(t => t.TokenId == id)
            .FirstOrDefaultAsync(cancellationToken)
            .ConfigureAwait(false);
    }

    public async ValueTask<AuthorityTokenDocument?> FindByReferenceIdAsync(string referenceId, CancellationToken cancellationToken)
    {
        if (string.IsNullOrWhiteSpace(referenceId))
        {
            return null;
        }

        var id = referenceId.Trim();
        return await collection.Find(t => t.ReferenceId == id)
            .FirstOrDefaultAsync(cancellationToken)
            .ConfigureAwait(false);
    }

    public async ValueTask UpdateStatusAsync(string tokenId, string status, DateTimeOffset? revokedAt, CancellationToken cancellationToken)
    {
        if (string.IsNullOrWhiteSpace(tokenId))
        {
            throw new ArgumentException("Token id cannot be empty.", nameof(tokenId));
        }

        if (string.IsNullOrWhiteSpace(status))
        {
            throw new ArgumentException("Status cannot be empty.", nameof(status));
        }

        var update = Builders<AuthorityTokenDocument>.Update
            .Set(t => t.Status, status)
            .Set(t => t.RevokedAt, revokedAt);

        var result = await collection.UpdateOneAsync(
            Builders<AuthorityTokenDocument>.Filter.Eq(t => t.TokenId, tokenId.Trim()),
            update,
            cancellationToken: cancellationToken).ConfigureAwait(false);

        logger.LogDebug("Updated token {TokenId} status to {Status} (matched {Matched}).", tokenId, status, result.MatchedCount);
    }

    public async ValueTask<long> DeleteExpiredAsync(DateTimeOffset threshold, CancellationToken cancellationToken)
    {
        var filter = Builders<AuthorityTokenDocument>.Filter.And(
            Builders<AuthorityTokenDocument>.Filter.Not(
                Builders<AuthorityTokenDocument>.Filter.Eq(t => t.Status, "revoked")),
            Builders<AuthorityTokenDocument>.Filter.Lt(t => t.ExpiresAt, threshold));

        var result = await collection.DeleteManyAsync(filter, cancellationToken).ConfigureAwait(false);
        if (result.DeletedCount > 0)
        {
            logger.LogInformation("Deleted {Count} expired Authority tokens.", result.DeletedCount);
        }

        return result.DeletedCount;
    }
}
