using Microsoft.Extensions.Logging.Abstractions;
using StellaOps.Vexer.Core;
using StellaOps.Vexer.Export;
using StellaOps.Vexer.Storage.Mongo;

namespace StellaOps.Vexer.Export.Tests;

public sealed class VexExportCacheServiceTests
{
    [Fact]
    public async Task InvalidateAsync_RemovesEntry()
    {
        var cacheIndex = new RecordingIndex();
        var maintenance = new StubMaintenance();
        var service = new VexExportCacheService(cacheIndex, maintenance, NullLogger<VexExportCacheService>.Instance);

        var signature = new VexQuerySignature("format=json|provider=vendor");
        await service.InvalidateAsync(signature, VexExportFormat.Json, CancellationToken.None);

        Assert.Equal(signature.Value, cacheIndex.LastSignature?.Value);
        Assert.Equal(VexExportFormat.Json, cacheIndex.LastFormat);
        Assert.Equal(1, cacheIndex.RemoveCalls);
    }

    [Fact]
    public async Task PruneExpiredAsync_ReturnsCount()
    {
        var cacheIndex = new RecordingIndex();
        var maintenance = new StubMaintenance { ExpiredCount = 3 };
        var service = new VexExportCacheService(cacheIndex, maintenance, NullLogger<VexExportCacheService>.Instance);

        var removed = await service.PruneExpiredAsync(DateTimeOffset.UtcNow, CancellationToken.None);

        Assert.Equal(3, removed);
    }

    [Fact]
    public async Task PruneDanglingAsync_ReturnsCount()
    {
        var cacheIndex = new RecordingIndex();
        var maintenance = new StubMaintenance { DanglingCount = 2 };
        var service = new VexExportCacheService(cacheIndex, maintenance, NullLogger<VexExportCacheService>.Instance);

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

        Assert.Equal(2, removed);
    }

    private sealed class RecordingIndex : IVexCacheIndex
    {
        public VexQuerySignature? LastSignature { get; private set; }
        public VexExportFormat LastFormat { get; private set; }
        public int RemoveCalls { get; private set; }

        public ValueTask<VexCacheEntry?> FindAsync(VexQuerySignature signature, VexExportFormat format, CancellationToken cancellationToken)
            => ValueTask.FromResult<VexCacheEntry?>(null);

        public ValueTask SaveAsync(VexCacheEntry entry, CancellationToken cancellationToken)
            => ValueTask.CompletedTask;

        public ValueTask RemoveAsync(VexQuerySignature signature, VexExportFormat format, CancellationToken cancellationToken)
        {
            LastSignature = signature;
            LastFormat = format;
            RemoveCalls++;
            return ValueTask.CompletedTask;
        }
    }

    private sealed class StubMaintenance : IVexCacheMaintenance
    {
        public int ExpiredCount { get; set; }
        public int DanglingCount { get; set; }

        public ValueTask<int> RemoveExpiredAsync(DateTimeOffset asOf, CancellationToken cancellationToken)
            => ValueTask.FromResult(ExpiredCount);

        public ValueTask<int> RemoveMissingManifestReferencesAsync(CancellationToken cancellationToken)
            => ValueTask.FromResult(DanglingCount);
    }
}
