"""OKX 交易账户自动兑 USDC 测试。"""

from __future__ import annotations

from unittest.mock import MagicMock, patch

from app.strategy.auto_usdc import ensure_okx_trading_usdc


def test_skip_when_usdc_enough() -> None:
    cap = {
        "option_need_usdc": 100.0,
        "option_have_usdc": 100.0,
        "perp_need_usdt": 50.0,
        "perp_have_usdt": 500.0,
        "option_can_open": True,
    }
    with (
        patch("app.strategy.auto_usdc._is_okx", return_value=True),
        patch("app.strategy.auto_usdc.assess_open_capacity", return_value=cap),
    ):
        r = ensure_okx_trading_usdc(db=MagicMock(), cap=cap)
    assert r["skipped"] is True
    assert r["acted"] is False
    assert "已够开仓" in r["detail"]


def test_skip_non_okx() -> None:
    with patch("app.strategy.auto_usdc._is_okx", return_value=False):
        r = ensure_okx_trading_usdc(db=MagicMock())
    assert r["skipped"] is True
    assert "非 OKX" in r["detail"]


def test_sim_convert_to_2x_need() -> None:
    """USDC 不足 → 交易账户兑到 需×2（预留永续保证金）。"""
    import app.strategy.auto_usdc as m

    m._last_attempt_ts = 0.0
    cap = {
        "option_need_usdc": 100.0,
        "option_have_usdc": 20.0,
        "perp_need_usdt": 50.0,
        "perp_have_usdt": 500.0,
        "option_can_open": False,
    }
    cap_after = {
        **cap,
        "option_have_usdc": 200.0,
        "option_can_open": True,
        "perp_have_usdt": 320.0,
    }
    wallets = MagicMock()
    wallets.convert.return_value = {"ok": True, "detail": "converted"}

    with (
        patch("app.strategy.auto_usdc._is_okx", return_value=True),
        patch("app.strategy.auto_usdc.assess_open_capacity", side_effect=[cap, cap_after]),
        patch("app.strategy.auto_usdc.get_settings") as gs,
        patch("app.live.okx_funds.usdc_usdt_mid_rate", return_value=1.0),
        patch("app.sim.funds_wallets.SimFundsWallets", return_value=wallets),
        patch("app.strategy.auto_usdc.invalidate_live_balance_cache"),
    ):
        gs.return_value.is_sim = True
        r = ensure_okx_trading_usdc(db=MagicMock(), cap=cap)

    assert r["acted"] is True
    assert r["ok"] is True
    # target=200, have=20 → gap=180 USDT
    wallets.convert.assert_called_once()
    kwargs = wallets.convert.call_args.kwargs
    assert kwargs["direction"] == "usdt_to_usdc"
    assert kwargs["account"] == "trading"
    assert kwargs["amount"] == 180.0


def test_reserve_perp_margin() -> None:
    """可兑 USDT = 交易 USDT − 永续所需。"""
    import app.strategy.auto_usdc as m

    m._last_attempt_ts = 0.0
    cap = {
        "option_need_usdc": 100.0,
        "option_have_usdc": 0.0,
        "perp_need_usdt": 400.0,
        "perp_have_usdt": 450.0,  # 可兑仅 50
        "option_can_open": False,
    }
    wallets = MagicMock()
    wallets.convert.return_value = {"ok": True, "detail": "converted"}
    cap_after = {**cap, "option_have_usdc": 50.0}

    with (
        patch("app.strategy.auto_usdc._is_okx", return_value=True),
        patch("app.strategy.auto_usdc.assess_open_capacity", side_effect=[cap, cap_after]),
        patch("app.strategy.auto_usdc.get_settings") as gs,
        patch("app.live.okx_funds.usdc_usdt_mid_rate", return_value=1.0),
        patch("app.sim.funds_wallets.SimFundsWallets", return_value=wallets),
        patch("app.strategy.auto_usdc.invalidate_live_balance_cache"),
    ):
        gs.return_value.is_sim = True
        r = ensure_okx_trading_usdc(db=MagicMock(), cap=cap, force=True)

    assert r["acted"] is True
    assert wallets.convert.call_args.kwargs["amount"] == 50.0


def test_force_does_not_bypass_cooldown() -> None:
    import app.strategy.auto_usdc as m
    import time

    m._last_attempt_ts = time.time()
    cap = {
        "option_need_usdc": 100.0,
        "option_have_usdc": 0.0,
        "perp_need_usdt": 10.0,
        "perp_have_usdt": 500.0,
        "option_can_open": False,
    }
    with (
        patch("app.strategy.auto_usdc._is_okx", return_value=True),
        patch("app.strategy.auto_usdc.assess_open_capacity", return_value=cap),
    ):
        r = ensure_okx_trading_usdc(db=MagicMock(), cap=cap, force=True)
    assert r["acted"] is False
    assert "冷却" in r["detail"]
