using System;
using System.Collections.Generic;

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

/// <summary>
///     Abstraction over an AVEVA alarm-consumer COM library. The production
///     implementation (<see cref="WnWrapAlarmConsumer"/>) wraps
///     <c>WNWRAPCONSUMERLib.wwAlarmConsumerClass</c> from
///     <c>C:\Program Files (x86)\Common Files\ArchestrA\wnwrapConsumer.dll</c>;
///     tests substitute a fake to drive transition events without a live
///     Galaxy.
/// </summary>
/// <remarks>
///     <para>
///         The receive surface is poll-based: the production consumer
///         periodically calls <c>GetXmlCurrentAlarms2</c>, parses the
///         returned XML payload, diffs against the previous snapshot keyed
///         by alarm GUID, and raises <see cref="AlarmTransitionEmitted"/>
///         once per state change. This bypasses the FILETIME marshaling
///         crash in <c>aaAlarmManagedClient.AlarmClient.GetHighPriAlarm</c>
///         (see <c>docs/AlarmClientDiscovery.md</c>) — XML strings carry
///         timestamps as ASCII fields, no DateTime auto-conversion happens
///         on the .NET interop boundary.
///     </para>
/// </remarks>
public interface IMxAccessAlarmConsumer : IDisposable
{
    /// <summary>
    ///     Fires once per detected alarm-state transition (raise, acknowledge,
    ///     clear, or new-alarm-already-acked-on-arrival). Subscribers are
    ///     expected to translate the record into the proto family
    ///     <c>OnAlarmTransition</c> and enqueue it. Fired on the consumer's
    ///     polling thread (the worker's STA in production); subscribers that
    ///     need a different thread must marshal back themselves.
    /// </summary>
    event EventHandler<MxAlarmTransitionEvent>? AlarmTransitionEmitted;

    /// <summary>
    ///     Initializes the AVEVA alarm-client connection, registers as a
    ///     consumer, and subscribes to the supplied alarm-provider expression.
    ///     Subscription string follows AVEVA's canonical format:
    ///     <c>\\&lt;node&gt;\Galaxy!&lt;area&gt;</c>. The literal "Galaxy" is
    ///     the provider name (regardless of the configured Galaxy database
    ///     name). Subscribe does not start any polling of its own; the caller
    ///     drives polls explicitly via <see cref="PollOnce"/>.
    /// </summary>
    /// <param name="subscription">The subscription expression (e.g., <c>\\HOST\Galaxy!Area</c>).</param>
    void Subscribe(string subscription);

    /// <summary>
    ///     Acknowledges a single alarm with full operator-identity fidelity.
    ///     Reaches AVEVA's native <c>AlarmAckByGUID</c>; operator
    ///     user / node / domain / full-name and the comment land atomically
    ///     with the ack transition in the alarm-history log.
    /// </summary>
    /// <param name="alarmGuid">The alarm GUID.</param>
    /// <param name="ackComment">The acknowledgment comment.</param>
    /// <param name="ackOperatorName">The operator name.</param>
    /// <param name="ackOperatorNode">The operator node.</param>
    /// <param name="ackOperatorDomain">The operator domain.</param>
    /// <param name="ackOperatorFullName">The operator full name.</param>
    /// <returns>The AVEVA-native status code.</returns>
    int AcknowledgeByGuid(
        Guid alarmGuid,
        string ackComment,
        string ackOperatorName,
        string ackOperatorNode,
        string ackOperatorDomain,
        string ackOperatorFullName);

    /// <summary>
    ///     Acknowledge a single alarm by its (name, provider, group) tuple.
    ///     Reaches AVEVA's <c>AlarmAckByName</c> on
    ///     <c>wwAlarmConsumerClass</c>; same alarm-history outcome as
    ///     <see cref="AcknowledgeByGuid"/>, used when the caller has the
    ///     human-readable reference but not the canonical GUID.
    /// </summary>
    /// <param name="alarmName">The alarm name.</param>
    /// <param name="providerName">The provider name.</param>
    /// <param name="groupName">The group name.</param>
    /// <param name="ackComment">The acknowledgment comment.</param>
    /// <param name="ackOperatorName">The operator name.</param>
    /// <param name="ackOperatorNode">The operator node.</param>
    /// <param name="ackOperatorDomain">The operator domain.</param>
    /// <param name="ackOperatorFullName">The operator full name.</param>
    /// <returns>The AVEVA-native status code.</returns>
    int AcknowledgeByName(
        string alarmName,
        string providerName,
        string groupName,
        string ackComment,
        string ackOperatorName,
        string ackOperatorNode,
        string ackOperatorDomain,
        string ackOperatorFullName);

    /// <summary>
    ///     Returns the consumer's most recently parsed snapshot of currently
    ///     active alarms, together with whether the fetch that produced it hit
    ///     the per-fetch cap. Used by the gateway's QueryActiveAlarms (PR A.7)
    ///     ConditionRefresh path — operator clients call this after reconnect
    ///     to seed local Part 9 state.
    /// </summary>
    /// <remarks>
    ///     <para>
    ///         The verdict is an <c>out</c> parameter rather than a separate
    ///         property on purpose, and the reason is a correctness one.
    ///         Implementations must produce both values from a single acquisition
    ///         of whatever lock guards the retained snapshot, mirroring the write
    ///         side (<c>WnWrapAlarmConsumer.FoldFetch</c> updates snapshot and
    ///         verdict together). Two separate reads could straddle a poll that
    ///         flips not-truncated → truncated and pair a stale
    ///         <see langword="false"/> with a capped snapshot — a snapshot that
    ///         reads as complete while missing actives, which is exactly the
    ///         false all-clear this signal exists to prevent. Making the pair
    ///         inseparable in the signature removes the possibility rather than
    ///         relying on callers, or on the STA serializing them.
    ///     </para>
    ///     <para>
    ///         While <paramref name="truncated"/> is <see langword="true"/> the
    ///         returned snapshot is authoritative about presence only: the
    ///         provider may hold actives it had no room to report, and the
    ///         consumer has suspended the absence-implies-Clear inference. Not
    ///         latched — the first sub-cap fetch clears it, because that fetch is
    ///         complete and its snapshot is again authoritative about absence.
    ///         Consumers with no per-fetch cap (the subtag fallback, which is
    ///         advise-driven) always report <see langword="false"/>.
    ///     </para>
    /// </remarks>
    /// <param name="truncated">
    ///     Receives whether the fetch behind the returned snapshot hit the
    ///     per-fetch cap.
    /// </param>
    /// <returns>The most recently parsed snapshot of currently active alarms.</returns>
    IReadOnlyList<MxAlarmSnapshotRecord> SnapshotActiveAlarms(out bool truncated);

    /// <summary>
    ///     Drives a single synchronous poll of the underlying alarm source.
    ///     The production consumer owns no internal timer; the worker's STA
    ///     drives polls via <c>StaRuntime.InvokeAsync</c>, satisfying the
    ///     <c>ThreadingModel=Apartment</c> requirement of
    ///     <c>wwAlarmConsumerClass</c>. Fake implementations should no-op.
    ///     This method must be invoked on the thread that created the consumer
    ///     (the worker's STA in production).
    /// </summary>
    void PollOnce();
}
