using MongoDB.Bson;
using StellaOps.Feedser.Source.Common.Cursors;

namespace StellaOps.Feedser.Source.Common.Tests;

public sealed class TimeWindowCursorPlannerTests
{
    [Fact]
    public void GetNextWindow_UsesInitialBackfillWhenStateEmpty()
    {
        var now = new DateTimeOffset(2024, 10, 1, 12, 0, 0, TimeSpan.Zero);
        var options = new TimeWindowCursorOptions
        {
            WindowSize = TimeSpan.FromHours(4),
            Overlap = TimeSpan.FromMinutes(15),
            InitialBackfill = TimeSpan.FromDays(2),
            MinimumWindowSize = TimeSpan.FromMinutes(1),
        };

        var window = TimeWindowCursorPlanner.GetNextWindow(now, null, options);

        Assert.Equal(now - options.InitialBackfill, window.Start);
        Assert.Equal(window.Start + options.WindowSize, window.End);
    }

    [Fact]
    public void GetNextWindow_ClampsEndToNowWhenWindowExtendPastPresent()
    {
        var now = new DateTimeOffset(2024, 10, 10, 0, 0, 0, TimeSpan.Zero);
        var options = new TimeWindowCursorOptions
        {
            WindowSize = TimeSpan.FromHours(6),
            Overlap = TimeSpan.FromMinutes(30),
            InitialBackfill = TimeSpan.FromDays(3),
            MinimumWindowSize = TimeSpan.FromMinutes(1),
        };

        var previousEnd = now - TimeSpan.FromMinutes(10);
        var state = new TimeWindowCursorState(previousEnd - options.WindowSize, previousEnd);

        var window = TimeWindowCursorPlanner.GetNextWindow(now, state, options);

        var expectedStart = previousEnd - options.Overlap;
        var earliest = now - options.InitialBackfill;
        if (expectedStart < earliest)
        {
            expectedStart = earliest;
        }

        Assert.Equal(expectedStart, window.Start);
        Assert.Equal(now, window.End);
    }

    [Fact]
    public void TimeWindowCursorState_RoundTripThroughBson()
    {
        var state = new TimeWindowCursorState(
            new DateTimeOffset(2024, 9, 1, 0, 0, 0, TimeSpan.Zero),
            new DateTimeOffset(2024, 9, 1, 6, 0, 0, TimeSpan.Zero));

        var document = new BsonDocument
        {
            ["preserve"] = "value",
        };

        state.WriteTo(document);
        var roundTripped = TimeWindowCursorState.FromBsonDocument(document);

        Assert.Equal(state.LastWindowStart, roundTripped.LastWindowStart);
        Assert.Equal(state.LastWindowEnd, roundTripped.LastWindowEnd);
        Assert.Equal("value", document["preserve"].AsString);
    }

    [Fact]
    public void PaginationPlanner_EnumeratesAdditionalPages()
    {
        var indices = PaginationPlanner.EnumerateAdditionalPages(4500, 2000).ToArray();
        Assert.Equal(new[] { 2000, 4000 }, indices);
    }

    [Fact]
    public void PaginationPlanner_ReturnsEmptyWhenSinglePage()
    {
        var indices = PaginationPlanner.EnumerateAdditionalPages(1000, 2000).ToArray();
        Assert.Empty(indices);
    }
}
