package com.zb.mom.ww.mxgateway.client;

import java.util.Objects;
import mxaccess_gateway.v1.MxaccessGateway.MxEvent;
import mxaccess_gateway.v1.MxaccessGateway.ReplayGap;

/**
 * Typed view over a single item drained from an {@link MxEventStream}.
 *
 * <p>A {@code StreamEvents} stream resumed via {@code after_worker_sequence}
 * may begin with a gateway reconnect-replay <em>gap sentinel</em>: a single
 * {@link MxEvent} whose {@code replay_gap} field is set, whose
 * {@code family} is {@code MX_EVENT_FAMILY_UNSPECIFIED}, and whose {@code body}
 * oneof is unset. It means the requested resume cursor predates the oldest
 * event the gateway still retains, so the events in the open interval
 * {@code (requested_after_sequence, oldest_available_sequence)} were evicted and
 * cannot be replayed.
 *
 * <p>This wrapper lets a consumer branch on that sentinel without inspecting
 * the raw event: {@link #isReplayGap()} is {@code true} only for the sentinel,
 * and {@link #replayGap()} exposes the typed {@link ReplayGap} cursors. Normal
 * MXAccess events return {@code false} from {@link #isReplayGap()} and carry
 * their payload on {@link #event()} exactly as before.
 *
 * <p>The gateway never synthesizes an {@code OperationComplete} or any other
 * event from the gap; the sentinel is the gateway's own forwarded signal and is
 * surfaced here untouched. On receiving a gap the consumer must discard any
 * cached per-item state and re-snapshot, then resume without incurring another
 * gap by requesting {@code oldest_available_sequence - 1} as the new
 * {@code after_worker_sequence} cursor.
 *
 * @param event the raw event; for a gap sentinel this is the sentinel event
 *     itself (family unspecified, body unset, {@code replay_gap} set)
 */
public record MxEventStreamItem(MxEvent event) {
    /**
     * Creates a stream-item view over the supplied event.
     *
     * @param event the raw event; must not be {@code null}
     */
    public MxEventStreamItem {
        Objects.requireNonNull(event, "event");
    }

    /**
     * Returns whether this item is the reconnect-replay gap sentinel rather
     * than a normal MXAccess event. Detected via the generated
     * {@code MxEvent.hasReplayGap()} presence flag.
     *
     * @return {@code true} for the gap sentinel, {@code false} for normal events
     */
    public boolean isReplayGap() {
        return event.hasReplayGap();
    }

    /**
     * Returns the typed reconnect-replay gap descriptor when this item is the
     * gap sentinel.
     *
     * @return the {@link ReplayGap} carrying {@code requested_after_sequence}
     *     and {@code oldest_available_sequence}
     * @throws IllegalStateException if this item is a normal event (check
     *     {@link #isReplayGap()} first)
     */
    public ReplayGap replayGap() {
        if (!event.hasReplayGap()) {
            throw new IllegalStateException("stream item is not a replay-gap sentinel");
        }
        return event.getReplayGap();
    }
}
