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

/// <summary>
///     Abstraction over the OPC UA SDK's address space. <c>OpcUaPublishActor</c> consumes this
///     so the Runtime project doesn't reference <c>Opc.Ua.Server</c> directly — production
///     binds a real SDK-backed sink in the fused Host's wiring, dev/Mac binds the
///     <see cref="NullOpcUaAddressSpaceSink"/> no-op.
/// </summary>
public interface IOpcUaAddressSpaceSink
{
    /// <summary>Write a Variable node's current value + quality + source timestamp.</summary>
    /// <param name="nodeId">The OPC UA node ID of the variable.</param>
    /// <param name="value">The value to write.</param>
    /// <param name="quality">The quality status of the value.</param>
    /// <param name="sourceTimestampUtc">The source timestamp in UTC.</param>
    void WriteValue(string nodeId, object? value, OpcUaQuality quality, DateTime sourceTimestampUtc);

    /// <summary>Write an alarm-condition Variable's active/acknowledged state.</summary>
    /// <param name="alarmNodeId">The OPC UA node ID of the alarm.</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>
    void WriteAlarmState(string alarmNodeId, bool active, bool acknowledged, DateTime sourceTimestampUtc);

    /// <summary>
    ///     Ensure a folder node exists under the given parent. Used by <c>Phase7Applier</c> to
    ///     materialise the UNS Area/Line/Equipment hierarchy in the address space. When
    ///     <paramref name="parentNodeId"/> is null the folder is parented under the namespace
    ///     root. Idempotent: calling twice with the same id is safe.
    /// </summary>
    /// <param name="folderNodeId">The OPC UA node ID for the folder.</param>
    /// <param name="parentNodeId">The parent folder node ID, or null for namespace root.</param>
    /// <param name="displayName">The display name for the folder.</param>
    void EnsureFolder(string folderNodeId, string? parentNodeId, string displayName);

    /// <summary>
    ///     Ensure a Variable node exists at <paramref name="variableNodeId"/>, parented under
    ///     <paramref name="parentFolderNodeId"/> (or the namespace root when null). Created with
    ///     Bad quality + null value; subsequent <see cref="WriteValue"/> calls update both.
    ///     Used by <c>Phase7Applier</c> to materialise Galaxy / SystemPlatform tags ahead of any
    ///     driver-side subscribe so OPC UA clients can browse them. Idempotent.
    /// </summary>
    /// <param name="variableNodeId">The OPC UA node ID for the variable.</param>
    /// <param name="parentFolderNodeId">The parent folder node ID, or null for namespace root.</param>
    /// <param name="displayName">The display name for the variable.</param>
    /// <param name="dataType">OPC UA built-in type name ("Boolean" / "Int32" / "Float" / etc.).</param>
    void EnsureVariable(string variableNodeId, string? parentFolderNodeId, string displayName, string dataType);

    /// <summary>
    ///     Tear down + repopulate the address space. Called by <c>OpcUaPublishActor</c> after a
    ///     successful deployment apply so the node manager reflects the new config. Idempotent.
    /// </summary>
    void RebuildAddressSpace();
}

/// <summary>OPC UA status code projection — Good / Uncertain / Bad. Real SDK has finer-grained
/// codes; the engine actors only need this 3-state classification.</summary>
public enum OpcUaQuality { Good, Uncertain, Bad }

/// <summary>No-op sink. Bound by default so the actors are safe to run in dev / Mac /
/// integration tests without a real SDK behind them.</summary>
public sealed class NullOpcUaAddressSpaceSink : IOpcUaAddressSpaceSink
{
    public static readonly NullOpcUaAddressSpaceSink Instance = new();
    private NullOpcUaAddressSpaceSink() { }

    /// <inheritdoc />
    public void WriteValue(string nodeId, object? value, OpcUaQuality quality, DateTime sourceTimestampUtc) { }

    /// <inheritdoc />
    public void WriteAlarmState(string alarmNodeId, bool active, bool acknowledged, DateTime sourceTimestampUtc) { }

    /// <inheritdoc />
    public void EnsureFolder(string folderNodeId, string? parentNodeId, string displayName) { }

    /// <inheritdoc />
    public void EnsureVariable(string variableNodeId, string? parentFolderNodeId, string displayName, string dataType) { }

    /// <inheritdoc />
    public void RebuildAddressSpace() { }
}
