"""Test deterministici dello SHADOW/esecuzione TP01 (src/live/deribit.py).

Coprono la logica a rischio zero che NON tocca la rete: quantizzazione notional->contratti (INVERSE
e LINEARE USDC), sizing target, costruzione ordine di ribilancio (buy/sell/exit/None), e PARITA' col
backtest. Il conto reale e' USDC -> il path principale e' il LINEARE BTC_USDC-PERPETUAL. Il fill reale
(slippage/fee) NON e' qui: si valida solo col micro-test mainnet.
"""
import sys
from pathlib import Path

PROJECT_ROOT = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(PROJECT_ROOT))

from src.live.deribit import (build_rebalance_order, notional_to_amount, target_notional_usd)
from src.strategies.trend_portfolio import CANONICAL, TrendPortfolio, resample_1d

LIN = "BTC_USDC-PERPETUAL"   # lineare USDC: amount in BTC, step 0.0001 (path reale del conto)
PX = 64000.0


def test_notional_linear_usdc():
    # amount in base-coin = notional/price, quantizzato a 0.0001, clamp al minimo
    assert notional_to_amount(LIN, 6.4, price=PX) == 0.0001       # 6.4/64000 = 0.0001
    assert notional_to_amount(LIN, 12.8, price=PX) == 0.0002
    assert notional_to_amount(LIN, 3.0, price=PX) == 0.0          # < mezzo step ($3.2) -> niente
    assert notional_to_amount(LIN, 100, price=None) == 0.0        # lineare senza prezzo -> 0
    assert notional_to_amount(LIN, -6.4, price=PX) == 0.0001      # usa il valore assoluto


def test_notional_inverse_still_supported():
    # l'helper regge ancora gli inverse (amount in USD), senza prezzo
    assert notional_to_amount("BTC-PERPETUAL", 1000) == 1000
    assert notional_to_amount("BTC-PERPETUAL", 7) == 10           # clamp al minimo
    assert notional_to_amount("BTC-PERPETUAL", 3) == 0.0


def test_no_float_artifacts():
    v = notional_to_amount(LIN, 0.0001 * PX * 72, price=PX)       # 72 step esatti
    assert v == 0.0072 and abs(v - 0.0072) < 1e-12


def test_target_notional_5050_weight():
    assert target_notional_usd(1.0, 0.5, 2000) == 1000
    assert target_notional_usd(2.0, 0.5, 2000) == 2000           # leva-cap 2x -> piena equity sull'asset
    assert target_notional_usd(0.0, 0.5, 2000) == 0.0


def test_build_order_entry_linear():
    o = build_rebalance_order(LIN, target_fraction=1.0, weight=0.5,
                              equity_usd=2000, current_pos_usd=0.0, price=PX)
    assert o["side"] == "buy" and o["reduce_only"] is False
    assert o["target_notional"] == 1000 and o["delta_notional"] == 1000
    assert abs(o["amount"] - 0.0156) < 1e-9                       # 1000/64000=0.015625 -> 0.0156


def test_build_order_exit_is_reduce_only():
    o = build_rebalance_order(LIN, target_fraction=0.0, weight=0.5,
                              equity_usd=2000, current_pos_usd=1000.0, price=PX)
    assert o["side"] == "sell" and o["reduce_only"] is True and o["amount"] > 0


def test_build_order_already_at_target_is_none():
    o = build_rebalance_order(LIN, 1.0, 0.5, 2000, current_pos_usd=1000.0, price=PX)
    assert o is None                                              # delta 0 -> nessun ordine


def test_build_order_subthreshold_is_none():
    # delta $2 (< mezzo step in notional, $3.2 a 64k) -> niente ordine
    o = build_rebalance_order(LIN, 1.0, 0.5, 2000, current_pos_usd=998.0, price=PX)
    assert o is None


def test_partial_rebalance_direction():
    up = build_rebalance_order(LIN, 1.0, 0.5, 2000, 600.0, price=PX)   # compra il delta
    dn = build_rebalance_order(LIN, 1.0, 0.5, 2000, 1400.0, price=PX)  # vende il delta
    assert up["side"] == "buy" and up["delta_notional"] == 400
    assert dn["side"] == "sell" and dn["delta_notional"] == -400 and dn["reduce_only"] is False


def test_disaster_stop_price():
    from src.live.deribit import disaster_stop_price
    assert disaster_stop_price(LIN, 64000, 0.30, long=True) == 44800.0     # -30%, al tick
    assert disaster_stop_price(LIN, 64000, 0.30, long=False) == 83200.0    # +30% (short)


def test_parity_live_target_equals_backtest():
    from src.backtest.harness import load
    tp = TrendPortfolio(**CANONICAL)
    df = resample_1d(load("BTC", "1h"))
    assert tp.current_target(df) == tp.target_series(df)[-1]
