"""对冲计划:情景测算与全仓建议仓(纯函数,无 IO)."""
from __future__ import annotations

from typing import Any, Optional


def _f(v: Any) -> Optional[float]:
    if v is None or v == "":
        return None
    try:
        return float(v)
    except (TypeError, ValueError):
        return None


def perp_coin_amount(*, contracts: float, contract_size: float) -> float:
    return float(contracts) * float(contract_size or 1.0)


def perp_pnl(
    *,
    direction: str,
    entry: float,
    exit_px: float,
    contracts: float,
    contract_size: float,
) -> float:
    coins = perp_coin_amount(contracts=contracts, contract_size=contract_size)
    d = (direction or "long").strip().lower()
    if d == "short":
        return (float(entry) - float(exit_px)) * coins
    return (float(exit_px) - float(entry)) * coins


def option_premium_total(*, ask: float, sheets: float, ct_mult: float) -> float:
    """卖一报价为每 1 币;权利金 = ask × 张数 × ct_mult."""
    return float(ask) * float(sheets) * float(ct_mult or 0.01)


def option_expiry_pnl(
    *,
    opt_type: str,
    strike: float,
    spot: float,
    sheets: float,
    ct_mult: float,
    premium_paid: float,
) -> float:
    o = (opt_type or "").strip().upper()
    intrinsic_per_coin = 0.0
    if o in ("C", "CALL"):
        intrinsic_per_coin = max(0.0, float(spot) - float(strike))
    elif o in ("P", "PUT"):
        intrinsic_per_coin = max(0.0, float(strike) - float(spot))
    else:
        return -float(premium_paid)
    value = intrinsic_per_coin * float(sheets) * float(ct_mult or 0.01)
    return value - float(premium_paid)


def suggest_contracts_from_notional(
    *,
    notional: float,
    entry: float,
    contract_size: float,
) -> float:
    if entry <= 0 or contract_size <= 0 or notional <= 0:
        return 0.0
    return float(notional) / (float(entry) * float(contract_size))


def floor_contracts_to_precision(contracts: float, decimals: int) -> float:
    """按交易所张数精度向下取整,避免建议张数超过可用保证金."""
    import math

    raw = float(contracts or 0.0)
    if raw <= 0:
        return 0.0
    try:
        d = int(decimals)
    except (TypeError, ValueError):
        d = 0
    if d <= 0:
        return float(math.floor(raw + 1e-12))
    scale = 10**d
    return math.floor(raw * scale + 1e-12) / scale


def build_perp_options_preview(
    *,
    direction: str,
    entry: float,
    tp: float,
    sl: float,
    contracts: float,
    contract_size: float,
    opt_type: str,
    strike: float,
    sheets: float,
    ct_mult: float,
    premium_paid: float,
    index_px: Optional[float] = None,
) -> dict[str, Any]:
    """
    永期情景.
    止盈账:永续止盈盈利 - 权利金.
    止损账:期权到期内在(按 SL 价) - 永续止损亏损额.
    """
    d = (direction or "long").strip().lower()
    pnl_tp_perp = perp_pnl(
        direction=d, entry=entry, exit_px=tp, contracts=contracts, contract_size=contract_size
    )
    pnl_sl_perp = perp_pnl(
        direction=d, entry=entry, exit_px=sl, contracts=contracts, contract_size=contract_size
    )
    # 止盈统计口径
    tp_total = float(pnl_tp_perp) - float(premium_paid)
    # 止损:期权按 SL 价结算内在 - |永续亏损|
    opt_at_sl = option_expiry_pnl(
        opt_type=opt_type,
        strike=strike,
        spot=sl,
        sheets=sheets,
        ct_mult=ct_mult,
        premium_paid=premium_paid,
    )
    sl_total = float(opt_at_sl) - abs(float(pnl_sl_perp)) if pnl_sl_perp < 0 else float(opt_at_sl) + float(
        pnl_sl_perp
    )
    # 有符号相加更稳:期权盈亏 + 永续盈亏
    sl_total_signed = float(opt_at_sl) + float(pnl_sl_perp)

    spot = float(index_px) if index_px is not None else float(entry)
    opt_flat = option_expiry_pnl(
        opt_type=opt_type,
        strike=strike,
        spot=spot,
        sheets=sheets,
        ct_mult=ct_mult,
        premium_paid=premium_paid,
    )
    flat_total = 0.0 + float(opt_flat)

    opt_at_tp = option_expiry_pnl(
        opt_type=opt_type,
        strike=strike,
        spot=tp,
        sheets=sheets,
        ct_mult=ct_mult,
        premium_paid=premium_paid,
    )

    return {
        "plan_type": "perp_options",
        "direction": d,
        "contracts": contracts,
        "coin_amount": perp_coin_amount(contracts=contracts, contract_size=contract_size),
        "premium_paid": round(float(premium_paid), 6),
        "scenarios": [
            {
                "id": "tp",
                "label": "止盈(计划结束口径)",
                "spot": tp,
                "perp_pnl": round(pnl_tp_perp, 4),
                "options_pnl": round(-float(premium_paid), 4),
                "total": round(tp_total, 4),
                "note": "止盈盈利 − 权利金;期权可不强平",
            },
            {
                "id": "sl",
                "label": "止损(计划结束口径)",
                "spot": sl,
                "perp_pnl": round(pnl_sl_perp, 4),
                "options_pnl": round(opt_at_sl, 4),
                "total": round(sl_total_signed, 4),
                "note": "期权盈利 − 永续亏损(有符号相加);期权须强平",
            },
            {
                "id": "flat",
                "label": "到期·现价附近",
                "spot": spot,
                "perp_pnl": 0.0,
                "options_pnl": round(opt_flat, 4),
                "total": round(flat_total, 4),
                "note": "示意:永续未动,期权按到期内在",
            },
            {
                "id": "expiry_tp",
                "label": "到期·止盈价",
                "spot": tp,
                "perp_pnl": round(pnl_tp_perp, 4),
                "options_pnl": round(opt_at_tp, 4),
                "total": round(pnl_tp_perp + opt_at_tp, 4),
                "note": "若期权拿到 TP 价到期(参考)",
            },
            {
                "id": "expiry_sl",
                "label": "到期·止损价",
                "spot": sl,
                "perp_pnl": round(pnl_sl_perp, 4),
                "options_pnl": round(opt_at_sl, 4),
                "total": round(pnl_sl_perp + opt_at_sl, 4),
                "note": "与止损口径相近(期权用内在)",
            },
        ],
        "summary": {
            "tp_total": round(tp_total, 4),
            "sl_total": round(sl_total_signed, 4),
            "premium_paid": round(float(premium_paid), 4),
            "hedge_ratio_at_sl": _hedge_ratio(opt_at_sl, pnl_sl_perp),
        },
    }


def _hedge_ratio(opt_pnl: float, perp_pnl: float) -> Optional[float]:
    loss = abs(float(perp_pnl)) if float(perp_pnl) < 0 else 0.0
    if loss <= 1e-12:
        return None
    if float(opt_pnl) <= 0:
        return 0.0
    return round(float(opt_pnl) / loss * 100.0, 2)


def build_options_options_preview(
    *,
    target_price: float | None = None,
    target_price_up: float | None = None,
    target_price_down: float | None = None,
    index_px: float,
    leg_a: dict[str, Any],
    leg_b: dict[str, Any],
) -> dict[str, Any]:
    """期期情景:上破/下破目标价 / 到期现价 / 最大保费损耗."""

    def _leg_pnl(leg: dict[str, Any], spot: float) -> float:
        return option_expiry_pnl(
            opt_type=str(leg.get("opt_type") or ""),
            strike=float(leg["strike"]),
            spot=spot,
            sheets=float(leg.get("sheets") or 0),
            ct_mult=float(leg.get("ct_mult") or 0.01),
            premium_paid=float(leg.get("premium_paid") or 0),
        )

    # 兼容旧单目标:若未传上下目标则用 target_price 填两边
    up = target_price_up if target_price_up is not None else target_price
    down = target_price_down if target_price_down is not None else target_price
    if up is None or down is None:
        raise ValueError("缺少上破/下破目标价")
    up_f = float(up)
    down_f = float(down)

    prem = float(leg_a.get("premium_paid") or 0) + float(leg_b.get("premium_paid") or 0)
    a_up = _leg_pnl(leg_a, up_f)
    b_up = _leg_pnl(leg_b, up_f)
    at_up = a_up + b_up
    win_up = "a" if a_up >= b_up else "b"

    a_dn = _leg_pnl(leg_a, down_f)
    b_dn = _leg_pnl(leg_b, down_f)
    at_dn = a_dn + b_dn
    win_dn = "a" if a_dn >= b_dn else "b"

    a_flat = _leg_pnl(leg_a, index_px)
    b_flat = _leg_pnl(leg_b, index_px)
    flat_total = a_flat + b_flat
    expiry_loss = flat_total if flat_total <= 0 else flat_total

    return {
        "plan_type": "options_options",
        "premium_paid": round(prem, 6),
        "target_price": up_f,  # 兼容旧字段,取上破
        "target_price_up": up_f,
        "target_price_down": down_f,
        "winner_at_up": win_up,
        "winner_at_down": win_dn,
        "winner_at_target": win_up,
        "scenarios": [
            {
                "id": "target_up",
                "label": "上破目标",
                "spot": up_f,
                "leg_a_pnl": round(a_up, 4),
                "leg_b_pnl": round(b_up, 4),
                "total": round(at_up, 4),
                "note": f"盈利方≈腿{win_up.upper()}(可平);亏损方默认到期",
            },
            {
                "id": "target_down",
                "label": "下破目标",
                "spot": down_f,
                "leg_a_pnl": round(a_dn, 4),
                "leg_b_pnl": round(b_dn, 4),
                "total": round(at_dn, 4),
                "note": f"盈利方≈腿{win_dn.upper()}(可平);亏损方默认到期",
            },
            {
                "id": "expiry_flat",
                "label": "到期·现价(无突破)",
                "spot": index_px,
                "leg_a_pnl": round(a_flat, 4),
                "leg_b_pnl": round(b_flat, 4),
                "total": round(flat_total, 4),
                "note": "无盈利则记总亏损结束" if flat_total <= 0 else "到期仍可能有净值",
            },
            {
                "id": "max_premium_loss",
                "label": "最大保费损耗",
                "spot": None,
                "leg_a_pnl": round(-float(leg_a.get("premium_paid") or 0), 4),
                "leg_b_pnl": round(-float(leg_b.get("premium_paid") or 0), 4),
                "total": round(-prem, 4),
                "note": "双腿权利金全部损失",
            },
        ],
        "summary": {
            "at_target_up_total": round(at_up, 4),
            "at_target_down_total": round(at_dn, 4),
            "at_target_total": round(at_up, 4),
            "expiry_flat_total": round(expiry_loss, 4),
            "premium_paid": round(prem, 6),
            "expiry_is_loss": flat_total <= 0,
        },
    }


def gate_status(
    *,
    hedge_enabled: bool,
    sizing_mode: str,
    plan_type: str,
    options_enabled: bool,
    live_order: bool = False,
    live_trading: bool = False,
    active_count: int = 0,
    max_active: int = 1,
) -> dict[str, Any]:
    from lib.trade.position_sizing_lib import is_full_margin_mode

    full = is_full_margin_mode(sizing_mode)
    pt = (plan_type or "").strip().lower()
    can_preview = True
    can_start = True
    reasons: list[str] = []
    if not hedge_enabled:
        can_start = False
        reasons.append("对冲计划未启用(HEDGE_PLAN_ENABLED)")
    if not options_enabled:
        can_preview = False
        can_start = False
        reasons.append("期权模块未启用")
    if not live_order:
        can_start = False
        reasons.append("未允许对冲真实下单(HEDGE_PLAN_LIVE_ORDER)")
    if active_count >= max(1, int(max_active or 1)):
        can_start = False
        reasons.append(f"活跃计划已达上限({max_active})")
    if pt == "perp_options":
        if not full:
            can_start = False
            reasons.append("永期开仓仅全仓模式可用(当前可测算)")
        if not live_trading:
            can_start = False
            reasons.append("未开启实盘(LIVE_TRADING_ENABLED)")
    elif pt == "options_options":
        pass
    else:
        can_start = False
        reasons.append("未知计划类型")
    if can_start:
        reasons = []
    return {
        "hedge_enabled": hedge_enabled,
        "options_enabled": options_enabled,
        "sizing_mode": sizing_mode,
        "is_full_margin": full,
        "plan_type": pt,
        "live_order": live_order,
        "live_trading": live_trading,
        "active_count": active_count,
        "max_active": max_active,
        "can_preview": can_preview,
        "can_start": can_start,
        "reasons": reasons,
    }
