# Feature: dual-pipeline-signal-engine, Property: Fibonacci retracement bounds
"""Property-based tests for the Fibonacci retracement formula.

Feature: dual-pipeline-signal-engine

Tests the Fibonacci retracement bounds property from the design specification:
for all retracement ratios r in [0, 1] and all swing high SH > swing low SL > 0,
the retracement level L(r) = SH - r * (SH - SL) must lie within [SL, SH].

Requirements: 2.1, 17.1
"""
from __future__ import annotations

from hypothesis import given, settings
from hypothesis import strategies as st

# ---------------------------------------------------------------------------
# Property: Fibonacci retracement bounds
# Validates: Requirements 2.1, 17.1
# ---------------------------------------------------------------------------

# ---------------------------------------------------------------------------
# Hypothesis strategies
# ---------------------------------------------------------------------------

# Retracement ratio in [0, 1]
_ratio = st.floats(min_value=0.0, max_value=1.0, allow_nan=False, allow_infinity=False)

# Positive floats for swing high / swing low
_positive_float = st.floats(
    min_value=1e-8, max_value=1e8, allow_nan=False, allow_infinity=False,
)


@st.composite
def _swing_pair(draw: st.DrawFn) -> tuple[float, float]:
    """Generate (SH, SL) where SH > SL > 0."""
    a = draw(_positive_float)
    b = draw(_positive_float)
    sh = max(a, b)
    sl = min(a, b)
    # Ensure strict inequality SH > SL
    if sh == sl:
        sh = sl + 1e-8
    return sh, sl


# ---------------------------------------------------------------------------
# Property test
# ---------------------------------------------------------------------------


@given(r=_ratio, swing=_swing_pair())
@settings(max_examples=100)
def test_fibonacci_retracement_within_bounds(r: float, swing: tuple[float, float]) -> None:
    """**Validates: Requirements 2.1, 17.1**

    For all r in [0, 1] and all SH > SL > 0, the Fibonacci retracement
    level L(r) = SH - r * (SH - SL) SHALL be in [SL, SH].

    This is a pure mathematical property — no evaluator class needed.
    """
    sh, sl = swing

    # Compute the retracement level
    level = sh - r * (sh - sl)

    assert sl <= level <= sh, (
        f"Fibonacci level {level} out of bounds [SL={sl}, SH={sh}] "
        f"for r={r}.\n"
        f"  L(r) = {sh} - {r} * ({sh} - {sl}) = {level}"
    )
