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

namespace StellaOps.Authority.Storage.Mongo.Stores;

internal sealed class AuthorityLoginAttemptStore : IAuthorityLoginAttemptStore
{
    private readonly IMongoCollection<AuthorityLoginAttemptDocument> collection;
    private readonly ILogger<AuthorityLoginAttemptStore> logger;

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

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

        await collection.InsertOneAsync(document, cancellationToken: cancellationToken).ConfigureAwait(false);
        logger.LogDebug(
            "Recorded authority audit event {EventType} for subject '{SubjectId}' with outcome {Outcome}.",
            document.EventType,
            document.SubjectId ?? document.Username ?? "<unknown>",
            document.Outcome);
    }

    public async ValueTask<IReadOnlyList<AuthorityLoginAttemptDocument>> ListRecentAsync(string subjectId, int limit, CancellationToken cancellationToken)
    {
        if (string.IsNullOrWhiteSpace(subjectId) || limit <= 0)
        {
            return Array.Empty<AuthorityLoginAttemptDocument>();
        }

        var normalized = subjectId.Trim();

        var cursor = await collection.FindAsync(
            Builders<AuthorityLoginAttemptDocument>.Filter.Eq(a => a.SubjectId, normalized),
            new FindOptions<AuthorityLoginAttemptDocument>
            {
                Sort = Builders<AuthorityLoginAttemptDocument>.Sort.Descending(a => a.OccurredAt),
                Limit = limit
            },
            cancellationToken).ConfigureAwait(false);

        return await cursor.ToListAsync(cancellationToken).ConfigureAwait(false);
    }
}
