"""中控永期对冲计算器:永续 1 币 + 按目标盈利反推期权仓位/波动点数(纯函数)."""
from __future__ import annotations

from typing import Any, Optional, Tuple

from lib.trade.trade_fee_lib import estimate_roundtrip_fee_usdt, taker_fee_rate

DEFAULT_CT_MULT = 0.01
PERP_COINS = 1.0


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


def _parse_base_common(
    *,
    base: str,
    spot: Any,
    capital_usdt: Any,
    target_profit_u: Any,
    perp_leverage: Any,
    option_leverage: Any,
    ct_mult: Any,
) -> Tuple[Optional[dict[str, float]], Optional[str]]:
    b = (base or "ETH").strip().upper()
    if b not in ("ETH", "BTC"):
        return None, "币种仅支持 BTC / ETH"
    s = _f(spot)
    capital = _f(capital_usdt)
    target = _f(target_profit_u)
    p_lev = _f(perp_leverage)
    o_lev = _f(option_leverage)
    ct = _f(ct_mult)
    if s is None or capital is None or target is None or p_lev is None or o_lev is None:
        return None, "参数格式错误"
    if ct is None or ct <= 0:
        ct = DEFAULT_CT_MULT
    if s <= 0 or capital <= 0 or p_lev <= 0 or o_lev <= 0:
        return None, "现价、资金、杠杆须大于 0"
    if target < 0:
        return None, "目标盈利不能为负"
    prem_per_coin = s / o_lev
    if prem_per_coin <= 0:
        return None, "单币权利金无效"
    margin = (s * PERP_COINS) / p_lev
    return {
        "base_ok": 1.0,
        "spot": s,
        "capital": capital,
        "target": target,
        "p_lev": p_lev,
        "o_lev": o_lev,
        "ct": ct,
        "prem_per_coin": prem_per_coin,
        "margin": margin,
        "fee_rate": taker_fee_rate(),
    }, None


def _move_for_perp_correct(
    *,
    spot: float,
    target: float,
    premium: float,
    fee_rate: float,
    perp_coins: float = 1.0,
) -> float:
    """净利 = qty*move − premium − fee(move,qty) = target → 解 move.

    fee = (2*spot + move) * qty * fee_rate
    qty*move*(1-fee_rate) = target + premium + 2*spot*qty*fee_rate
    """
    qty = float(perp_coins)
    if qty <= 0:
        return 0.0
    denom = qty * (1.0 - float(fee_rate))
    if denom <= 0:
        return 0.0
    return (float(target) + float(premium) + 2.0 * float(spot) * qty * float(fee_rate)) / denom


def _case_sideways(
    *,
    spot: float,
    premium_total: float,
    perp_coins: float = 1.0,
) -> dict[str, Any]:
    """横盘/到期无方向:永续≈0,期权权利金全亏,另计永续开平同价手续费.

    最大亏损(正数) = 权利金总额 + 开平手续费(exit=entry)
    组合净利 = −最大亏损
    """
    qty = float(perp_coins) if float(perp_coins) > 0 else PERP_COINS
    fee_flat = estimate_roundtrip_fee_usdt(spot, spot, qty=qty, contract_size=1.0)
    prem = float(premium_total)
    max_loss = prem + float(fee_flat)
    return {
        "label": "横盘",
        "perp_pnl_u": 0.0,
        "premium_u": round(prem, 8),
        "fee_u": round(float(fee_flat), 8),
        "max_loss_u": round(max_loss, 8),
        "net_u": round(-max_loss, 8),
    }


def calc_perp_options_hedge(
    *,
    base: str = "ETH",
    spot: float,
    capital_usdt: float,
    target_profit_u: float,
    move_mode: str = "points",
    move_value: float,
    perp_leverage: float,
    option_leverage: float,
    ct_mult: float = DEFAULT_CT_MULT,
) -> Tuple[Optional[dict[str, Any]], Optional[str]]:
    """由波动反推期权开仓币数/张数(calc_mode=size)."""
    common, err = _parse_base_common(
        base=base,
        spot=spot,
        capital_usdt=capital_usdt,
        target_profit_u=target_profit_u,
        perp_leverage=perp_leverage,
        option_leverage=option_leverage,
        ct_mult=ct_mult,
    )
    if err or not common:
        return None, err

    s = common["spot"]
    capital = common["capital"]
    target = common["target"]
    p_lev = common["p_lev"]
    o_lev = common["o_lev"]
    ct = common["ct"]
    prem_per_coin = common["prem_per_coin"]
    margin = common["margin"]
    fee_rate = common["fee_rate"]
    b = (base or "ETH").strip().upper()

    move = _f(move_value)
    mode = (move_mode or "points").strip().lower()
    if mode not in ("points", "pct", "percent", "rate"):
        return None, "波动模式须为 points 或 pct"
    if mode in ("percent", "rate"):
        mode = "pct"
    if move is None:
        return None, "参数格式错误"
    if move <= 0:
        return None, "现价、资金、波动、杠杆须大于 0"

    if mode == "pct":
        move_points = s * (move / 100.0)
    else:
        move_points = move
    if move_points <= 0:
        return None, "波动对应价格变动须大于 0"

    exit_px = s + move_points
    perp_gross = move_points * PERP_COINS
    fee = estimate_roundtrip_fee_usdt(s, exit_px, qty=PERP_COINS, contract_size=1.0)

    premium_budget = perp_gross - target - fee
    if premium_budget <= 0:
        return None, "波动收益不足以覆盖目标盈利+手续费，无法开期权"

    opt_coins = premium_budget / prem_per_coin
    opt_sheets = opt_coins / ct
    premium_total = opt_coins * prem_per_coin

    case_a_net = perp_gross - premium_total - fee
    opt_intrinsic = opt_coins * move_points
    opt_net = opt_intrinsic - premium_total
    perp_loss = -perp_gross
    portfolio_net = opt_net + perp_loss

    return {
        "calc_mode": "size",
        "base": b,
        "spot": round(s, 8),
        "capital_usdt": round(capital, 8),
        "target_profit_u": round(target, 8),
        "move_mode": mode,
        "move_value": round(move, 8),
        "move_points": round(move_points, 8),
        "exit_price": round(exit_px, 8),
        "perp_coins": PERP_COINS,
        "perp_leverage": round(p_lev, 8),
        "option_leverage": round(o_lev, 8),
        "ct_mult": ct,
        "prem_per_coin": round(prem_per_coin, 8),
        "perp_gross_u": round(perp_gross, 8),
        "perp_fee_u": round(fee, 8),
        "fee_rate": fee_rate,
        "premium_budget_u": round(premium_budget, 8),
        "opt_coins": round(opt_coins, 8),
        "opt_sheets": round(opt_sheets, 8),
        "premium_total_u": round(premium_total, 8),
        "perp_margin_u": round(margin, 8),
        "capital_ok": bool(capital >= margin),
        "case_a": {
            "label": "永续方向对",
            "perp_pnl_u": round(perp_gross, 8),
            "premium_u": round(premium_total, 8),
            "fee_u": round(fee, 8),
            "net_u": round(case_a_net, 8),
        },
        "case_b": {
            "label": "期权方向对",
            "opt_intrinsic_u": round(opt_intrinsic, 8),
            "premium_u": round(premium_total, 8),
            "opt_net_u": round(opt_net, 8),
            "perp_pnl_u": round(perp_loss, 8),
            "portfolio_net_u": round(portfolio_net, 8),
        },
        "case_sideways": _case_sideways(spot=s, premium_total=premium_total),
    }, None


def calc_perp_options_points(
    *,
    base: str = "ETH",
    spot: float,
    capital_usdt: float,
    target_profit_u: float,
    perp_leverage: float,
    option_leverage: float,
    ratio_perp: float = 1.0,
    ratio_opt: float = 2.0,
    ct_mult: float = DEFAULT_CT_MULT,
) -> Tuple[Optional[dict[str, Any]], Optional[str]]:
    """按永续/期权币数 + 目标盈利,反推两套情景所需波动点数.

    永续币数 = ratio_perp, 期权币数 = ratio_opt(按绝对币数,不再归一到 1 币).
    例 2:4 → 永续 2 币 + 期权 4 币;1:2 → 永续 1 币 + 期权 2 币.

    A 永续方向对: qty*move − premium − fee(move,qty) = 目标盈利
    B 期权方向对:
      - 期权净利达目标: opt_coins*move − premium = 目标
      - 组合净利达目标: move*(opt_coins − perp_coins) − premium = 目标
    """
    common, err = _parse_base_common(
        base=base,
        spot=spot,
        capital_usdt=capital_usdt,
        target_profit_u=target_profit_u,
        perp_leverage=perp_leverage,
        option_leverage=option_leverage,
        ct_mult=ct_mult,
    )
    if err or not common:
        return None, err

    rp = _f(ratio_perp)
    ro = _f(ratio_opt)
    if rp is None or ro is None or rp <= 0 or ro <= 0:
        return None, "永续/期权币数须大于 0"

    s = common["spot"]
    capital = common["capital"]
    target = common["target"]
    p_lev = common["p_lev"]
    o_lev = common["o_lev"]
    ct = common["ct"]
    prem_per_coin = common["prem_per_coin"]
    fee_rate = common["fee_rate"]
    b = (base or "ETH").strip().upper()

    perp_coins = rp
    opt_coins = ro
    premium_total = opt_coins * prem_per_coin
    opt_sheets = opt_coins / ct
    margin = (s * perp_coins) / p_lev

    move_a = _move_for_perp_correct(
        spot=s,
        target=target,
        premium=premium_total,
        fee_rate=fee_rate,
        perp_coins=perp_coins,
    )
    if move_a <= 0:
        return None, "无法解出永续方向对所需点数"

    fee_a = estimate_roundtrip_fee_usdt(s, s + move_a, qty=perp_coins, contract_size=1.0)
    net_a = move_a * perp_coins - premium_total - fee_a

    # 期权净利 = 目标
    move_b_opt = (target + premium_total) / opt_coins
    opt_net_at_b_opt = opt_coins * move_b_opt - premium_total
    portfolio_at_b_opt = opt_net_at_b_opt - move_b_opt * perp_coins

    # 组合净利 = 目标
    edge = opt_coins - perp_coins
    if edge <= 0:
        move_b_port = None
        port_err = "期权币数须大于永续币数,组合才能在方向对时赚到目标盈利"
    else:
        move_b_port = (target + premium_total) / edge
        port_err = None
    if move_b_port is not None:
        opt_net_at_b_port = opt_coins * move_b_port - premium_total
        portfolio_at_b_port = opt_net_at_b_port - move_b_port * perp_coins
    else:
        opt_net_at_b_port = None
        portfolio_at_b_port = None

    return {
        "calc_mode": "points",
        "base": b,
        "spot": round(s, 8),
        "capital_usdt": round(capital, 8),
        "target_profit_u": round(target, 8),
        "ratio_perp": round(rp, 8),
        "ratio_opt": round(ro, 8),
        "ratio_label": f"{_fmt_ratio(rp)}:{_fmt_ratio(ro)}",
        "perp_coins": round(perp_coins, 8),
        "opt_coins": round(opt_coins, 8),
        "opt_sheets": round(opt_sheets, 8),
        "perp_leverage": round(p_lev, 8),
        "option_leverage": round(o_lev, 8),
        "ct_mult": ct,
        "prem_per_coin": round(prem_per_coin, 8),
        "premium_total_u": round(premium_total, 8),
        "fee_rate": fee_rate,
        "perp_margin_u": round(margin, 8),
        "capital_ok": bool(capital >= margin),
        "case_a": {
            "label": "永续方向对",
            "move_points": round(move_a, 8),
            "move_pct": round(move_a / s * 100.0, 8),
            "perp_pnl_u": round(move_a * perp_coins, 8),
            "premium_u": round(premium_total, 8),
            "fee_u": round(fee_a, 8),
            "net_u": round(net_a, 8),
        },
        "case_b": {
            "label": "期权方向对",
            "move_points_opt_net": round(move_b_opt, 8),
            "move_pct_opt_net": round(move_b_opt / s * 100.0, 8),
            "opt_net_u": round(opt_net_at_b_opt, 8),
            "portfolio_net_at_opt_target_u": round(portfolio_at_b_opt, 8),
            "move_points_portfolio": None if move_b_port is None else round(move_b_port, 8),
            "move_pct_portfolio": None
            if move_b_port is None
            else round(move_b_port / s * 100.0, 8),
            "opt_net_at_portfolio_target_u": None
            if opt_net_at_b_port is None
            else round(opt_net_at_b_port, 8),
            "portfolio_net_u": None if portfolio_at_b_port is None else round(portfolio_at_b_port, 8),
            "portfolio_error": port_err,
            "premium_u": round(premium_total, 8),
        },
        "case_sideways": _case_sideways(
            spot=s, premium_total=premium_total, perp_coins=perp_coins
        ),
    }, None


def _fmt_ratio(v: float) -> str:
    if abs(v - round(v)) < 1e-9:
        return str(int(round(v)))
    s = f"{v:.4f}".rstrip("0").rstrip(".")
    return s


def calc_perp_options(
    *,
    calc_mode: str = "size",
    **kwargs: Any,
) -> Tuple[Optional[dict[str, Any]], Optional[str]]:
    """统一入口:size=由波动推仓位;points=由比例推点数."""
    mode = (calc_mode or "size").strip().lower()
    if mode in ("points", "ratio", "move"):
        return calc_perp_options_points(**kwargs)
    # size mode: ignore ratio kwargs if present
    kwargs.pop("ratio_perp", None)
    kwargs.pop("ratio_opt", None)
    return calc_perp_options_hedge(**kwargs)
