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

/// <summary>
///     Abstraction over the Phase 6.1 resilience pipeline that wraps a single driver instance's
///     capability calls (retry / circuit-breaker / in-flight accounting / tracker telemetry). The Runtime
///     dispatch layer (<c>DriverInstanceActor</c>) consumes this interface instead of the concrete
///     <c>CapabilityInvoker</c> so the actor assembly does NOT pull in
///     <c>ZB.MOM.WW.OtOpcUa.Core</c> (which drags in Polly + driver hosting) — the same boundary
///     rationale as <see cref="IDriverFactory"/>. The fused Host binds a real invoker (built by
///     <see cref="IDriverCapabilityInvokerFactory"/>) per spawned driver; when none is supplied the
///     dispatch layer uses <see cref="NullDriverCapabilityInvoker"/>, a genuine pass-through.
/// </summary>
/// <remarks>
///     The method shapes mirror <c>CapabilityInvoker</c> exactly so the concrete invoker satisfies
///     this interface without adapters. Callers pass the resolved <c>hostName</c> per call (a
///     multi-host driver resolves it from the tag reference via <see cref="IPerCallHostResolver"/>;
///     a single-host driver passes its <see cref="IDriver.DriverInstanceId"/>) so per-host breaker
///     isolation + in-flight-depth accounting key on the right target.
/// </remarks>
public interface IDriverCapabilityInvoker
{
    /// <summary>Execute a value-returning capability call through the per-capability pipeline.</summary>
    /// <typeparam name="TResult">Return type of the underlying driver call.</typeparam>
    /// <param name="capability">The driver capability being executed.</param>
    /// <param name="hostName">The resolved host name for pipeline keying + status tracking.</param>
    /// <param name="callSite">The async function to execute (the raw driver-capability call).</param>
    /// <param name="cancellationToken">Cancellation token for the operation.</param>
    /// <returns>The result of the underlying driver call.</returns>
    ValueTask<TResult> ExecuteAsync<TResult>(
        DriverCapability capability,
        string hostName,
        Func<CancellationToken, ValueTask<TResult>> callSite,
        CancellationToken cancellationToken);

    /// <summary>Execute a void-returning capability call through the per-capability pipeline.</summary>
    /// <param name="capability">The driver capability being executed.</param>
    /// <param name="hostName">The resolved host name for pipeline keying + status tracking.</param>
    /// <param name="callSite">The async function to execute (the raw driver-capability call).</param>
    /// <param name="cancellationToken">Cancellation token for the operation.</param>
    /// <returns>A task that represents the asynchronous operation.</returns>
    ValueTask ExecuteAsync(
        DriverCapability capability,
        string hostName,
        Func<CancellationToken, ValueTask> callSite,
        CancellationToken cancellationToken);

    /// <summary>
    ///     Execute a <see cref="DriverCapability.Write"/> call honoring idempotence semantics — when
    ///     <paramref name="isIdempotent"/> is <c>false</c>, retries are disabled regardless of the
    ///     configured policy; when <c>true</c>, the call runs through the Write pipeline which may
    ///     retry if the tier configuration permits. <b>The current production default is <c>false</c></b>
    ///     (R2-02/S-8): <c>DriverInstanceActor.HandleWriteAsync</c> passes <c>isIdempotent: false</c> so a
    ///     command-shaped write is never replayed; per-tag opt-in via <see cref="WriteIdempotentAttribute"/>
    ///     is the unshipped forward path.
    /// </summary>
    /// <typeparam name="TResult">Return type of the underlying driver write call.</typeparam>
    /// <param name="hostName">The resolved host name for pipeline keying + status tracking.</param>
    /// <param name="isIdempotent">Whether the write operation is safe to retry.</param>
    /// <param name="callSite">The async function to execute (the raw driver write call).</param>
    /// <param name="cancellationToken">Cancellation token for the operation.</param>
    /// <returns>The result of the underlying driver write call.</returns>
    ValueTask<TResult> ExecuteWriteAsync<TResult>(
        string hostName,
        bool isIdempotent,
        Func<CancellationToken, ValueTask<TResult>> callSite,
        CancellationToken cancellationToken);
}

/// <summary>
///     Pass-through <see cref="IDriverCapabilityInvoker"/> that invokes the call site directly with
///     NO pipeline — no timeout, retry, breaker, or telemetry. Bound when the dispatch layer is
///     constructed without a real invoker (unit tests, the F7 skeleton path, admin-only nodes that
///     never run drivers). Keeps behavior byte-for-byte identical to a raw driver call so existing
///     dispatch-layer tests are unaffected by the resilience wiring.
/// </summary>
public sealed class NullDriverCapabilityInvoker : IDriverCapabilityInvoker
{
    /// <summary>The shared singleton instance.</summary>
    public static readonly NullDriverCapabilityInvoker Instance = new();
    private NullDriverCapabilityInvoker() { }

    /// <inheritdoc />
    public ValueTask<TResult> ExecuteAsync<TResult>(
        DriverCapability capability,
        string hostName,
        Func<CancellationToken, ValueTask<TResult>> callSite,
        CancellationToken cancellationToken) => callSite(cancellationToken);

    /// <inheritdoc />
    public ValueTask ExecuteAsync(
        DriverCapability capability,
        string hostName,
        Func<CancellationToken, ValueTask> callSite,
        CancellationToken cancellationToken) => callSite(cancellationToken);

    /// <inheritdoc />
    public ValueTask<TResult> ExecuteWriteAsync<TResult>(
        string hostName,
        bool isIdempotent,
        Func<CancellationToken, ValueTask<TResult>> callSite,
        CancellationToken cancellationToken) => callSite(cancellationToken);
}
