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

namespace StellaOps.Vexer.Storage.Mongo;

public sealed class MongoVexRawStore : IVexRawStore
{
    private readonly IMongoCollection<VexRawDocumentRecord> _collection;

    public MongoVexRawStore(IMongoDatabase database)
    {
        if (database is null)
        {
            throw new ArgumentNullException(nameof(database));
        }

        VexMongoMappingRegistry.Register();
        _collection = database.GetCollection<VexRawDocumentRecord>(VexMongoCollectionNames.Raw);
        EnsureIndexes(_collection);
    }

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

    public async ValueTask<VexRawDocument?> FindByDigestAsync(string digest, CancellationToken cancellationToken)
    {
        if (string.IsNullOrWhiteSpace(digest))
        {
            throw new ArgumentException("Digest must be provided.", nameof(digest));
        }

        var filter = Builders<VexRawDocumentRecord>.Filter.Eq(x => x.Id, digest.Trim());
        var record = await _collection.Find(filter).FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false);
        return record?.ToDomain();
    }

    private static void EnsureIndexes(IMongoCollection<VexRawDocumentRecord> collection)
    {
        var keys = Builders<VexRawDocumentRecord>.IndexKeys
            .Ascending(x => x.ProviderId)
            .Ascending(x => x.SourceUri);
        var model = new CreateIndexModel<VexRawDocumentRecord>(keys);
        _ = collection.Indexes.CreateOne(model);
    }
}
