"""`_pending_ziel`: das SQUEEZE-Verhalten darf sich beim Entfernen von SIG
NICHT aendern.

ANLASS (2026-08-12): der Auto-Signal-Pfad wird entfernt — gemessen negativ auf
BEIDEN Ausfuehrungs-Achsen (Markt: alle 8 Felder; ruhende Order mit Touch-Fill:
alle 6 Felder, sogar schlechter als der Markt-Einstieg). Grund strukturell:
`_pend` WANDERT, und „ein Level ist nur dann ein Ausfuehrungs-Vorteil, wenn es
STILLSTEHT".

⚠⚠ DAS RISIKO IST NICHT SIG, SONDERN DER GETEILTE MANAGER. Squeeze und Signal
teilen sich `_pending_ziel` — bewusst, weil zwei getrennte Manager ueber
`trader.pending_orders()` die Orders des jeweils anderen sehen und sich
**gegenseitig stornieren** wuerden (der Bug waere sporadisch und im Log kaum
erkennbar). Dieser Test nagelt den Squeeze-Zweig fest, BEVOR der Signal-Zweig
faellt, und laeuft danach unveraendert weiter.
"""
from __future__ import annotations

import MetaTrader5 as mt5
import pytest

from core.engine import TradingEngine

_SQ_K = 0.1          # `_SQ_K_ENG` — Ausbruchspuffer in ×ATR


class _Wave:
    def __init__(self, snap):
        self._snap = snap

    def snapshot(self):
        return self._snap


class _Stub:
    """Minimal-Engine — nur was `_pending_ziel` liest."""

    _pending_ziel = TradingEngine._pending_ziel

    def __init__(self, *, sq_state="armed", hi=80.5, lo=80.0, atr=0.2,
                 sq_pending=True, guard=None):
        self._squeeze_pending = sq_pending
        self._guard = guard
        self.wave = _Wave({
            "pb_feats": {"atr": atr},
            "squeeze": {"state": sq_state, "box_hi": hi, "box_lo": lo},
        })
        # SIG-Felder bleiben gesetzt, damit der Test VOR und NACH dem Entfernen
        # laeuft: existiert der Signal-Zweig nicht mehr, werden sie ignoriert.
        self._signal_pending = False
        self._auto_signal_min_conf = 75

    def _auto_guard(self, quelle):
        return self._guard


def test_armed_legt_beide_box_grenzen():
    """Der Kern: solange der Squeeze `armed` ist, liegen BEIDE Stop-Orders —
    je einen Ausbruchspuffer ausserhalb der Box."""
    ziel, quelle = _Stub(hi=80.5, lo=80.0, atr=0.2)._pending_ziel()
    assert quelle == "squeeze"
    assert ziel == {mt5.ORDER_TYPE_BUY_STOP: round(80.5 + _SQ_K * 0.2, 3),
                    mt5.ORDER_TYPE_SELL_STOP: round(80.0 - _SQ_K * 0.2, 3)}


def test_nicht_armed_legt_nichts():
    for st in ("active", None):
        ziel, quelle = _Stub(sq_state=st)._pending_ziel()
        assert ziel == {} and quelle == ""


def test_guard_storniert():
    """Nacht-Fenster / News / Circuit-Breaker: der Guard ist die EINE Quelle fuer
    beide Wege — schlaegt er zu, darf nichts liegen."""
    ziel, quelle = _Stub(guard="nacht")._pending_ziel()
    assert ziel == {} and quelle == ""


def test_feature_aus_legt_nichts():
    ziel, quelle = _Stub(sq_pending=False)._pending_ziel()
    assert ziel == {} and quelle == ""


def test_ohne_atr_kein_absturz():
    s = _Stub()
    s.wave = _Wave({"pb_feats": {"atr": 0.0}, "squeeze": {"state": "armed",
                                                          "box_hi": 80.5, "box_lo": 80.0}})
    ziel, quelle = s._pending_ziel()
    assert ziel == {} and quelle == ""


def test_fehlende_box_legt_nichts():
    for hi, lo in ((None, 80.0), (80.5, None), (None, None)):
        ziel, quelle = _Stub(hi=hi, lo=lo)._pending_ziel()
        assert ziel == {} and quelle == ""


def test_preise_haengen_am_atr():
    """Der Puffer skaliert mit dem ATR — sonst waere er in ruhigen Phasen zu weit
    und in schnellen zu eng."""
    a, _ = _Stub(atr=0.1)._pending_ziel()
    b, _ = _Stub(atr=0.4)._pending_ziel()
    assert a[mt5.ORDER_TYPE_BUY_STOP] < b[mt5.ORDER_TYPE_BUY_STOP]
    assert a[mt5.ORDER_TYPE_SELL_STOP] > b[mt5.ORDER_TYPE_SELL_STOP]


if __name__ == "__main__":
    raise SystemExit(pytest.main([__file__, "-v"]))
