using Shouldly;
using Xunit;
using ZB.MOM.WW.OtOpcUa.Core.Scripting;

namespace ZB.MOM.WW.OtOpcUa.Core.Scripting.Tests;

/// <summary>
///     Locks the boundary semantics of <see cref="ScriptContext.Deadband"/>. The helper
///     is the canonical "ignore small noise" predicate alarm authors compose into bigger
///     expressions; subtle sign / boundary changes here would silently move the
///     active-state edge of every consuming alarm. (Core.Scripting-011.)
/// </summary>
[Trait("Category", "Unit")]
public sealed class ScriptContextTests
{
    /// <summary>Verifies that Deadband returns false when the difference exactly equals the tolerance.</summary>
    [Fact]
    public void Deadband_returns_false_when_difference_equals_tolerance()
    {
        // Strict greater-than comparison: a delta exactly equal to tolerance is
        // considered "within deadband" and must NOT trip.
        ScriptContext.Deadband(current: 10.5, previous: 10.0, tolerance: 0.5).ShouldBeFalse();
    }

    /// <summary>Verifies that Deadband returns true when the difference exceeds the tolerance.</summary>
    [Fact]
    public void Deadband_returns_true_when_difference_just_exceeds_tolerance()
    {
        // Any delta strictly greater than tolerance trips the deadband.
        ScriptContext.Deadband(current: 10.6, previous: 10.0, tolerance: 0.5).ShouldBeTrue();
    }

    /// <summary>Verifies that Deadband returns false when the difference is below the tolerance.</summary>
    [Fact]
    public void Deadband_returns_false_when_difference_just_below_tolerance()
    {
        ScriptContext.Deadband(current: 10.4, previous: 10.0, tolerance: 0.5).ShouldBeFalse();
    }

    /// <summary>Verifies that Deadband returns the same result regardless of the direction of change.</summary>
    [Fact]
    public void Deadband_is_symmetric_in_direction_of_change()
    {
        // Math.Abs ensures the helper trips identically whether the value rose or fell.
        ScriptContext.Deadband(current: 9.0, previous: 10.0, tolerance: 0.5).ShouldBeTrue();
        ScriptContext.Deadband(current: 11.0, previous: 10.0, tolerance: 0.5).ShouldBeTrue();
    }

    /// <summary>Verifies that Deadband returns false when current and previous values are equal.</summary>
    [Fact]
    public void Deadband_returns_false_when_values_are_equal()
    {
        ScriptContext.Deadband(current: 10.0, previous: 10.0, tolerance: 0.001).ShouldBeFalse();
    }

    /// <summary>Verifies that Deadband with zero tolerance trips on any non-zero difference.</summary>
    [Fact]
    public void Deadband_with_zero_tolerance_returns_true_for_any_difference()
    {
        // Zero-tolerance is the "trip on any non-equal change" mode. Equality still
        // returns false (delta 0 is not strictly greater than 0).
        ScriptContext.Deadband(current: 10.0, previous: 10.0, tolerance: 0).ShouldBeFalse();
        ScriptContext.Deadband(current: 10.0, previous: 10.000001, tolerance: 0).ShouldBeTrue();
    }

    /// <summary>Verifies that Deadband with negative tolerance trips on any non-zero difference.</summary>
    [Fact]
    public void Deadband_with_negative_tolerance_always_trips_for_unequal_values()
    {
        // A negative tolerance is nonsensical input but the helper does not guard
        // against it — Math.Abs(delta) is always >= 0, so the comparison is
        // effectively "any non-equal change". Lock this so a refactor that adds
        // (or removes) input validation requires an explicit test update.
        ScriptContext.Deadband(current: 10.0, previous: 10.5, tolerance: -1.0).ShouldBeTrue();
        ScriptContext.Deadband(current: 10.0, previous: 10.0, tolerance: -1.0).ShouldBeTrue();
    }
}
