from lib.trade.order_monitor_display_lib import (
    apply_order_price_display_fields,
    calc_latest_risk_amount,
    calc_risk_fraction,
    is_sl_breakeven_secured,
    monitor_open_stop_loss,
    order_monitor_tpsl_needs_sync,
    resolve_breakeven_entry_price,
    resolve_live_tpsl_prices,
    sl_breakeven_from_exchange_tpsl,
    snapshot_rr,
    snapshot_stop_loss,
    stale_breakeven_armed,
)


def _calc_rr(direction, entry, sl, tp):
    if direction == "long":
        risk = entry - sl
        reward = tp - entry
    else:
        risk = sl - entry
        reward = entry - tp
    if risk <= 0 or reward <= 0:
        return None
    return round(reward / risk, 4)


def test_snapshot_stop_loss_prefers_initial():
    assert snapshot_stop_loss(2.45, 2.6) == 2.45
    assert snapshot_stop_loss(None, 2.6) == 2.6


def test_monitor_open_stop_loss_prefers_initial_snapshot():
    row = {"initial_stop_loss": 64000, "stop_loss": 63200}
    assert monitor_open_stop_loss(row) == 64000


def test_snapshot_rr_ignores_current_stop_after_manual_move():
    rr = snapshot_rr(_calc_rr, "long", 2.726, 2.45, 2.65, 3.3)
    assert rr is not None
    assert rr > 2.0


def test_breakeven_long():
    assert is_sl_breakeven_secured("long", 2.726, 2.726) is True
    assert is_sl_breakeven_secured("long", 2.726, 2.75) is True
    assert is_sl_breakeven_secured("long", 2.726, 2.45) is False


def test_breakeven_short():
    assert is_sl_breakeven_secured("short", 72.73, 72.73) is True
    assert is_sl_breakeven_secured("short", 72.73, 72.0) is True
    assert is_sl_breakeven_secured("short", 72.73, 74.0) is False


def test_sl_breakeven_from_exchange_tpsl():
    ok = sl_breakeven_from_exchange_tpsl(
        "long",
        2.726,
        {"sl": {"trigger_price": 2.735}, "tp": {"trigger_price": 3.3}},
    )
    assert ok is True


def test_resolve_live_tpsl_prefers_exchange():
    disp_sl, disp_tp, ex_sl, ex_tp = resolve_live_tpsl_prices(
        1674,
        1647.65,
        {"sl": {"trigger_price": 1661}, "tp": {"trigger_price": 1647.65}},
    )
    assert disp_sl == 1661
    assert disp_tp == 1647.65
    assert ex_sl == 1661
    assert ex_tp == 1647.65


def test_order_monitor_tpsl_needs_sync_detects_sl_change():
    new_sl, new_tp, changed = order_monitor_tpsl_needs_sync(
        1674,
        1647.65,
        {"sl": {"trigger_price": 1661}, "tp": {"trigger_price": 1647.65}},
    )
    assert changed is True
    assert new_sl == 1661
    assert new_tp == 1647.65


def test_apply_order_price_display_fields_live_sl():
    payload = {}
    apply_order_price_display_fields(
        payload,
        direction="short",
        entry_price=1663.45,
        initial_stop_loss=1674,
        stop_loss=1674,
        take_profit=1647.65,
        calc_rr_ratio_fn=_calc_rr,
        exchange_tpsl={"sl": {"trigger_price": 1661}, "tp": {"trigger_price": 1647.65}},
        format_price_fn=lambda _s, v: f"{v:.2f}",
        symbol="ETH/USDT:USDT",
        margin_capital=100,
        leverage=10,
        exchange_notional=1000,
        contracts=2.0,
        contract_size=1.0,
        avg_entry_price=1660.0,
    )
    assert payload["stop_loss"] == 1661
    assert payload["stop_loss_display"] == "1661.00"
    assert payload["sl_breakeven_secured"] is False
    assert payload["rr_ratio"] is not None
    assert payload["latest_risk_amount"] is not None
    assert payload["latest_risk_amount"] >= 0
    assert payload["contracts"] == 2.0
    assert payload["reward_at_tp_usdt"] is not None
    assert payload["reward_at_tp_usdt"] > 0


def test_apply_order_price_display_fields_gate_contract_size():
    payload = {}
    apply_order_price_display_fields(
        payload,
        direction="short",
        entry_price=62063.4,
        initial_stop_loss=62650,
        stop_loss=62650,
        take_profit=61200,
        calc_rr_ratio_fn=_calc_rr,
        exchange_tpsl={},
        symbol="BTC/USDT:USDT",
        margin_capital=48,
        leverage=10,
        contracts=78.0,
        contract_size=0.0001,
        avg_entry_price=62063.4,
    )
    assert payload["reward_at_tp_usdt"] is not None
    assert abs(payload["reward_at_tp_usdt"] - 6.73) < 0.1


def test_calc_latest_risk_amount_long():
    rf = calc_risk_fraction("long", 100, 95)
    assert rf is not None and abs(rf - 0.05) < 1e-9
    risk = calc_latest_risk_amount(
        "long", 100, 95, exchange_notional=1000, funds_decimals=2
    )
    assert risk == 50.0


def test_calc_latest_risk_amount_profit_side_stop():
    risk = calc_latest_risk_amount("long", 100, 101, exchange_notional=1000)
    assert risk == 0.0


def test_resolve_breakeven_entry_price_prefers_avg():
    assert resolve_breakeven_entry_price(1777.39, 1777.2) == 1777.2
    assert resolve_breakeven_entry_price(1777.39, None) == 1777.39


def test_roll_long_not_breakeven_with_avg_entry():
    payload = {}
    apply_order_price_display_fields(
        payload,
        direction="long",
        entry_price=1777.39,
        initial_stop_loss=1750,
        stop_loss=1767,
        take_profit=1833,
        calc_rr_ratio_fn=_calc_rr,
        exchange_tpsl={"sl": {"trigger_price": 1767}, "tp": {"trigger_price": 1833}},
        avg_entry_price=1777.2,
    )
    assert payload["sl_breakeven_secured"] is False


def test_stale_breakeven_armed_after_roll_down():
    assert stale_breakeven_armed("long", 1777.39, 1767, 1) is True
    assert stale_breakeven_armed("long", 1777.39, 1778, 1) is False
    assert stale_breakeven_armed("long", 1777.39, 1767, 0) is False
