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

/// <summary>
///     Abstraction over the compiled virtual-tag expression engine. Runtime consumes this so
///     <see cref="VirtualTagActor"/> can stay free of Roslyn / scripting machinery and the
///     production wiring binds an adapter over <c>VirtualTagEngine</c> from
///     <c>Core.VirtualTags</c>.
/// </summary>
public interface IVirtualTagEvaluator
{
    /// <summary>
    ///     Evaluate <paramref name="expression"/> against the snapshot in
    ///     <paramref name="dependencies"/>. Implementations must not throw — script failures
    ///     are reported via <see cref="VirtualTagEvalResult.Failure"/>.
    /// </summary>
    /// <param name="virtualTagId">The unique identifier of the virtual tag being evaluated.</param>
    /// <param name="expression">The expression string to evaluate.</param>
    /// <param name="dependencies">Read-only dictionary of variable names to values for expression evaluation.</param>
    /// <returns>Result containing success flag, evaluated value, and optional failure reason.</returns>
    VirtualTagEvalResult Evaluate(string virtualTagId, string expression, IReadOnlyDictionary<string, object?> dependencies);
}

/// <summary>Result of one virtual-tag expression eval. Stash a Reason on every Failure so
/// callers can emit a useful <c>ScriptLogEntry</c> to operators.</summary>
public sealed record VirtualTagEvalResult(bool Success, object? Value, string? Reason)
{
    public static readonly VirtualTagEvalResult NoChange = new(true, null, "no-change");

    /// <summary>Creates a successful evaluation result with the given value.</summary>
    /// <param name="value">The evaluated value.</param>
    /// <returns>A successful evaluation result.</returns>
    public static VirtualTagEvalResult Ok(object? value) => new(true, value, null);

    /// <summary>Creates a failed evaluation result with the given reason.</summary>
    /// <param name="reason">Description of the failure cause.</param>
    /// <returns>A failed evaluation result.</returns>
    public static VirtualTagEvalResult Failure(string reason) => new(false, null, reason);
}

/// <summary>Returns <see cref="VirtualTagEvalResult.NoChange"/> from every call. Bound by default
/// when the production <c>VirtualTagEngine</c> adapter hasn't been registered (Mac dev, tests).</summary>
public sealed class NullVirtualTagEvaluator : IVirtualTagEvaluator
{
    public static readonly NullVirtualTagEvaluator Instance = new();
    private NullVirtualTagEvaluator() { }
    /// <inheritdoc />
    public VirtualTagEvalResult Evaluate(string virtualTagId, string expression, IReadOnlyDictionary<string, object?> dependencies)
        => VirtualTagEvalResult.NoChange;
}
