using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging.Abstractions;
using Mongo2Go;
using MongoDB.Driver;
using StellaOps.Vexer.Storage.Mongo.Migrations;

namespace StellaOps.Vexer.Storage.Mongo.Tests;

public sealed class VexMongoMigrationRunnerTests : IAsyncLifetime
{
    private readonly MongoDbRunner _runner;
    private readonly IMongoDatabase _database;

    public VexMongoMigrationRunnerTests()
    {
        _runner = MongoDbRunner.Start();
        var client = new MongoClient(_runner.ConnectionString);
        _database = client.GetDatabase("vexer-migrations-tests");
    }

    [Fact]
    public async Task RunAsync_AppliesInitialIndexesOnce()
    {
        var migration = new VexInitialIndexMigration();
        var runner = new VexMongoMigrationRunner(_database, new[] { migration }, NullLogger<VexMongoMigrationRunner>.Instance);

        await runner.RunAsync(CancellationToken.None);
        await runner.RunAsync(CancellationToken.None);

        var appliedCollection = _database.GetCollection<VexMigrationRecord>(VexMongoCollectionNames.Migrations);
        var applied = await appliedCollection.Find(FilterDefinition<VexMigrationRecord>.Empty).ToListAsync();
        Assert.Single(applied);
        Assert.Equal(migration.Id, applied[0].Id);

        Assert.True(HasIndex(_database.GetCollection<VexRawDocumentRecord>(VexMongoCollectionNames.Raw), "ProviderId_1_Format_1_RetrievedAt_1"));
        Assert.True(HasIndex(_database.GetCollection<VexProviderRecord>(VexMongoCollectionNames.Providers), "Kind_1"));
        Assert.True(HasIndex(_database.GetCollection<VexConsensusRecord>(VexMongoCollectionNames.Consensus), "VulnerabilityId_1_Product.Key_1"));
        Assert.True(HasIndex(_database.GetCollection<VexConsensusRecord>(VexMongoCollectionNames.Consensus), "PolicyRevisionId_1_PolicyDigest_1"));
        Assert.True(HasIndex(_database.GetCollection<VexExportManifestRecord>(VexMongoCollectionNames.Exports), "QuerySignature_1_Format_1"));
        Assert.True(HasIndex(_database.GetCollection<VexCacheEntryRecord>(VexMongoCollectionNames.Cache), "QuerySignature_1_Format_1"));
        Assert.True(HasIndex(_database.GetCollection<VexCacheEntryRecord>(VexMongoCollectionNames.Cache), "ExpiresAt_1"));
    }

    private static bool HasIndex<TDocument>(IMongoCollection<TDocument> collection, string name)
    {
        var indexes = collection.Indexes.List().ToList();
        return indexes.Any(index => index["name"].AsString == name);
    }

    public Task InitializeAsync() => Task.CompletedTask;

    public Task DisposeAsync()
    {
        _runner.Dispose();
        return Task.CompletedTask;
    }
}
