using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using MongoDB.Driver;
using StellaOps.Vexer.Core;

namespace StellaOps.Vexer.Storage.Mongo;

public sealed class MongoVexConsensusStore : IVexConsensusStore
{
    private readonly IMongoCollection<VexConsensusRecord> _collection;

    public MongoVexConsensusStore(IMongoDatabase database)
    {
        ArgumentNullException.ThrowIfNull(database);
        VexMongoMappingRegistry.Register();
        _collection = database.GetCollection<VexConsensusRecord>(VexMongoCollectionNames.Consensus);
    }

    public async ValueTask<VexConsensus?> FindAsync(string vulnerabilityId, string productKey, CancellationToken cancellationToken)
    {
        ArgumentException.ThrowIfNullOrWhiteSpace(vulnerabilityId);
        ArgumentException.ThrowIfNullOrWhiteSpace(productKey);
        var id = VexConsensusRecord.CreateId(vulnerabilityId, productKey);
        var filter = Builders<VexConsensusRecord>.Filter.Eq(x => x.Id, id);
        var record = await _collection.Find(filter).FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false);
        return record?.ToDomain();
    }

    public async ValueTask<IReadOnlyCollection<VexConsensus>> FindByVulnerabilityAsync(string vulnerabilityId, CancellationToken cancellationToken)
    {
        ArgumentException.ThrowIfNullOrWhiteSpace(vulnerabilityId);
        var filter = Builders<VexConsensusRecord>.Filter.Eq(x => x.VulnerabilityId, vulnerabilityId.Trim());
        var records = await _collection.Find(filter).ToListAsync(cancellationToken).ConfigureAwait(false);
        return records.ConvertAll(static record => record.ToDomain());
    }

    public async ValueTask SaveAsync(VexConsensus consensus, CancellationToken cancellationToken)
    {
        ArgumentNullException.ThrowIfNull(consensus);
        var record = VexConsensusRecord.FromDomain(consensus);
        var filter = Builders<VexConsensusRecord>.Filter.Eq(x => x.Id, record.Id);
        await _collection.ReplaceOneAsync(filter, record, new ReplaceOptions { IsUpsert = true }, cancellationToken).ConfigureAwait(false);
    }

}
