using ZB.MOM.WW.ScadaBridge.Commons.Types.Alarms;
using ZB.MOM.WW.ScadaBridge.Commons.Types.Enums;

namespace ZB.MOM.WW.ScadaBridge.Commons.Messages.Streaming;

public record AlarmStateChanged(
    string InstanceUniqueName,
    string AlarmName,
    AlarmState State,
    int Priority,
    DateTimeOffset Timestamp) : ISiteStreamEvent
{
    /// <summary>
    /// Severity level when <see cref="State"/> is <see cref="AlarmState.Active"/>.
    /// Always <see cref="AlarmLevel.None"/> for binary trigger types
    /// (ValueMatch, RangeViolation, RateOfChange); set by the HiLo trigger
    /// type to one of Low/LowLow/High/HighHigh based on the crossed setpoint.
    /// Added as an init-property so existing positional constructors still
    /// work — message contract evolves additively.
    /// </summary>
    public AlarmLevel Level { get; init; } = AlarmLevel.None;

    /// <summary>
    /// Optional per-band operator message (e.g., "Coolant critically low —
    /// shut down"). Set by HiLo triggers when the per-setpoint message is
    /// configured; otherwise empty. Notification routing and UI tooltips may
    /// surface this to operators.
    /// </summary>
    public string Message { get; init; } = string.Empty;

    /// <summary>
    /// Whether this alarm is computed at the site or mirrored from a native
    /// source. Defaults to <see cref="AlarmKind.Computed"/>.
    /// </summary>
    public AlarmKind Kind { get; init; } = AlarmKind.Computed;

    private AlarmConditionState? _condition;

    /// <summary>
    /// Unified A&amp;C-style condition (active/acked/shelved/suppressed + severity).
    /// When not explicitly set, defaults to a computed mapping of
    /// <see cref="State"/> + <see cref="Priority"/> so existing callers and
    /// computed alarms carry a correct condition without extra work.
    /// </summary>
    public AlarmConditionState Condition
    {
        get => _condition ?? AlarmConditionStateFactory.ForComputed(State, Priority);
        init => _condition = value;
    }

    /// <summary>Native per-condition key (e.g. "Tank01.Level.HiHi"); empty for computed alarms.</summary>
    public string SourceReference { get; init; } = string.Empty;

    /// <summary>Native alarm type name (e.g. "AnalogLimitAlarm.HiHi"); empty for computed alarms.</summary>
    public string AlarmTypeName { get; init; } = string.Empty;

    /// <summary>Native alarm category/taxonomy; empty for computed alarms.</summary>
    public string Category { get; init; } = string.Empty;

    /// <summary>Operator who acknowledged at the source (display-only); empty otherwise.</summary>
    public string OperatorUser { get; init; } = string.Empty;

    /// <summary>Operator comment captured at the source (display-only); empty otherwise.</summary>
    public string OperatorComment { get; init; } = string.Empty;

    /// <summary>When the native condition originally became active, if known.</summary>
    public DateTimeOffset? OriginalRaiseTime { get; init; }

    /// <summary>
    /// When the condition was acknowledged, or <c>null</c> while it is unacknowledged.
    /// Additive native-mirror enrichment (MES alarm-status API §6.4) — the ack timestamp
    /// the MES <c>AlarmInfo.AckDT</c> field reports and the <c>Alarms.CurrentAsync()</c>
    /// script accessor surfaces as <c>ScriptAlarm.AckTime</c>.
    ///
    /// <para>
    /// Provenance: the DCL stamps the SOURCE's own ack instant where the protocol supplies
    /// one (OPC UA A&amp;C exposes <c>AckedState/TransitionTime</c>); where it does not
    /// (MxAccess Gateway alarm events), the DCL stamps the transition time it OBSERVED the
    /// ack at. It is therefore never fabricated, but for MxGateway sources it is accurate
    /// to "when the system saw the ack", not necessarily to the operator's click.
    /// </para>
    ///
    /// <para>
    /// Null for computed alarms (they are auto-acked and have no operator ack event) and
    /// null while a native condition is unacknowledged; cleared on re-raise, because a
    /// re-raise transition carries <c>Acknowledged = false</c>.
    /// </para>
    /// </summary>
    public DateTimeOffset? AckTime { get; init; }

    /// <summary>Current source value (display-only); empty for computed alarms.</summary>
    public string CurrentValue { get; init; } = string.Empty;

    /// <summary>Limit/threshold value for native limit alarms (display-only); empty otherwise.</summary>
    public string LimitValue { get; init; } = string.Empty;

    /// <summary>
    /// Canonical name of the native alarm SOURCE BINDING this condition belongs to
    /// (e.g. "Motor1.MotorAlarms"). Lets the Debug View nest live native conditions
    /// under their configured binding node. Empty for computed alarms. Additive.
    /// </summary>
    public string NativeSourceCanonicalName { get; init; } = string.Empty;

    /// <summary>
    /// True when this row is a placeholder emitted for a CONFIGURED native source
    /// binding that currently has no active conditions, so the Debug View tree can
    /// show the binding node as "no active conditions". Additive; default false.
    /// </summary>
    public bool IsConfiguredPlaceholder { get; init; }
}
