using ZB.MOM.WW.MxGateway.Contracts.Proto;

namespace ZB.MOM.WW.MxGateway.Server.Alarms;

/// <summary>Lifecycle state of the gateway's central alarm monitor.</summary>
public enum GatewayAlarmMonitorState
{
    /// <summary>Alarm monitoring is switched off (<c>MxGateway:Alarms:Enabled</c> is false).</summary>
    Disabled,

    /// <summary>The monitor is opening or re-opening its worker session.</summary>
    Starting,

    /// <summary>The monitor is connected and tracking the active-alarm set.</summary>
    Monitoring,

    /// <summary>The monitor's last lifecycle attempt failed; a restart is pending.</summary>
    Faulted,
}

/// <summary>
///     The gateway's always-on alarm broker. A single gateway-owned worker
///     session monitors the AVEVA alarm provider; this service caches the
///     current active-alarm set and fans it out to any number of clients —
///     no client needs to open its own worker session to see alarms.
/// </summary>
public interface IGatewayAlarmService
{
    /// <summary>Current monitor lifecycle state.</summary>
    GatewayAlarmMonitorState State { get; }

    /// <summary>Diagnostic message from the most recent fault, or null.</summary>
    string? LastError { get; }

    /// <summary>Process id of the worker backing the monitor, when one is attached.</summary>
    int? WorkerProcessId { get; }

    /// <summary>A point-in-time copy of the current active-alarm set.</summary>
    IReadOnlyList<ActiveAlarmSnapshot> CurrentAlarms { get; }

    /// <summary>
    ///     True when the worker's most recent reconcile fetch hit the provider's
    ///     per-fetch cap, so the active-alarm set may be missing alarms. The
    ///     monitor is otherwise healthy — this is not a fault, it is a
    ///     completeness caveat, which is why it is separate from
    ///     <see cref="State"/> and <see cref="LastError"/>. Cleared by the first
    ///     reconcile whose fetch comes back under the cap.
    ///     <para>
    ///     Read it as "as of the last full reconcile, the fetch was capped", not as
    ///     a property of a particular <see cref="CurrentAlarms"/> array: the two are
    ///     separate reads, and live transitions keep moving the cached set between
    ///     reconciles. A consumer that reads both — the dashboard poll does — can
    ///     therefore straddle a reconcile, in which case its caveat describes the
    ///     adjacent generation and the banner is at worst one poll stale. That is
    ///     the intended granularity for a completeness hint; pairing them exactly
    ///     would need a combined accessor this seam deliberately does not have.
    ///     </para>
    ///     <para>
    ///     This is the polled read of the verdict. The same verdict is pushed to
    ///     the live feed as the <c>snapshot_status</c>
    ///     (<c>AlarmSnapshotStatus</c>) case of <see cref="AlarmFeedMessage"/> —
    ///     once at <see cref="StreamAsync"/> open and again on every change — so
    ///     a streaming consumer need not poll this property.
    ///     </para>
    /// </summary>
    bool SnapshotTruncated { get; }

    /// <summary>
    ///     Attaches to the central alarm feed. The returned stream opens with a
    ///     <c>provider_status</c> and a <c>snapshot_status</c>
    ///     <see cref="AlarmFeedMessage"/> — the current provider mode and
    ///     completeness verdict, so the caveats precede the records they
    ///     qualify — then one message per currently-active alarm, then a single
    ///     <c>snapshot_complete</c> sentinel, then a <c>transition</c> for every
    ///     subsequent change (and a further status message on every provider-mode
    ///     or truncation-verdict change).
    /// </summary>
    /// <param name="alarmFilterPrefix">Optional alarm-reference prefix scoping the feed.</param>
    /// <param name="cancellationToken">Token that ends the subscription.</param>
    /// <returns>An asynchronous stream of alarm feed messages.</returns>
    IAsyncEnumerable<AlarmFeedMessage> StreamAsync(
        string? alarmFilterPrefix,
        CancellationToken cancellationToken);

    /// <summary>
    ///     Acknowledges an alarm through the monitor's worker session. Never
    ///     throws — transport and monitor-state failures surface in the
    ///     reply's <see cref="AcknowledgeAlarmReply.ProtocolStatus"/>.
    /// </summary>
    /// <param name="request">The acknowledge request.</param>
    /// <param name="cancellationToken">Token to cancel the call.</param>
    /// <returns>The acknowledge reply, carrying the outcome in its protocol status.</returns>
    Task<AcknowledgeAlarmReply> AcknowledgeAsync(
        AcknowledgeAlarmRequest request,
        CancellationToken cancellationToken);
}
