using System.Collections.Generic;
using ZB.MOM.WW.MxGateway.Contracts.Proto;

namespace ZB.MOM.WW.MxGateway.Worker.MxAccess;

/// <summary>
/// Outcome of a byte-budgeted drain from the MXAccess outbound event queue.
/// </summary>
/// <remarks>
///     A count cap alone cannot keep a <c>DrainEvents</c> reply inside the negotiated frame
///     maximum: byte-heavy events (large string or array <c>MxValue</c>s) overshoot the frame max
///     long before the count ceiling is reached, and the writer's per-frame rejection then
///     destroys events that were already removed from the queue. The byte-budgeted drain sizes
///     the reply while draining, so an event that does not fit is never dequeued (WRK-21), and
///     this result carries the truncation facts the reply's <c>DiagnosticMessage</c> reports —
///     no contract change is needed to express them.
///     Plain constructor and get-only properties: the worker targets .NET Framework 4.8, which
///     has no init-only members or positional records.
/// </remarks>
public sealed class WorkerEventDrainResult
{
    /// <summary>Initializes a new instance of the <see cref="WorkerEventDrainResult"/> class.</summary>
    /// <param name="events">Events removed from the queue, in enqueue order.</param>
    /// <param name="truncatedBySize">Whether the byte budget, not the count cap, ended the drain.</param>
    /// <param name="remainingCount">Number of events still queued after the drain.</param>
    /// <param name="oversizedHeadSequence">
    ///     Worker sequence of a head event whose own serialized size exceeds the whole budget, so
    ///     no future call of the same budget can ship it; 0 when there is no such event.
    /// </param>
    public WorkerEventDrainResult(
        IReadOnlyList<WorkerEvent> events,
        bool truncatedBySize,
        int remainingCount,
        ulong oversizedHeadSequence)
    {
        Events = events;
        TruncatedBySize = truncatedBySize;
        RemainingCount = remainingCount;
        OversizedHeadSequence = oversizedHeadSequence;
    }

    /// <summary>Gets the events removed from the queue, in enqueue order.</summary>
    public IReadOnlyList<WorkerEvent> Events { get; }

    /// <summary>Gets a value indicating whether the byte budget ended the drain early.</summary>
    public bool TruncatedBySize { get; }

    /// <summary>Gets the number of events still queued after the drain.</summary>
    public int RemainingCount { get; }

    /// <summary>
    /// Gets the worker sequence of the head event that alone exceeds the byte budget, or 0 when
    /// no single event blocks the drain. Naming it lets an operator find the offending tag.
    /// </summary>
    public ulong OversizedHeadSequence { get; }
}
