from src.portfolio.runner import build_worker_for, pos_for_spec
from src.portfolio.base import SleeveSpec
from src.live.strategy_worker import StrategyWorker
from src.live.pairs_worker import PairsWorker


def test_build_single_worker_capital_from_alloc(tmp_path):
    spec = SleeveSpec(kind="single", name="MR01", sid="MR01_BTC", asset="BTC",
                      params={"bb_window": 50, "k": 2.5, "sl_atr": 2.0, "max_bars": 24})
    w = build_worker_for(spec, alloc_capital=300.0, leverage=2.0, data_dir=tmp_path)
    assert isinstance(w, StrategyWorker)
    assert w.capital == 300.0 and w.leverage == 2.0


def test_build_pairs_worker(tmp_path):
    spec = SleeveSpec(kind="pairs", name="PR01", sid="PR_ETHBTC", a="ETH", b="BTC",
                      params={"n": 50, "z_in": 2.0, "z_exit": 0.75, "max_bars": 72})
    w = build_worker_for(spec, alloc_capital=200.0, leverage=2.0, data_dir=tmp_path)
    assert isinstance(w, PairsWorker)
    assert w.capital == 200.0


def test_pos_for_spec_family_override():
    """position_size per-famiglia (improvement-sweep punto 8): l'override PAIRS si
    applica ai soli PR_*, gli altri sleeve restano sul globale."""
    fam = {"PAIRS": 0.20}
    assert pos_for_spec("PR_ETHBTC", 0.5, fam) == 0.20
    assert pos_for_spec("PR_ADAETH", 0.5, fam) == 0.20
    assert pos_for_spec("MR01_BTC", 0.5, fam) == 0.5      # FADE -> globale
    assert pos_for_spec("SH_BTC", 0.5, fam) == 0.5        # SHAPE -> globale
    assert pos_for_spec("TSM01", 0.5, fam) == 0.5
    assert pos_for_spec("PR_ETHBTC", 0.5, {}) == 0.5      # nessun override


def test_build_pairs_worker_position_size(tmp_path):
    spec = SleeveSpec(kind="pairs", name="PR01", sid="PR_ETHBTC", a="ETH", b="BTC",
                      params={"n": 50, "z_in": 2.0, "z_exit": 0.75, "max_bars": 72})
    w = build_worker_for(spec, alloc_capital=200.0, leverage=2.0,
                         position_size=0.20, data_dir=tmp_path)
    assert w.position_size == 0.20


def test_build_ml_sh01_is_plain_strategyworker(tmp_path):
    """SH01 (kind=ml) deve costruirsi come StrategyWorker che esegue SH01_shape_ml — NON il
    vecchio MLWorkerWrapper (SignalEngine squeeze scartato, che ignorava la strategia ed usciva
    a hold_bars=3). Regressione del bug di wiring scoperto il 2026-06-01."""
    spec = SleeveSpec(kind="ml", name="SH01", sid="SH_BTC", asset="BTC")
    w = build_worker_for(spec, alloc_capital=58.82, leverage=2.0, data_dir=tmp_path)
    assert isinstance(w, StrategyWorker)
    assert w.strategy.name == "SH01_shape_ml"          # esegue davvero shape-ML
    assert not hasattr(w, "engine")                     # niente SignalEngine squeeze
    assert w.tf == "1h"
