"""Order planning for the Bybit testnet real-paper leg (design §4.2).

NO overlay re-apply: `bybit_4edge` is built naive equal-weight with NO risk overlay (see
`bybit_paper_book` module docstring + `latest_raw_sleeve_weights`), so the raw sleeve weights ARE the
effective weights — there is nothing to re-apply. `plan_orders` therefore diffs the raw target weights
straight against the testnet positions: target_qty = weight·equity/mark snapped to qtyStep, dust-filtered on
minNotional/minOrderQty, delta vs current, reduce_only on reductions/closes, gross capped at max_gross=1.0.
"""
from __future__ import annotations

from fxhnt.application.bybit_testnet_execution import (
    InstrumentLimit,
    OrderIntent,
    plan_orders,
)


def _lim(min_order_qty: float = 0.001, qty_step: float = 0.001,
         min_notional: float = 10.0) -> dict[str, InstrumentLimit]:
    return {
        "BTCUSDT": InstrumentLimit(min_order_qty, qty_step, min_notional),
        "ETHUSDT": InstrumentLimit(min_order_qty, qty_step, min_notional),
        "X": InstrumentLimit(min_order_qty, qty_step, min_notional),
        "A": InstrumentLimit(min_order_qty, qty_step, min_notional),
        "B": InstrumentLimit(min_order_qty, 0.5, min_notional),
    }


def test_buy_when_target_above_current():
    intents = plan_orders({"BTCUSDT": 0.5}, {"BTCUSDT": 0.0}, equity=10_000,
                          marks={"BTCUSDT": 100.0}, limits=_lim())
    assert intents == [OrderIntent("BTCUSDT", qty=50.0, reduce_only=False)]  # 0.5*10000/100


def test_reduce_only_on_close():
    intents = plan_orders({"BTCUSDT": 0.0}, {"BTCUSDT": 50.0}, 10_000,
                          {"BTCUSDT": 100.0}, _lim())
    assert intents[0].reduce_only is True
    assert intents[0].qty == -50.0


def test_partial_reduce_is_reduce_only():
    intents = plan_orders({"BTCUSDT": 0.2}, {"BTCUSDT": 50.0}, 10_000,
                          {"BTCUSDT": 100.0}, _lim())
    assert intents[0].qty == -30.0  # 20 target - 50 current
    assert intents[0].reduce_only is True


def test_min_notional_filters_dust():
    intents = plan_orders({"X": 0.0001}, {"X": 0.0}, 10_000, {"X": 100.0},
                          _lim(min_notional=50))
    assert intents == []  # 0.0001*10000=1 USD < 50 minNotional


def test_qty_step_snap():
    # 0.507*10000/100 = 50.7, snapped to a 0.5 step → 50.5
    intents = plan_orders({"B": 0.507}, {"B": 0.0}, 10_000, {"B": 100.0}, _lim())
    assert intents == [OrderIntent("B", qty=50.5, reduce_only=False)]


def test_gross_cap_clips_oversized_weights():
    # Σ|weight·equity| = 1.6·equity > equity·1.0 → scale by 0.625 → 0.5 each → qty 50 each
    intents = plan_orders({"A": 0.8, "B": 0.8}, {"A": 0.0, "B": 0.0}, 10_000,
                          {"A": 100.0, "B": 100.0}, _lim())
    by_symbol = {i.symbol: i for i in intents}
    assert by_symbol["A"].qty == 50.0
    assert by_symbol["B"].qty == 50.0


def test_no_order_when_already_on_target():
    intents = plan_orders({"BTCUSDT": 0.5}, {"BTCUSDT": 50.0}, 10_000,
                          {"BTCUSDT": 100.0}, _lim())
    assert intents == []


def test_flatten_on_zero_equity_closes_held_position():
    # De-funded / killswitched edge: crypto_envelope returns 0 -> equity=0 here, so target_qty collapses to
    # 0 regardless of the target weight (pinned NONZERO below — the production flatten path passes LIVE sleeve
    # weights, not a zeroed one), and the diff against the held long is a reduce_only close.
    intents = plan_orders({"BTCUSDT": 0.5}, {"BTCUSDT": 1.0}, equity=0.0,
                          marks={"BTCUSDT": 100.0}, limits=_lim(min_notional=5.0),
                          min_order_usd=10.0, max_gross=1.0)
    assert len(intents) == 1
    intent = intents[0]
    assert intent.symbol == "BTCUSDT"
    assert intent.reduce_only is True
    assert intent.qty == -1.0  # closes the entire long (target_qty collapses to 0 despite nonzero weight)
