using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
using Microsoft.Extensions.Time.Testing;
using MongoDB.Bson;
using MongoDB.Driver;
using MongoDB.Driver.GridFS;
using StellaOps.Feedser.Storage.Mongo;
using StellaOps.Feedser.Storage.Mongo.Documents;
using StellaOps.Feedser.Storage.Mongo.Dtos;

namespace StellaOps.Feedser.Storage.Mongo.Tests;

[Collection("mongo-fixture")]
public sealed class RawDocumentRetentionServiceTests : IClassFixture<MongoIntegrationFixture>
{
    private readonly MongoIntegrationFixture _fixture;

    public RawDocumentRetentionServiceTests(MongoIntegrationFixture fixture)
    {
        _fixture = fixture;
    }

    [Fact]
    public async Task SweepExpiredDocumentsAsync_RemovesExpiredRawDocuments()
    {
        var database = _fixture.Database;
        var documents = database.GetCollection<DocumentDocument>(MongoStorageDefaults.Collections.Document);
        var dtos = database.GetCollection<DtoDocument>(MongoStorageDefaults.Collections.Dto);
        var bucket = new GridFSBucket(database, new GridFSBucketOptions { BucketName = "documents" });

        var now = new DateTimeOffset(2024, 10, 1, 12, 0, 0, TimeSpan.Zero);
        var fakeTime = new FakeTimeProvider(now);

        var options = Options.Create(new MongoStorageOptions
        {
            ConnectionString = _fixture.Runner.ConnectionString,
            DatabaseName = database.DatabaseNamespace.DatabaseName,
            RawDocumentRetention = TimeSpan.FromDays(1),
            RawDocumentRetentionTtlGrace = TimeSpan.Zero,
            RawDocumentRetentionSweepInterval = TimeSpan.FromMinutes(5),
        });

        var expiredId = Guid.NewGuid().ToString();
        var gridFsId = await bucket.UploadFromBytesAsync("expired", new byte[] { 1, 2, 3 });
        await documents.InsertOneAsync(new DocumentDocument
        {
            Id = expiredId,
            SourceName = "nvd",
            Uri = "https://example.test/cve",
            FetchedAt = now.AddDays(-2).UtcDateTime,
            Sha256 = "abc",
            Status = "pending",
            ExpiresAt = now.AddMinutes(-5).UtcDateTime,
            GridFsId = gridFsId,
        });

        await dtos.InsertOneAsync(new DtoDocument
        {
            Id = Guid.NewGuid().ToString(),
            DocumentId = expiredId,
            SourceName = "nvd",
            SchemaVersion = "schema",
            Payload = new BsonDocument("value", 1),
            ValidatedAt = now.UtcDateTime,
        });

        var freshId = Guid.NewGuid().ToString();
        await documents.InsertOneAsync(new DocumentDocument
        {
            Id = freshId,
            SourceName = "nvd",
            Uri = "https://example.test/future",
            FetchedAt = now.UtcDateTime,
            Sha256 = "def",
            Status = "pending",
            ExpiresAt = now.AddHours(1).UtcDateTime,
            GridFsId = null,
        });

        var service = new RawDocumentRetentionService(database, options, NullLogger<RawDocumentRetentionService>.Instance, fakeTime);

        var removed = await service.SweepExpiredDocumentsAsync(CancellationToken.None);

        Assert.Equal(1, removed);
        Assert.Equal(0, await documents.CountDocumentsAsync(d => d.Id == expiredId));
        Assert.Equal(0, await dtos.CountDocumentsAsync(d => d.DocumentId == expiredId));
        Assert.Equal(1, await documents.CountDocumentsAsync(d => d.Id == freshId));

        var filter = Builders<GridFSFileInfo>.Filter.Eq("_id", gridFsId);
        using var cursor = await bucket.FindAsync(filter);
        Assert.Empty(await cursor.ToListAsync());
    }
}
