namespace ZB.MOM.WW.OtOpcUa.Commons.OpcUa;

/// <summary>
///     Wrapper <see cref="IOpcUaAddressSpaceSink"/> that defers to an inner sink swapped in at
///     runtime. Needed because the production sink (<c>SdkAddressSpaceSink</c>) wraps an
///     <c>OtOpcUaNodeManager</c> that only exists after the SDK <c>StandardServer</c> has
///     started — but Akka actors resolve their sink dependency at construction time, before
///     the hosted service has booted the SDK.
///
///     Bound as a singleton in DI on driver-role hosts; the OPC UA hosted service calls
///     <see cref="SetSink"/> once the server is up. Until that swap happens, every call is a
///     no-op against <see cref="NullOpcUaAddressSpaceSink"/>, so the actor stays safe to
///     receive messages from the moment it boots.
/// </summary>
public sealed class DeferredAddressSpaceSink : IOpcUaAddressSpaceSink
{
    private volatile IOpcUaAddressSpaceSink _inner = NullOpcUaAddressSpaceSink.Instance;

    /// <summary>Swap in the production sink. Pass <c>null</c> to revert to the null sink
    /// (used during graceful shutdown so post-stop writes don't hit a half-disposed manager).</summary>
    /// <param name="sink">The sink implementation to use, or null to use the null sink.</param>
    public void SetSink(IOpcUaAddressSpaceSink? sink) =>
        _inner = sink ?? NullOpcUaAddressSpaceSink.Instance;

    /// <summary>Writes a value to the OPC UA address space through the inner sink.</summary>
    /// <param name="nodeId">The node ID of the variable.</param>
    /// <param name="value">The value to write.</param>
    /// <param name="quality">The OPC UA quality value.</param>
    /// <param name="sourceTimestampUtc">The source timestamp in UTC.</param>
    public void WriteValue(string nodeId, object? value, OpcUaQuality quality, DateTime sourceTimestampUtc)
        => _inner.WriteValue(nodeId, value, quality, sourceTimestampUtc);

    /// <summary>Writes an alarm state through the inner sink.</summary>
    /// <param name="alarmNodeId">The node ID of the alarm condition.</param>
    /// <param name="active">Whether the alarm is active.</param>
    /// <param name="acknowledged">Whether the alarm has been acknowledged.</param>
    /// <param name="sourceTimestampUtc">The source timestamp in UTC.</param>
    public void WriteAlarmState(string alarmNodeId, bool active, bool acknowledged, DateTime sourceTimestampUtc)
        => _inner.WriteAlarmState(alarmNodeId, active, acknowledged, sourceTimestampUtc);

    /// <summary>Ensures a folder exists in the address space through the inner sink.</summary>
    /// <param name="folderNodeId">The node ID of the folder.</param>
    /// <param name="parentNodeId">The node ID of the parent folder, or null for root.</param>
    /// <param name="displayName">The display name of the folder.</param>
    public void EnsureFolder(string folderNodeId, string? parentNodeId, string displayName)
        => _inner.EnsureFolder(folderNodeId, parentNodeId, displayName);

    /// <summary>Ensures a variable exists in the address space through the inner sink.</summary>
    /// <param name="variableNodeId">The node ID of the variable.</param>
    /// <param name="parentFolderNodeId">The node ID of the parent folder, or null for root.</param>
    /// <param name="displayName">The display name of the variable.</param>
    /// <param name="dataType">The OPC UA data type of the variable.</param>
    public void EnsureVariable(string variableNodeId, string? parentFolderNodeId, string displayName, string dataType)
        => _inner.EnsureVariable(variableNodeId, parentFolderNodeId, displayName, dataType);

    /// <summary>Rebuilds the address space through the inner sink.</summary>
    public void RebuildAddressSpace() => _inner.RebuildAddressSpace();
}
