using MongoDB.Bson;

namespace StellaOps.Feedser.Source.Common.Cursors;

/// <summary>
/// Represents the persisted state of a sliding time-window cursor.
/// </summary>
public sealed record TimeWindowCursorState(DateTimeOffset? LastWindowStart, DateTimeOffset? LastWindowEnd)
{
    public static TimeWindowCursorState Empty { get; } = new(null, null);

    public TimeWindowCursorState WithWindow(TimeWindow window)
    {
        return new TimeWindowCursorState(window.Start, window.End);
    }

    public BsonDocument ToBsonDocument(string startField = "windowStart", string endField = "windowEnd")
    {
        var document = new BsonDocument();
        WriteTo(document, startField, endField);
        return document;
    }

    public void WriteTo(BsonDocument document, string startField = "windowStart", string endField = "windowEnd")
    {
        ArgumentNullException.ThrowIfNull(document);
        ArgumentException.ThrowIfNullOrEmpty(startField);
        ArgumentException.ThrowIfNullOrEmpty(endField);

        document.Remove(startField);
        document.Remove(endField);

        if (LastWindowStart.HasValue)
        {
            document[startField] = LastWindowStart.Value.UtcDateTime;
        }

        if (LastWindowEnd.HasValue)
        {
            document[endField] = LastWindowEnd.Value.UtcDateTime;
        }
    }

    public static TimeWindowCursorState FromBsonDocument(BsonDocument? document, string startField = "windowStart", string endField = "windowEnd")
    {
        if (document is null)
        {
            return Empty;
        }

        DateTimeOffset? start = null;
        DateTimeOffset? end = null;

        if (document.TryGetValue(startField, out var startValue))
        {
            start = ReadDateTimeOffset(startValue);
        }

        if (document.TryGetValue(endField, out var endValue))
        {
            end = ReadDateTimeOffset(endValue);
        }

        return new TimeWindowCursorState(start, end);
    }

    private static DateTimeOffset? ReadDateTimeOffset(BsonValue value)
    {
        return value.BsonType switch
        {
            BsonType.DateTime => DateTime.SpecifyKind(value.ToUniversalTime(), DateTimeKind.Utc),
            BsonType.String when DateTimeOffset.TryParse(value.AsString, out var parsed) => parsed.ToUniversalTime(),
            _ => null,
        };
    }
}

/// <summary>
/// Simple value object describing a time window.
/// </summary>
public readonly record struct TimeWindow(DateTimeOffset Start, DateTimeOffset End)
{
    public TimeSpan Duration => End - Start;
}
