"""OKX：交易账户 USDC 不足时市价 USDT→USDC（目标=期权所需×2）。

仅 OKX SIM/LIVE。资金账户不参与；期权已可开则跳过。
等待选约阶段只用预览名义检测（不落库）；落库定仓在 open_pipeline.size_and_gate。
"""

from __future__ import annotations

import logging
import math
import time
from typing import Any

from ..config import get_settings
from ..models.db import Database, get_db
from .open_capacity import assess_open_capacity, invalidate_live_balance_cache

logger = logging.getLogger(__name__)

_TARGET_MULTIPLE = 2.0
_MIN_CONVERT_USDT = 1.0
_RETRY_COOLDOWN_SEC = 45.0
_last_attempt_ts: float = 0.0


def _is_okx(exchange: str | None = None) -> bool:
    from ..exchange.runtime import load_runtime_settings, normalize_exchange_name

    ex = exchange
    if not ex:
        try:
            ex = load_runtime_settings().exchange
        except Exception:
            ex = get_settings().exchange
    return normalize_exchange_name(ex) == "okx"


def _round_down(n: float, nd: int = 2) -> float:
    if n <= 0:
        return 0.0
    f = 10**nd
    return math.floor(n * f + 1e-12) / f


def preview_capacity_for_convert(db: Database | None = None) -> dict[str, Any]:
    """
    用盘口预览以损定仓名义评估资金门（不写 settings）。
    手动仓位则直接 assess 当前账本名义。
    """
    database = db or get_db()
    try:
        from .risk_sizing import preview_risk_sizing

        prev = preview_risk_sizing(database)
        if prev.get("risk_based") and prev.get("ok"):
            return assess_open_capacity(
                database,
                option_ask=float(prev["option_ask"])
                if prev.get("option_ask") is not None
                else None,
                option_qty_eth=float(prev["option_qty_eth"])
                if prev.get("option_qty_eth") is not None
                else None,
                perp_qty_eth=float(prev["perp_qty_eth"])
                if prev.get("perp_qty_eth") is not None
                else None,
            )
    except Exception:
        logger.exception("preview capacity for convert failed")
    return assess_open_capacity(database)


def prepare_okx_trading_usdc(db: Database | None = None) -> dict[str, Any]:
    """等待阶段：预览名义评估 + 兑换，不落库。"""
    database = db or get_db()
    cap = preview_capacity_for_convert(database)
    conv = ensure_okx_trading_usdc(database, cap=cap, force=False)
    return {"capacity": cap, "convert": conv}


def ensure_okx_trading_usdc(
    db: Database | None = None,
    *,
    cap: dict[str, Any] | None = None,
    force: bool = False,
) -> dict[str, Any]:
    """
    - 非 OKX → 跳过
    - 期权可开（USDC≥需）→ 跳过
    - 否则交易账户市价 USDT→USDC，尽量补到 需×2（预留永续保证金）
    - force 不再绕过冷却（防砸单）；保留参数仅为兼容调用方
    """
    global _last_attempt_ts
    _ = force  # 明确忽略：冷却始终生效
    db = db or get_db()
    out: dict[str, Any] = {
        "ok": True,
        "acted": False,
        "skipped": True,
        "detail": "skip",
    }
    if not _is_okx():
        out["detail"] = "非 OKX，跳过自动兑 USDC"
        return out

    cap = cap or assess_open_capacity(db)
    need = cap.get("option_need_usdc")
    have = cap.get("option_have_usdc")
    if need is None or have is None:
        out["detail"] = "期权所需/持有未知，跳过兑换"
        out["capacity"] = cap
        return out

    need_f = float(need)
    have_f = float(have)
    if need_f <= 0:
        out["detail"] = "期权所需为 0，跳过"
        return out

    if have_f + 1e-9 >= need_f:
        out["detail"] = (
            f"交易账户 USDC 已够开仓（有 {have_f:.2f} ≥ 需 {need_f:.2f}），不兑换"
        )
        out["capacity"] = cap
        return out

    now = time.time()
    if _last_attempt_ts > 0 and now - _last_attempt_ts < _RETRY_COOLDOWN_SEC:
        left = _RETRY_COOLDOWN_SEC - (now - _last_attempt_ts)
        out["detail"] = f"USDC 不足，自动兑换冷却中（{left:.0f}s）"
        out["capacity"] = cap
        out["need_usdc"] = round(need_f, 2)
        out["have_usdc"] = round(have_f, 2)
        return out

    target = need_f * _TARGET_MULTIPLE
    gap_usdc = target - have_f
    if gap_usdc <= 1e-6:
        out["detail"] = "无需补足"
        return out

    from ..live.okx_funds import usdc_usdt_mid_rate

    rate = float(usdc_usdt_mid_rate() or 1.0)
    if rate <= 0:
        rate = 1.0
    want_usdt = gap_usdc * rate

    perp_need = float(cap.get("perp_need_usdt") or 0)
    trading_usdt = float(cap.get("perp_have_usdt") or 0)
    spendable = max(0.0, trading_usdt - max(0.0, perp_need))
    spend_usdt = _round_down(min(want_usdt, spendable), 2)

    out.update(
        {
            "need_usdc": round(need_f, 2),
            "have_usdc": round(have_f, 2),
            "target_usdc": round(target, 2),
            "want_usdt": round(want_usdt, 2),
            "spend_usdt": spend_usdt,
            "rate": rate,
            "spendable_usdt": round(spendable, 2),
        }
    )

    if spend_usdt < _MIN_CONVERT_USDT:
        out["ok"] = False
        out["skipped"] = True
        out["detail"] = (
            f"交易账户可兑 USDT 不足（可兑 {spendable:.2f}，"
            f"目标补约 {want_usdt:.2f}，门槛 {_MIN_CONVERT_USDT}）"
        )
        out["capacity"] = cap
        return out

    _last_attempt_ts = now
    s = get_settings()
    try:
        if s.is_sim:
            from ..sim.funds_wallets import SimFundsWallets

            r = SimFundsWallets(db).convert(
                direction="usdt_to_usdc",
                amount=spend_usdt,
                rate=rate,
                account="trading",
            )
        else:
            from ..live.okx_funds import OkxFundsClient

            client = OkxFundsClient()
            try:
                r = client.spot_swap_usdt_usdc(
                    direction="usdt_to_usdc",
                    amount=spend_usdt,
                )
            finally:
                client.close()
            invalidate_live_balance_cache()
    except Exception as e:
        logger.exception("auto USDC convert failed")
        out["ok"] = False
        out["skipped"] = False
        out["acted"] = False
        out["detail"] = f"自动兑换异常：{e}"
        return out

    if not r.get("ok"):
        out["ok"] = False
        out["skipped"] = False
        out["acted"] = False
        out["detail"] = f"自动兑换失败：{r.get('detail') or r}"
        out["raw"] = r
        logger.warning("auto_usdc failed: %s", out["detail"])
        return out

    invalidate_live_balance_cache()
    cap2 = assess_open_capacity(db)
    out.update(
        {
            "ok": True,
            "acted": True,
            "skipped": False,
            "detail": (
                f"交易账户市价兑 USDC：花 {spend_usdt:.2f} USDT"
                f"（目标持仓≈{target:.2f}=需{need_f:.2f}×{_TARGET_MULTIPLE:g}）"
            ),
            "capacity_before": cap,
            "capacity_after": cap2,
            "raw": r,
        }
    )
    logger.info("auto_usdc: %s", out["detail"])
    return out
