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

/// <summary>
///     Reverse-path gateway for an inbound OPC UA operator write to a writable equipment-tag node.
///     The node manager calls <see cref="WriteAsync"/> fire-and-forget from its OnWriteValue handler
///     (which runs under the node-manager Lock, so the call MUST return promptly — it kicks off an
///     asynchronous route and the Task completes later) and uses the resolved
///     <see cref="NodeWriteOutcome"/> to self-correct the node on failure.
/// </summary>
public interface IOpcUaNodeWriteGateway
{
    /// <summary>Route a write of <paramref name="value"/> to the driver backing node
    /// <paramref name="nodeId"/>; the returned task resolves with the device-write outcome.</summary>
    /// <param name="nodeId">The full ns-qualified node id being written (<c>node.NodeId.ToString()</c>).</param>
    /// <param name="value">The value the client wrote.</param>
    /// <param name="realm">Which of the two v3 namespaces (<see cref="AddressSpaceRealm.Raw"/> device tree or
    /// <see cref="AddressSpaceRealm.Uns"/> equipment tree) <paramref name="nodeId"/> lives in — the node
    /// manager resolves it from the node's namespace index (<c>RealmOf</c>). It is REQUIRED for correct
    /// routing: a raw <c>s=&lt;RawPath&gt;</c> and a UNS <c>s=&lt;Area/Line/Equip/Eff&gt;</c> can collide as bare
    /// strings, so the routing map is keyed by <c>(realm, bareId)</c> — dropping the realm would let an
    /// operator write route to the WRONG driver ref.</param>
    /// <param name="ct">Cancellation token.</param>
    /// <returns>A task resolving to the device-write outcome.</returns>
    Task<NodeWriteOutcome> WriteAsync(string nodeId, object? value, AddressSpaceRealm realm, CancellationToken ct);
}

/// <summary>Outcome of routing an inbound node write to the backing driver.</summary>
/// <param name="Success">True when the driver accepted the write.</param>
/// <param name="Reason">Failure detail when <paramref name="Success"/> is false; null on success.</param>
public readonly record struct NodeWriteOutcome(bool Success, string? Reason);

/// <summary>No-op gateway: every write resolves to "writes unavailable" (matches the legacy
/// no-router-wired <c>BadNotWritable</c>). The default before the host wires the real gateway.</summary>
public sealed class NullOpcUaNodeWriteGateway : IOpcUaNodeWriteGateway
{
    /// <summary>The shared singleton instance.</summary>
    public static readonly NullOpcUaNodeWriteGateway Instance = new();
    private NullOpcUaNodeWriteGateway() { }
    /// <inheritdoc />
    public Task<NodeWriteOutcome> WriteAsync(string nodeId, object? value, AddressSpaceRealm realm, CancellationToken ct) =>
        Task.FromResult(new NodeWriteOutcome(false, "writes unavailable"));
}
