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

namespace StellaOps.Feedser.Source.Nvd.Internal;

internal sealed record NvdCursor(
    TimeWindowCursorState Window,
    IReadOnlyCollection<Guid> PendingDocuments,
    IReadOnlyCollection<Guid> PendingMappings)
{
    public static NvdCursor Empty { get; } = new(TimeWindowCursorState.Empty, Array.Empty<Guid>(), Array.Empty<Guid>());

    public BsonDocument ToBsonDocument()
    {
        var document = new BsonDocument();
        Window.WriteTo(document);
        document["pendingDocuments"] = new BsonArray(PendingDocuments.Select(id => id.ToString()));
        document["pendingMappings"] = new BsonArray(PendingMappings.Select(id => id.ToString()));
        return document;
    }

    public static NvdCursor FromBsonDocument(BsonDocument? document)
    {
        if (document is null || document.ElementCount == 0)
        {
            return Empty;
        }

        var window = TimeWindowCursorState.FromBsonDocument(document);
        var pendingDocuments = ReadGuidArray(document, "pendingDocuments");
        var pendingMappings = ReadGuidArray(document, "pendingMappings");

        return new NvdCursor(window, pendingDocuments, pendingMappings);
    }

    private static IReadOnlyCollection<Guid> ReadGuidArray(BsonDocument document, string field)
    {
        if (!document.TryGetValue(field, out var value) || value is not BsonArray array)
        {
            return Array.Empty<Guid>();
        }

        var results = new List<Guid>(array.Count);
        foreach (var element in array)
        {
            if (Guid.TryParse(element.AsString, out var guid))
            {
                results.Add(guid);
            }
        }

        return results;
    }

    public NvdCursor WithWindow(TimeWindow window)
        => this with { Window = Window.WithWindow(window) };

    public NvdCursor WithPendingDocuments(IEnumerable<Guid> ids)
        => this with { PendingDocuments = ids?.Distinct().ToArray() ?? Array.Empty<Guid>() };

    public NvdCursor WithPendingMappings(IEnumerable<Guid> ids)
        => this with { PendingMappings = ids?.Distinct().ToArray() ?? Array.Empty<Guid>() };
}
