using ZB.MOM.WW.MxGateway.Contracts.Proto;
namespace ZB.MOM.WW.MxGateway.Client;
///
/// One item yielded by the typed event stream. It is either a normal MXAccess
/// or a reconnect-replay gap sentinel.
///
///
///
/// The gateway emits a single sentinel at the head of a
/// StreamEvents stream that was resumed via
/// StreamEventsRequest.after_worker_sequence when the requested sequence
/// predates the oldest event still retained in the session replay ring — i.e.
/// events were evicted and cannot be replayed. On that sentinel the
/// MxEvent.replay_gap field is set, is
/// , the body oneof is unset, and
/// no per-item fields are populated.
///
///
/// This wrapper makes that sentinel observable instead of forcing the consumer
/// to inspect the raw . It never synthesizes or swallows an
/// event: the gap is exactly the gateway's own sentinel, exposed with
/// set and populated. Every
/// other event flows through unchanged with false.
///
///
/// When is the consumer has
/// missed events and MUST discard local state and re-snapshot. To resume without
/// incurring another gap, reconnect with
/// after_worker_sequence = ReplayGap.OldestAvailableSequence - 1.
///
///
public sealed class MxEventStreamItem
{
private MxEventStreamItem(MxEvent gatewayEvent, ReplayGap? replayGap)
{
Event = gatewayEvent;
ReplayGap = replayGap;
}
///
/// The underlying raw . For a normal event this is the
/// MXAccess event itself; for a replay-gap item this is the gateway's
/// sentinel event whose only meaningful payload is .
/// Never .
///
public MxEvent Event { get; }
///
/// The reconnect-replay gap payload when this item is a gap sentinel;
/// otherwise . Read
/// and
/// to learn
/// which events were lost.
///
public ReplayGap? ReplayGap { get; }
///
/// when this item is a reconnect-replay gap sentinel:
/// the consumer missed events and must discard local state and re-snapshot.
/// Resume without another gap by reconnecting with
/// after_worker_sequence = ReplayGap.OldestAvailableSequence - 1.
///
public bool IsReplayGap => ReplayGap is not null;
///
/// Wraps a raw stream as a typed item, classifying it
/// as a replay-gap sentinel when MxEvent.replay_gap is present.
///
/// The raw event from the StreamEvents stream.
/// A typed item exposing either the normal event or the replay gap.
internal static MxEventStreamItem From(MxEvent gatewayEvent)
{
ArgumentNullException.ThrowIfNull(gatewayEvent);
// For a message-typed proto3 field, presence is a non-null reference.
return gatewayEvent.ReplayGap is { } replayGap
? new MxEventStreamItem(gatewayEvent, replayGap)
: new MxEventStreamItem(gatewayEvent, replayGap: null);
}
}