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

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

/// <summary>
///     An <see cref="IAddressSpaceBuilder"/> that RECORDS the streamed tree instead of creating OPC UA
///     nodes — used to capture an <see cref="ITagDiscovery"/> driver's discovered hierarchy so the
///     runtime can graft it under an equipment node. Folder nesting is tracked (each child builder
///     carries its accumulated path), so every variable records its full <see cref="DiscoveredNode.FolderPathSegments"/>.
///     <para>Value nodes only: <see cref="AddProperty"/> is ignored and alarm marking returns a no-op sink
///     (discovered alarms are out of scope — alarms come via the config path).</para>
///     <para>Single-threaded: a driver's <c>DiscoverAsync</c> streams on one caller; the root and its child
///     builders share one <see cref="List{T}"/>. Not thread-safe by design.</para>
/// </summary>
public sealed class CapturingAddressSpaceBuilder : IAddressSpaceBuilder
{
    private readonly List<DiscoveredNode> _nodes;
    private readonly IReadOnlyList<string> _path;

    /// <summary>Create a root capturing builder with an empty folder path and a fresh node list.</summary>
    public CapturingAddressSpaceBuilder() : this([], []) { }

    private CapturingAddressSpaceBuilder(List<DiscoveredNode> nodes, IReadOnlyList<string> path)
    {
        _nodes = nodes;
        _path = path;
    }

    /// <summary>All variables captured across the whole tree (shared by the root and every child scope).</summary>
    public IReadOnlyList<DiscoveredNode> Nodes => _nodes;

    /// <inheritdoc />
    public IAddressSpaceBuilder Folder(string browseName, string displayName)
        => new CapturingAddressSpaceBuilder(_nodes, [.. _path, browseName]);

    /// <inheritdoc />
    public IVariableHandle Variable(string browseName, string displayName, DriverAttributeInfo attributeInfo)
    {
        _nodes.Add(new DiscoveredNode(
            FolderPathSegments: _path,
            BrowseName: browseName,
            DisplayName: displayName,
            FullReference: attributeInfo.FullName,
            DataType: attributeInfo.DriverDataType,
            IsArray: attributeInfo.IsArray,
            ArrayDim: attributeInfo.ArrayDim,
            Writable: attributeInfo.SecurityClass != SecurityClassification.ViewOnly,
            IsHistorized: attributeInfo.IsHistorized));
        return new NullHandle(attributeInfo.FullName);
    }

    /// <inheritdoc />
    public void AddProperty(string browseName, DriverDataType dataType, object? value) { /* metadata only — ignored */ }

    /// <summary>A variable handle whose alarm marking is a no-op (discovered alarms are out of scope).</summary>
    private sealed class NullHandle(string fullRef) : IVariableHandle
    {
        /// <inheritdoc />
        public string FullReference => fullRef;

        /// <inheritdoc />
        public IAlarmConditionSink MarkAsAlarmCondition(AlarmConditionInfo info) => new NullSink();
    }

    /// <summary>A null sink that ignores alarm condition transitions.</summary>
    private sealed class NullSink : IAlarmConditionSink
    {
        /// <inheritdoc />
        public void OnTransition(AlarmEventArgs args) { }
    }
}
