using Microsoft.Extensions.Logging.Abstractions;
using MongoDB.Bson;
using StellaOps.Feedser.Storage.Mongo;

namespace StellaOps.Feedser.Storage.Mongo.Tests;

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

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

    [Fact]
    public async Task UpsertAndUpdateCursorFlow()
    {
        var repository = new MongoSourceStateRepository(_fixture.Database, NullLogger<MongoSourceStateRepository>.Instance);
        var sourceName = "nvd";

        var record = new SourceStateRecord(
            SourceName: sourceName,
            Enabled: true,
            Paused: false,
            Cursor: new BsonDocument("page", 1),
            LastSuccess: null,
            LastFailure: null,
            FailCount: 0,
            BackoffUntil: null,
            UpdatedAt: DateTimeOffset.UtcNow,
            LastFailureReason: null);

        var upserted = await repository.UpsertAsync(record, CancellationToken.None);
        Assert.True(upserted.Enabled);

        var cursor = new BsonDocument("page", 2);
        var updated = await repository.UpdateCursorAsync(sourceName, cursor, DateTimeOffset.UtcNow, CancellationToken.None);
        Assert.NotNull(updated);
        Assert.Equal(0, updated!.FailCount);
        Assert.Equal(2, updated.Cursor["page"].AsInt32);

        var failure = await repository.MarkFailureAsync(sourceName, DateTimeOffset.UtcNow, TimeSpan.FromMinutes(5), "network timeout", CancellationToken.None);
        Assert.NotNull(failure);
        Assert.Equal(1, failure!.FailCount);
        Assert.NotNull(failure.BackoffUntil);
        Assert.Equal("network timeout", failure.LastFailureReason);

        var fetched = await repository.TryGetAsync(sourceName, CancellationToken.None);
        Assert.NotNull(fetched);
        Assert.Equal(failure.BackoffUntil, fetched!.BackoffUntil);
        Assert.Equal("network timeout", fetched.LastFailureReason);
    }
}
