"""
Smoke tests for analysis/regime.py and analysis/bias.py.

Regime: tabular driven (input -> expected regime).
Bias: synthetic H4 series.

Run:
    cd ~/apex_v16
    python -m tests.test_regime_bias
"""

from __future__ import annotations

import sys
from pathlib import Path

sys.path.insert(0, str(Path(__file__).resolve().parent.parent))

from analysis.bias import BiasData, compute_algo_bias
from analysis.regime import determine_regime
from tests._fixtures.bars import (
    downtrend,
    hh_hl_h1,
    lh_ll_h1,
    ranging_h1,
    sideways,
    uptrend,
)


def _ok(label: str) -> None:
    print(f"  ok  {label}")


# ============================================================
# REGIME
# ============================================================

def _regime(**kwargs) -> tuple[str, str, list[str]]:
    """Helper: build kwargs with sensible defaults, override for the test case."""
    defaults = dict(
        rsi_h1=50.0,
        market_structure="RANGING",
        trend_maturity=0,
        atr_ratio=1.0,
        vol_spike=False,
        h1_struct_bull=False,
        h1_struct_bear=False,
    )
    defaults.update(kwargs)
    return determine_regime(**defaults)


def test_regime_strong_bearish():
    r, _, _ = _regime(rsi_h1=25, market_structure="BEARISH_EXPANSION", trend_maturity=4)
    assert r == "TRENDING"
    _ok("regime: H1 RSI<30+BEARISH_EXP+mat>=3 -> TRENDING")


def test_regime_strong_bullish():
    r, _, _ = _regime(rsi_h1=75, market_structure="BULLISH_EXPANSION", trend_maturity=4)
    assert r == "TRENDING"
    _ok("regime: H1 RSI>70+BULLISH_EXP+mat>=3 -> TRENDING")


def test_regime_breakout():
    r, _, _ = _regime(atr_ratio=2.5, vol_spike=True)
    assert r == "BREAKOUT"
    _ok("regime: ATR>2.0 + vol_spike -> BREAKOUT")


def test_regime_atr_maturity_trending():
    r, _, _ = _regime(atr_ratio=1.4, trend_maturity=3)
    assert r == "TRENDING"
    _ok("regime: ATR>1.3 + mat>=3 -> TRENDING")


def test_regime_atr_moderate_soft():
    r, _, _ = _regime(atr_ratio=1.2, trend_maturity=3)
    assert r == "TRENDING_SOFT"
    _ok("regime: ATR 1.1-1.3 + mat>=3 -> TRENDING_SOFT")


def test_regime_struct_bull_trending():
    r, _, _ = _regime(rsi_h1=60, market_structure="BULLISH_EXPANSION",
                      h1_struct_bull=True, trend_maturity=3)
    assert r == "TRENDING"
    _ok("regime: HH+HL struct + RSI>50 -> TRENDING")


def test_regime_default_ranging():
    r, reason, near = _regime()
    assert r == "RANGING"
    assert reason == "Multi-TF neutral"
    assert near == []
    _ok("regime: default neutral inputs -> RANGING")


def test_regime_ranging_with_diagnostics():
    r, _, near = _regime(
        rsi_h1=35, market_structure="BEARISH_EXPANSION",
        atr_ratio=1.05, trend_maturity=2,
        h1_struct_bear=True,
    )
    assert r == "RANGING"
    assert len(near) >= 1
    _ok(f"regime: near-trending diagnostics emitted ({len(near)} hints)")


# ============================================================
# BIAS
# ============================================================

def test_bias_neutro_on_short_history():
    df = uptrend(10)
    b = compute_algo_bias(df)
    assert b.bias == "NEUTRO"
    assert b.allowed_direction == "NONE"
    _ok("bias: <20 bars -> NEUTRO")


def test_bias_bullish_ema_struct_path():
    """
    Noisy uptrend (some red bars) -> RSI stays in [50,80] so RSI override
    does NOT fire; EMA+struct path produces RIALZISTA.
    """
    import pandas as pd
    rows = []
    base = 100.0
    # 30 bars: bullish drift with occasional pullbacks so RSI doesn't saturate
    # Tuned to keep RSI around 60-70 (no override) and EMA20>EMA50.
    pattern = [+0.3, +0.2, -0.4, +0.5, +0.3, -0.5, +0.4, +0.3, -0.4, +0.5] * 3
    for delta in pattern:
        o = base
        c = base + delta
        h = max(o, c) + 0.1
        l = min(o, c) - 0.1
        rows.append({"open": o, "high": h, "low": l, "close": c, "volume": 1000})
        base = c
    df = pd.DataFrame(rows)
    b = compute_algo_bias(df)
    assert b.rsi_h4_override is False, f"override fired unexpectedly: {b.h1_reason}"
    assert b.bias == "RIALZISTA", f"got {b.bias} | {b.h1_reason}"
    assert b.allowed_direction == "BUY"
    _ok(f"bias: EMA+struct bullish path -> RIALZISTA comp={b.h1_compatibility}")


def test_bias_bearish_ema_struct_path():
    import pandas as pd
    rows = []
    base = 100.0
    pattern = [-0.3, -0.2, +0.4, -0.5, -0.3, +0.5, -0.4, -0.3, +0.4, -0.5] * 3
    for delta in pattern:
        o = base
        c = base + delta
        h = max(o, c) + 0.1
        l = min(o, c) - 0.1
        rows.append({"open": o, "high": h, "low": l, "close": c, "volume": 1000})
        base = c
    df = pd.DataFrame(rows)
    b = compute_algo_bias(df)
    assert b.rsi_h4_override is False
    assert b.bias == "RIBASSISTA"
    assert b.allowed_direction == "SELL"
    _ok(f"bias: EMA+struct bearish path -> RIBASSISTA comp={b.h1_compatibility}")


def test_bias_rsi_h4_override_overbought():
    """
    Strong uptrend will saturate RSI > 80 -> RSI override returns
    RIBASSISTA / SELL (mean-reversion expected).
    """
    df = uptrend(40, body=1.0, step=1.0)
    b = compute_algo_bias(df)
    assert b.rsi_h4_override is True
    assert b.bias == "RIBASSISTA"
    assert b.allowed_direction == "SELL"
    _ok("bias: RSI H4>80 override -> RIBASSISTA SELL")


def test_bias_rsi_h4_override_oversold():
    df = downtrend(40, body=1.0, step=1.0)
    b = compute_algo_bias(df)
    assert b.rsi_h4_override is True
    assert b.bias == "RIALZISTA"
    assert b.allowed_direction == "BUY"
    _ok("bias: RSI H4<20 override -> RIALZISTA BUY")


def test_bias_ambiguous_ema_struct_clash():
    """
    Construct a series where EMA20>EMA50 (rising recent prices) but
    last 8 bars have LH/LL struct -> ambiguous -> NEUTRO with
    h1_compatibility 0.5 and ambiguous=True.

    Build: 20 bars rising 100->110 (lifts EMA20 above EMA50), then
    8 bars descending from 110 down to 105 (last-8 LH/LL).
    """
    import pandas as pd
    closes = [100.0 + i * 0.5 for i in range(20)] + \
             [110.0 - i * 0.6 for i in range(1, 9)]
    rows = [{"open": closes[i], "close": closes[i+1] if i+1 < len(closes) else closes[i],
             "high": max(closes[i], closes[min(i+1, len(closes)-1)]) + 0.05,
             "low":  min(closes[i], closes[min(i+1, len(closes)-1)]) - 0.05,
             "volume": 1000}
            for i in range(len(closes))]
    df = pd.DataFrame(rows)
    b = compute_algo_bias(df)
    # Either ambiguous NEUTRO, or compat <= 0.7 — anything but a clean 1.0
    assert b.h1_compatibility <= 0.7, \
        f"unexpected strong bias: {b.bias} comp={b.h1_compatibility} | {b.h1_reason}"
    _ok(f"bias: EMA-struct conflict -> {b.bias} comp={b.h1_compatibility} ambig={b.ambiguous}")


# ============================================================
# RUN
# ============================================================

def main() -> int:
    print("test_regime_bias.py")
    test_regime_strong_bearish()
    test_regime_strong_bullish()
    test_regime_breakout()
    test_regime_atr_maturity_trending()
    test_regime_atr_moderate_soft()
    test_regime_struct_bull_trending()
    test_regime_default_ranging()
    test_regime_ranging_with_diagnostics()
    test_bias_neutro_on_short_history()
    test_bias_bullish_ema_struct_path()
    test_bias_bearish_ema_struct_path()
    test_bias_rsi_h4_override_overbought()
    test_bias_rsi_h4_override_oversold()
    test_bias_ambiguous_ema_struct_clash()
    print("ALL 14 TESTS PASSED")
    return 0


if __name__ == "__main__":
    sys.exit(main())
