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

namespace ZB.MOM.WW.OtOpcUa.Driver.Galaxy.E2E;

/// <summary>
///     Test-only <see cref="IAddressSpaceBuilder"/> that records every Folder + Variable
///     registration. Mirrors the v1 in-process address-space build so tests can assert on
///     the same shape the legacy <c>LmxNodeManager</c> produced.
/// </summary>
public sealed class RecordingAddressSpaceBuilder : IAddressSpaceBuilder
{
    public List<RecordedFolder> Folders { get; } = new();
    public List<RecordedVariable> Variables { get; } = new();
    public List<RecordedProperty> Properties { get; } = new();
    public List<RecordedAlarmCondition> AlarmConditions { get; } = new();

    public IAddressSpaceBuilder Folder(string browseName, string displayName)
    {
        Folders.Add(new RecordedFolder(browseName, displayName));
        return this; // single flat builder for tests; nesting irrelevant for parity assertions
    }

    public IVariableHandle Variable(string browseName, string displayName, DriverAttributeInfo attributeInfo)
    {
        Variables.Add(new RecordedVariable(browseName, displayName, attributeInfo));
        return new RecordedVariableHandle(attributeInfo.FullName, AlarmConditions);
    }

    public void AddProperty(string browseName, DriverDataType dataType, object? value)
    {
        Properties.Add(new RecordedProperty(browseName, dataType, value));
    }

    public sealed record RecordedFolder(string BrowseName, string DisplayName);
    public sealed record RecordedVariable(string BrowseName, string DisplayName, DriverAttributeInfo AttributeInfo);
    public sealed record RecordedProperty(string BrowseName, DriverDataType DataType, object? Value);
    public sealed record RecordedAlarmCondition(string SourceNodeId, AlarmConditionInfo Info);
    public sealed record RecordedAlarmTransition(string SourceNodeId, AlarmEventArgs Args);

    /// <summary>
    ///     Sink the tests assert on to verify the alarm event forwarder routed a transition
    ///     to the correct source-node-id. One entry per <see cref="IAlarmSource.OnAlarmEvent"/>.
    /// </summary>
    public List<RecordedAlarmTransition> AlarmTransitions { get; } = new();

    private sealed class RecordedVariableHandle : IVariableHandle
    {
        private readonly List<RecordedAlarmCondition> _conditions;
        public string FullReference { get; }
        public RecordedVariableHandle(string fullReference, List<RecordedAlarmCondition> conditions)
        {
            FullReference = fullReference;
            _conditions = conditions;
        }

        public IAlarmConditionSink MarkAsAlarmCondition(AlarmConditionInfo info)
        {
            _conditions.Add(new RecordedAlarmCondition(FullReference, info));
            return new RecordingSink(FullReference);
        }

        private sealed class RecordingSink : IAlarmConditionSink
        {
            public string SourceNodeId { get; }
            public List<AlarmEventArgs> Received { get; } = new();
            public RecordingSink(string sourceNodeId) => SourceNodeId = sourceNodeId;
            public void OnTransition(AlarmEventArgs args) => Received.Add(args);
        }
    }
}
