"""Einsatz je Pfad: der explizite `buffer_pct` muss den globalen Wert schlagen.

Hintergrund (Gesamtpruefung 2026-08-19). Der erste Entwurf hat den Einsatz je
Pfad umgesetzt, indem `engine._open` den Modul-Global `config.MARGIN_BUFFER`
temporaer ueberschrieb. Zwei Fehler steckten darin:

  (1) RACE. Der Global wirkt prozessweit. Zwischen dem Setzen in `_open` und dem
      `mt5_lock` in `trader._send` liegt ein Fenster von bis zu 15 s
      (Lock-Timeout). Eine gleichzeitige MANUELLE Order haette darin mit dem
      Prozentsatz des AUTONOMEN Pfads gesized.
  (2) WIRKUNGSLOS FUER BRK. `_manage_squeeze_pending` legt ruhende Stop-Orders
      ueber `trader.place_stop` -> `calc_lots`, also NICHT ueber `_open`. Da
      gemessen 7 von 8 BRK-Trades per ruhender Order fuellen, griff das Feld
      genau dort nicht, wo es hingehoert.

Beides ist behoben, indem `calc_lots` einen expliziten Parameter bekommt. Diese
Tests halten das fest -- ohne sie faellt ein Rueckbau auf die Global-Mutation
nicht auf.

⚠ `calc_lots` braucht MT5. Statt es zu stubben wird hier die reine RECHENREGEL
geprueft, die der Parameter steuert -- das ist der Teil, der kaputtgehen kann.
"""
import core.config as C
from core.mt5_utils import calc_lots


def _buf(buffer_pct):
    """Die Klemm-/Auswahlregel aus `calc_lots`, isoliert nachgebildet.

    ⚠ Bewusst KEINE Kopie der Formel: geprueft wird, dass der Parameter den
    Global schlaegt und dieselben Grenzen gelten (1-99 %).
    """
    from core.config import get_margin_buffer
    return (max(0.01, min(0.99, buffer_pct / 100.0))
            if buffer_pct and buffer_pct > 0 else get_margin_buffer())


def test_parameter_schlaegt_global():
    C.set_margin_buffer(40)
    assert abs(_buf(None) - 0.40) < 1e-9      # kein Parameter -> global
    assert abs(_buf(0) - 0.40) < 1e-9         # 0 heisst ausdruecklich "global"
    assert abs(_buf(50) - 0.50) < 1e-9        # Parameter gewinnt
    assert abs(_buf(25) - 0.25) < 1e-9


def test_global_bleibt_unveraendert():
    """Der Kern des Race-Fixes: ein Aufruf mit Parameter darf den PROZESSWEITEN
    Wert nicht anfassen -- sonst sizen parallele manuelle Orders falsch."""
    C.set_margin_buffer(40)
    vorher = C.get_margin_buffer()
    _buf(90)
    assert C.get_margin_buffer() == vorher, "der Global wurde veraendert"


def test_grenzen_geklemmt():
    C.set_margin_buffer(40)
    assert abs(_buf(150) - 0.99) < 1e-9       # 100 % liesse keinen Puffer
    assert abs(_buf(-5) - 0.40) < 1e-9        # negativ -> global, nicht 0


def test_signatur_nimmt_buffer_pct():
    """Haelt die Verdrahtung fest: `calc_lots` MUSS den Parameter annehmen.

    Ohne diesen Test faellt es nicht auf, wenn jemand die Signatur zurueckbaut --
    die Aufrufer wuerden dann mit TypeError sterben, und zwar erst beim ERSTEN
    autonomen Trade (also im `except`-Zweig, still).
    """
    import inspect
    p = inspect.signature(calc_lots).parameters
    assert "buffer_pct" in p, "calc_lots hat den Parameter verloren"
    assert p["buffer_pct"].default is None, "Default muss None (= global) sein"


def test_pfade_reichen_durch():
    """`place_stop` und `open_long/short` muessen den Parameter weiterreichen.

    ⚠ Das ist der Fund (2): BRK fuellt ueberwiegend per RUHENDER Order. Fehlt der
    Parameter an `place_stop`, ist das Feld fuer BRK wirkungslos -- und das faellt
    im Betrieb nur auf, wenn jemand die Lots nachrechnet.
    """
    import inspect

    import core.trader as T
    for name in ("open_long", "open_short", "place_stop", "_send", "_send_locked"):
        p = inspect.signature(getattr(T.TradeManager, name)).parameters
        assert "buffer_pct" in p, f"{name} reicht buffer_pct nicht durch"


def test_calc_lots_benutzt_den_parameter_wirklich():
    """Schliesst die Luecke, die die Mutationsprobe aufgedeckt hat.

    ⚠⚠ Die Tests darueber pruefen eine NACHGEBILDETE Regel (`_buf`) -- damit
    faellt es NICHT auf, wenn `calc_lots` den Parameter zwar entgegennimmt, ihn
    aber ignoriert und wieder den Global liest. Genau diese Mutation lief bei
    der ersten Probe gruen durch. Das ist dieselbe Nachbau-Falle, die im Projekt
    schon dreimal zugeschlagen hat (`sim_run` in >=6 Skripten, der nachgebaute
    Level-Cluster, das fuenfte Exit-Modell) -- hier in einem TEST.

    `calc_lots` braucht MT5 und darf in der Suite nicht laufen (conftest-Regel),
    deshalb wird strukturell geprueft: der Parametername MUSS im Rumpf der
    Funktion vorkommen. Das ist schwaecher als ein Verhaltenstest, faengt aber
    genau den Rueckbau, um den es geht.
    """
    import ast
    import inspect

    import core.mt5_utils as M
    baum = ast.parse(inspect.getsource(M.calc_lots))
    namen = {n.id for n in ast.walk(baum) if isinstance(n, ast.Name)}
    assert "buffer_pct" in namen, (
        "calc_lots nimmt buffer_pct entgegen, benutzt es aber nicht -- "
        "der Einsatz je Pfad waere wirkungslos")
