using ZB.MOM.WW.OtOpcUa.Commons.OpcUa;
using ZB.MOM.WW.OtOpcUa.Core.Abstractions;

namespace ZB.MOM.WW.OtOpcUa.Runtime.Drivers;

/// <summary>
///     Derives a full Part 9 <see cref="AlarmConditionSnapshot"/> from each native
///     <see cref="AlarmEventArgs"/> delta, tracking per-condition-NodeId prior state. Owned by the
///     single-threaded <c>DriverHostActor</c> (no locking). Native alarms carry only a transition
///     <see cref="AlarmTransitionKind"/>, not a full state machine, so this is the translation the
///     scripted-alarm engine does internally.
/// </summary>
public sealed class NativeAlarmProjector
{
    private readonly Dictionary<string, (bool Active, bool Acked)> _prior = new(StringComparer.Ordinal);

    /// <summary>Project an alarm transition onto the full condition snapshot for <paramref name="nodeId"/>.</summary>
    /// <param name="nodeId">The materialised condition node's id (the projection's state key).</param>
    /// <param name="e">The native alarm transition.</param>
    /// <returns>The full Part 9 condition snapshot to write to the node.</returns>
    public AlarmConditionSnapshot Project(string nodeId, AlarmEventArgs e)
    {
        var prev = _prior.TryGetValue(nodeId, out var p) ? p : (Active: false, Acked: true);
        var sev = MapSeverity(e.Severity);
        var (active, acked) = e.Kind switch
        {
            AlarmTransitionKind.Raise or AlarmTransitionKind.Retrigger => (true, false),
            AlarmTransitionKind.Acknowledge => (prev.Active, true),
            AlarmTransitionKind.Clear => (false, prev.Acked),
            _ => (prev.Active, prev.Acked),
        };
        _prior[nodeId] = (active, acked);
        return new AlarmConditionSnapshot(
            Active: active, Acknowledged: acked, Confirmed: true, Enabled: true,
            Shelving: AlarmShelvingKind.Unshelved, Severity: sev, Message: e.Message);
    }

    /// <summary>Clears all tracked per-node state (call on address-space rebuild).</summary>
    public void Clear() => _prior.Clear();

    private static ushort MapSeverity(AlarmSeverity s) => s switch
    {
        AlarmSeverity.Low => 200,
        AlarmSeverity.Medium => 500,
        AlarmSeverity.High => 700,
        AlarmSeverity.Critical => 900,
        _ => 500,
    };
}
