using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using MongoDB.Driver;

namespace StellaOps.Feedser.Storage.Mongo.ChangeHistory;

public sealed class MongoChangeHistoryStore : IChangeHistoryStore
{
    private readonly IMongoCollection<ChangeHistoryDocument> _collection;
    private readonly ILogger<MongoChangeHistoryStore> _logger;

    public MongoChangeHistoryStore(IMongoDatabase database, ILogger<MongoChangeHistoryStore> logger)
    {
        _collection = (database ?? throw new ArgumentNullException(nameof(database)))
            .GetCollection<ChangeHistoryDocument>(MongoStorageDefaults.Collections.ChangeHistory);
        _logger = logger ?? throw new ArgumentNullException(nameof(logger));
    }

    public async Task AddAsync(ChangeHistoryRecord record, CancellationToken cancellationToken)
    {
        ArgumentNullException.ThrowIfNull(record);
        var document = record.ToDocument();
        await _collection.InsertOneAsync(document, cancellationToken: cancellationToken).ConfigureAwait(false);
        _logger.LogDebug("Recorded change history for {Source}/{Advisory} with hash {Hash}", record.SourceName, record.AdvisoryKey, record.CurrentHash);
    }

    public async Task<IReadOnlyList<ChangeHistoryRecord>> GetRecentAsync(string sourceName, string advisoryKey, int limit, CancellationToken cancellationToken)
    {
        ArgumentException.ThrowIfNullOrEmpty(sourceName);
        ArgumentException.ThrowIfNullOrEmpty(advisoryKey);
        if (limit <= 0)
        {
            limit = 10;
        }

        var cursor = await _collection.Find(x => x.SourceName == sourceName && x.AdvisoryKey == advisoryKey)
            .SortByDescending(x => x.CapturedAt)
            .Limit(limit)
            .ToListAsync(cancellationToken)
            .ConfigureAwait(false);

        var records = new List<ChangeHistoryRecord>(cursor.Count);
        foreach (var document in cursor)
        {
            records.Add(document.ToRecord());
        }

        return records;
    }
}
