using ZB.MOM.WW.OtOpcUa.Core.Abstractions;
using ZB.MOM.WW.OtOpcUa.Core.ScriptedAlarms;

namespace ZB.MOM.WW.OtOpcUa.Server.Phase7;

/// <summary>
///     <see cref="IReadable"/> adapter exposing each scripted alarm's current
///     <see cref="AlarmActiveState"/> as an OPC UA boolean. Phase 7 follow-up (task #245).
/// </summary>
/// <remarks>
///     <para>
///         Paired with the <see cref="NodeSourceKind.ScriptedAlarm"/> dispatch in
///         <c>DriverNodeManager.OnReadValue</c>. Full-reference lookup is the
///         <c>ScriptedAlarmId</c> the walker wrote into <c>DriverAttributeInfo.FullName</c>
///         when emitting the alarm variable node.
///     </para>
///     <para>
///         Unknown alarm ids return <c>BadNodeIdUnknown</c> so misconfiguration surfaces
///         instead of silently reading <c>false</c>. Alarms whose predicate has never
///         been evaluated (brand new, before the engine's first cascade tick) report
///         <see cref="AlarmActiveState.Inactive"/> via <see cref="AlarmConditionState.Fresh"/>,
///         which matches the Part 9 initial-state semantics.
///     </para>
/// </remarks>
public sealed class ScriptedAlarmReadable : IReadable
{
    /// <summary>OPC UA <c>StatusCodes.BadNodeIdUnknown</c> — kept local so we don't pull the OPC stack.</summary>
    private const uint BadNodeIdUnknown = 0x80340000;

    private readonly ScriptedAlarmEngine _engine;

    public ScriptedAlarmReadable(ScriptedAlarmEngine engine)
    {
        _engine = engine ?? throw new ArgumentNullException(nameof(engine));
    }

    public Task<IReadOnlyList<DataValueSnapshot>> ReadAsync(
        IReadOnlyList<string> fullReferences, CancellationToken cancellationToken)
    {
        ArgumentNullException.ThrowIfNull(fullReferences);

        var now = DateTime.UtcNow;
        var results = new DataValueSnapshot[fullReferences.Count];
        for (var i = 0; i < fullReferences.Count; i++)
        {
            var alarmId = fullReferences[i];
            var state = _engine.GetState(alarmId);
            if (state is null)
            {
                results[i] = new DataValueSnapshot(null, BadNodeIdUnknown, null, now);
                continue;
            }
            var active = state.Active == AlarmActiveState.Active;
            results[i] = new DataValueSnapshot(active, 0u, now, now);
        }
        return Task.FromResult<IReadOnlyList<DataValueSnapshot>>(results);
    }
}
