"""期权持仓展示(实例页 / 中控快照共用)."""
from __future__ import annotations

from typing import Any

from lib.options.options_db import init_options_tables, sum_open_premium_paid
from lib.options.options_history_lib import enrich_position_row_display
from lib.options.options_close_gate_lib import clear_close_gate, is_close_gate_passed, update_close_gate
from lib.options.options_pricing_lib import estimate_close_by_bids, intrinsic_px_per_unit


def _safe_float(v: Any) -> float | None:
    if v is None or v == "":
        return None
    try:
        return float(v)
    except (TypeError, ValueError):
        return None


def attach_close_preview(
    cfg: dict[str, Any],
    ex: Any,
    row: dict[str, Any],
    *,
    sheets: int | None = None,
    premium_paid: float | None = None,
) -> dict[str, Any]:
    inst_id = str(row.get("inst_id") or row.get("instId") or "").strip()
    if not inst_id:
        return row
    ct_mult = float(row.get("ct_mult") or 0.01)
    target_sheets = int(sheets) if sheets is not None else int(abs(_safe_float(row.get("pos")) or 0))
    paid = premium_paid if premium_paid is not None else _safe_float(row.get("premium_paid"))
    book = cfg["fetch_option_book_depth"](ex, inst_id, 5)
    row["bid_depth"] = book.get("bids") or []
    row["ask_depth"] = book.get("asks") or []
    mark_px = _safe_float(row.get("mark_px") or row.get("markPx"))
    intrinsic = intrinsic_px_per_unit(
        row.get("opt_type") or row.get("optType"),
        _safe_float(row.get("strike") or row.get("stk")),
        _safe_float(row.get("idx_px") or row.get("idxPx")),
    )
    # 与实盘一致:只按买一估算本轮可平
    preview = estimate_close_by_bids(
        row["bid_depth"],
        target_sheets,
        ct_mult=ct_mult,
        premium_paid=paid,
        mark_px=mark_px,
        intrinsic_px=intrinsic,
        max_levels=1,
    )
    # 残档时不累计 2×门控;有效买一时刷新计时(仅自动平仓需要)
    if preview.get("bid_invalid") or preview.get("auto_close_blocked"):
        gate = update_close_gate(inst_id, recycle_usdc=None, premium_paid=paid)
        preview["close_gate"] = gate
        preview["close_gate_blocked"] = True
        preview["close_gate_msg"] = preview.get("bid_invalid_reason") or gate.get("msg")
        preview["manual_close_blocked"] = True
        preview["liquidity_ok"] = False
    else:
        gate = update_close_gate(
            inst_id,
            recycle_usdc=_safe_float(preview.get("total_received")),
            premium_paid=paid,
        )
        passed = bool(gate.get("passed") or is_close_gate_passed(inst_id) or gate.get("ready"))
        preview["close_gate"] = gate
        preview["close_gate_blocked"] = not passed
        preview["close_gate_msg"] = gate.get("msg")
        preview["manual_close_blocked"] = False
        preview["liquidity_ok"] = True
        if not passed:
            preview["auto_close_blocked"] = True
    row["close_preview"] = preview
    return row


def forget_close_gate_for_inst(inst_id: str) -> None:
    clear_close_gate(inst_id)


def net_pnl_from_display_row(row: dict[str, Any]) -> float | None:
    """与持仓卡「净盈亏」同口径:买一可回收 − 权利金;残档买一则无净值."""
    preview = row.get("close_preview") if isinstance(row.get("close_preview"), dict) else {}
    if preview.get("bid_invalid"):
        return None
    net = preview.get("estimated_pnl")
    if net is not None:
        try:
            return float(net)
        except (TypeError, ValueError):
            pass
    recv = _safe_float(preview.get("total_received"))
    paid = _safe_float(row.get("premium_paid"))
    if recv is not None and paid is not None:
        return round(recv - paid, 4)
    return None


def sum_options_net_pnl_usdc(
    cfg: dict[str, Any],
    ex: Any,
    raw_positions: list[dict[str, Any]] | None = None,
) -> float | None:
    """
    期权浮盈合计(USDC),与顶栏实时盈亏/中控口径对齐为「净盈亏」:
    各仓买一可回收 − 权利金之和.获取失败返回 None;无持仓返回 0.
    """
    raw = raw_positions
    if raw is None:
        raw = cfg["fetch_option_positions"](ex)
    if raw is None:
        return None
    if not raw:
        return 0.0
    positions = build_display_option_positions(cfg, ex, raw)
    total = 0.0
    found = False
    for p in positions:
        net = net_pnl_from_display_row(p)
        if net is None:
            continue
        found = True
        total += float(net)
    return round(total, 4) if found else (0.0 if not positions else None)


def build_display_option_positions(
    cfg: dict[str, Any],
    ex: Any,
    raw_positions: list[dict[str, Any]],
) -> list[dict[str, Any]]:
    """与实例 /api/options/positions 相同 enrichment + close_preview."""
    meta_cache: dict[str, dict[str, Any] | None] = {}
    rows: list[dict[str, Any]] = []
    conn = cfg["get_db"]()
    try:
        init_options_tables(conn)
        for p in raw_positions:
            inst = str(p.get("instId") or "").strip()
            premium_override = sum_open_premium_paid(conn, inst) if inst else None
            row = enrich_position_row_display(
                cfg,
                ex,
                p,
                meta_cache=meta_cache,
                premium_override=premium_override,
            )
            attach_close_preview(cfg, ex, row, premium_paid=_safe_float(row.get("premium_paid")))
            rows.append(row)
    finally:
        conn.close()
    return rows
