"""OKX 期权开仓/平仓企业微信推送(必发,幂等落库标记).支持 USDC / 币本位(ETH/BTC)."""
from __future__ import annotations

import sqlite3
from typing import Any, Callable, Optional


def _fmt(v: Any, d: int = 4) -> str:
    try:
        if v is None or v == "":
            return "—"
        return f"{float(v):.{d}f}"
    except (TypeError, ValueError):
        return str(v)


def _opt_type_label(opt_type: Any) -> str:
    t = str(opt_type or "").strip().upper()
    if t in ("C", "CALL"):
        return "Call"
    if t in ("P", "PUT"):
        return "Put"
    return t or "—"


def resolve_options_premium_ccy(
    *,
    inst_id: str = "",
    underlying: str = "",
    premium_ccy: Any = None,
    margin_mode: Any = None,
    row: dict[str, Any] | None = None,
) -> str:
    """权利金计价币种:USDC 或 ETH/BTC."""
    raw = premium_ccy
    if (raw is None or str(raw).strip() == "") and row:
        raw = row.get("premium_ccy")
    ccy = str(raw or "").strip().upper()
    if ccy:
        return ccy
    try:
        from lib.options.options_margin_mode_lib import margin_mode_from_inst_id, premium_ccy_for_mode

        mid = str(inst_id or (row or {}).get("inst_id") or "").strip()
        mode = margin_mode if margin_mode is not None else (row or {}).get("margin_mode")
        if mode is None and mid:
            mode = margin_mode_from_inst_id(mid)
        u = str(
            underlying
            or (row or {}).get("underlying")
            or (mid.split("-")[0] if mid else "ETH")
            or "ETH"
        ).strip().upper() or "ETH"
        return premium_ccy_for_mode(str(mode or "usdc"), u)
    except Exception:
        return "USDC"


def _amount_decimals(ccy: str) -> int:
    c = (ccy or "USDC").strip().upper()
    if c in ("ETH", "BTC"):
        return 6
    return 4


def _mode_tag(*, inst_id: str = "", premium_ccy: str = "", margin_mode: Any = None) -> str:
    ccy = (premium_ccy or "").strip().upper()
    if ccy and ccy != "USDC":
        return "币本位"
    try:
        from lib.options.options_margin_mode_lib import is_coin_margin_mode, margin_mode_from_inst_id

        mode = margin_mode
        if mode is None and inst_id:
            mode = margin_mode_from_inst_id(inst_id)
        if is_coin_margin_mode(mode):
            return "币本位"
    except Exception:
        pass
    return "USDC"


def ensure_options_notify_columns(conn: sqlite3.Connection) -> None:
    for ddl in (
        "ALTER TABLE options_trades ADD COLUMN wechat_open_sent INTEGER DEFAULT 0",
        "ALTER TABLE options_trades ADD COLUMN wechat_close_sent INTEGER DEFAULT 0",
    ):
        try:
            conn.execute(ddl)
        except Exception:
            pass


def notify_options_send(cfg: dict[str, Any], content: str) -> bool:
    send: Optional[Callable[[str], Any]] = cfg.get("send_wechat")
    if not callable(send):
        return False
    try:
        send(content)
        return True
    except Exception:
        return False


def build_options_open_message(
    *,
    account_label: str,
    inst_id: str,
    underlying: str = "",
    opt_type: Any = None,
    sheets: Any = None,
    premium_paid: Any = None,
    open_quote: Any = None,
    target_index: Any = None,
    signal_note: str = "",
    trade_id: Any = None,
    premium_ccy: Any = None,
    margin_mode: Any = None,
) -> str:
    ccy = resolve_options_premium_ccy(
        inst_id=inst_id,
        underlying=underlying,
        premium_ccy=premium_ccy,
        margin_mode=margin_mode,
    )
    d = _amount_decimals(ccy)
    mode = _mode_tag(inst_id=inst_id, premium_ccy=ccy, margin_mode=margin_mode)
    lines = [
        "【OKX期权·开仓】",
        f"账户:{account_label or 'OKX期权'}",
        f"本位:{mode}",
    ]
    if trade_id is not None:
        lines.append(f"本地单号:#{trade_id}")
    lines.extend(
        [
            f"合约:{inst_id}",
            f"标的:{(underlying or '—')} · {_opt_type_label(opt_type)}",
            f"张数:{sheets if sheets is not None else '—'}",
            f"开仓报价:{_fmt(open_quote, d)} {ccy}",
            f"权利金:{_fmt(premium_paid, d)} {ccy}",
        ]
    )
    if target_index is not None and str(target_index).strip() != "":
        try:
            lines.append(f"目标指数:{float(target_index):g}")
        except (TypeError, ValueError):
            lines.append(f"目标指数:{target_index}")
    if signal_note:
        lines.append(f"备注:{signal_note[:200]}")
    return "\n".join(lines)


def build_options_close_message(
    *,
    account_label: str,
    inst_id: str,
    reason: str = "",
    underlying: str = "",
    opt_type: Any = None,
    sheets: Any = None,
    premium_paid: Any = None,
    premium_received: Any = None,
    realized_pnl: Any = None,
    close_quote: Any = None,
    target_index: Any = None,
    trigger_idx: Any = None,
    trade_id: Any = None,
    premium_ccy: Any = None,
    margin_mode: Any = None,
) -> str:
    ccy = resolve_options_premium_ccy(
        inst_id=inst_id,
        underlying=underlying,
        premium_ccy=premium_ccy,
        margin_mode=margin_mode,
    )
    d = _amount_decimals(ccy)
    mode = _mode_tag(inst_id=inst_id, premium_ccy=ccy, margin_mode=margin_mode)
    lines = [
        "【OKX期权·平仓】",
        f"账户:{account_label or 'OKX期权'}",
        f"本位:{mode}",
    ]
    if trade_id is not None:
        lines.append(f"本地单号:#{trade_id}")
    lines.extend(
        [
            f"合约:{inst_id}",
            f"标的:{(underlying or '—')} · {_opt_type_label(opt_type)}",
            f"原因:{(reason or '平仓').strip()}",
            f"张数:{sheets if sheets is not None else '—'}",
            f"平仓报价:{_fmt(close_quote, d)} {ccy}",
            f"已付/收回:{_fmt(premium_paid, d)} / {_fmt(premium_received, d)} {ccy}",
            f"实现盈亏:{_fmt(realized_pnl, d)} {ccy}",
        ]
    )
    if target_index is not None and str(target_index).strip() != "":
        try:
            lines.append(f"目标指数:{float(target_index):g}")
        except (TypeError, ValueError):
            lines.append(f"目标指数:{target_index}")
    if trigger_idx is not None and str(trigger_idx).strip() != "":
        try:
            lines.append(f"触发指数:{float(trigger_idx):g}")
        except (TypeError, ValueError):
            lines.append(f"触发指数:{trigger_idx}")
    return "\n".join(lines)


def notify_options_open(
    cfg: dict[str, Any],
    conn: sqlite3.Connection | None,
    *,
    trade_id: int | None,
    inst_id: str,
    underlying: str = "",
    opt_type: Any = None,
    sheets: Any = None,
    premium_paid: Any = None,
    open_quote: Any = None,
    target_index: Any = None,
    signal_note: str = "",
    premium_ccy: Any = None,
    margin_mode: Any = None,
) -> bool:
    ensure_options_notify_columns(conn) if conn is not None else None
    row_ccy = premium_ccy
    row_mode = margin_mode
    if conn is not None and trade_id is not None:
        row = conn.execute(
            "SELECT wechat_open_sent, premium_ccy, margin_mode, underlying FROM options_trades WHERE id=?",
            (int(trade_id),),
        ).fetchone()
        if row and int(row["wechat_open_sent"] or 0):
            return False
        if row:
            if row_ccy is None:
                row_ccy = row["premium_ccy"] if "premium_ccy" in row.keys() else None
            if row_mode is None:
                row_mode = row["margin_mode"] if "margin_mode" in row.keys() else None
            if not underlying:
                underlying = str(row["underlying"] or "") if "underlying" in row.keys() else underlying
    msg = build_options_open_message(
        account_label=str(cfg.get("account_label") or "OKX期权"),
        inst_id=inst_id,
        underlying=underlying,
        opt_type=opt_type,
        sheets=sheets,
        premium_paid=premium_paid,
        open_quote=open_quote,
        target_index=target_index,
        signal_note=signal_note,
        trade_id=trade_id,
        premium_ccy=row_ccy,
        margin_mode=row_mode,
    )
    ok = notify_options_send(cfg, msg)
    if ok and conn is not None and trade_id is not None:
        conn.execute(
            "UPDATE options_trades SET wechat_open_sent=1 WHERE id=?",
            (int(trade_id),),
        )
        try:
            conn.commit()
        except Exception:
            pass
    return ok


def _load_trade_row(conn: sqlite3.Connection, trade_id: int) -> dict[str, Any] | None:
    row = conn.execute("SELECT * FROM options_trades WHERE id=?", (int(trade_id),)).fetchone()
    return dict(row) if row else None


def notify_options_close(
    cfg: dict[str, Any],
    conn: sqlite3.Connection | None,
    *,
    inst_id: str,
    reason: str = "平仓",
    trade_id: int | None = None,
    underlying: str = "",
    opt_type: Any = None,
    sheets: Any = None,
    premium_paid: Any = None,
    premium_received: Any = None,
    realized_pnl: Any = None,
    close_quote: Any = None,
    target_index: Any = None,
    trigger_idx: Any = None,
    premium_ccy: Any = None,
    margin_mode: Any = None,
    force: bool = False,
) -> bool:
    """平仓必发.默认按 trade_id / 同合约未标记行幂等."""
    if conn is not None:
        ensure_options_notify_columns(conn)
    rows: list[dict[str, Any]] = []
    if conn is not None and trade_id is not None:
        r = _load_trade_row(conn, int(trade_id))
        if r:
            rows = [r]
    elif conn is not None and inst_id:
        q = conn.execute(
            """
            SELECT * FROM options_trades
            WHERE inst_id=? AND status='closed'
              AND COALESCE(wechat_close_sent,0)=0
            ORDER BY id DESC
            LIMIT 20
            """,
            (inst_id,),
        ).fetchall()
        rows = [dict(x) for x in q]
        if not rows and force:
            q2 = conn.execute(
                """
                SELECT * FROM options_trades
                WHERE inst_id=? AND status='closed'
                ORDER BY id DESC LIMIT 1
                """,
                (inst_id,),
            ).fetchone()
            if q2:
                rows = [dict(q2)]
        if not rows:
            # 已有平仓记录且均已推送:幂等跳过,避免再走「无库行」重复推
            exists = conn.execute(
                """
                SELECT 1 FROM options_trades
                WHERE inst_id=? AND status='closed'
                LIMIT 1
                """,
                (inst_id,),
            ).fetchone()
            if exists:
                return False

    if rows:
        # 同次平仓可能多腿:合并一条推送,逐条标记
        total_paid = sum(float(r.get("premium_paid") or 0) for r in rows)
        total_recv = sum(float(r.get("premium_received") or 0) for r in rows if r.get("premium_received") is not None)
        pnls = [float(r["realized_pnl"]) for r in rows if r.get("realized_pnl") is not None]
        total_pnl = sum(pnls) if pnls else None
        if total_pnl is None and (premium_received is not None or realized_pnl is not None):
            total_pnl = realized_pnl
            total_recv = premium_received if premium_received is not None else total_recv
            total_paid = premium_paid if premium_paid is not None else total_paid
        head = rows[0]
        pending = [r for r in rows if not int(r.get("wechat_close_sent") or 0)]
        if not pending and not force:
            return False
        msg = build_options_close_message(
            account_label=str(cfg.get("account_label") or "OKX期权"),
            inst_id=inst_id or str(head.get("inst_id") or ""),
            reason=reason,
            underlying=underlying or str(head.get("underlying") or ""),
            opt_type=opt_type or head.get("opt_type"),
            sheets=sheets if sheets is not None else sum(int(r.get("sheets") or 0) for r in rows),
            premium_paid=total_paid,
            premium_received=total_recv if rows else premium_received,
            realized_pnl=total_pnl,
            close_quote=close_quote if close_quote is not None else head.get("close_quote"),
            target_index=target_index,
            trigger_idx=trigger_idx,
            trade_id=head.get("id") if len(rows) == 1 else None,
            premium_ccy=premium_ccy or head.get("premium_ccy"),
            margin_mode=margin_mode or head.get("margin_mode"),
        )
        ok = notify_options_send(cfg, msg)
        if ok and conn is not None:
            for r in pending or rows:
                conn.execute(
                    "UPDATE options_trades SET wechat_close_sent=1 WHERE id=?",
                    (int(r["id"]),),
                )
            try:
                conn.commit()
            except Exception:
                pass
        return ok

    # 无库行时仍发一条(尽量不丢提醒)
    msg = build_options_close_message(
        account_label=str(cfg.get("account_label") or "OKX期权"),
        inst_id=inst_id,
        reason=reason,
        underlying=underlying,
        opt_type=opt_type,
        sheets=sheets,
        premium_paid=premium_paid,
        premium_received=premium_received,
        realized_pnl=realized_pnl,
        close_quote=close_quote,
        target_index=target_index,
        trigger_idx=trigger_idx,
        trade_id=trade_id,
        premium_ccy=premium_ccy,
        margin_mode=margin_mode,
    )
    return notify_options_send(cfg, msg)


def notify_options_close_trade_ids(
    cfg: dict[str, Any],
    conn: sqlite3.Connection,
    trade_ids: list[int],
    *,
    reason: str,
) -> bool:
    ids = [int(x) for x in trade_ids if x is not None]
    if not ids:
        return False
    ensure_options_notify_columns(conn)
    placeholders = ",".join("?" for _ in ids)
    rows = conn.execute(
        f"""
        SELECT * FROM options_trades
        WHERE id IN ({placeholders}) AND COALESCE(wechat_close_sent,0)=0
        """,
        ids,
    ).fetchall()
    if not rows:
        return False
    first = dict(rows[0])
    return notify_options_close(
        cfg,
        conn,
        inst_id=str(first.get("inst_id") or ""),
        reason=reason,
        trade_id=int(first["id"]) if len(rows) == 1 else None,
        underlying=str(first.get("underlying") or ""),
        opt_type=first.get("opt_type"),
        sheets=sum(int(r["sheets"] or 0) for r in rows),
        premium_paid=sum(float(r["premium_paid"] or 0) for r in rows),
        premium_received=sum(float(r["premium_received"] or 0) for r in rows if r["premium_received"] is not None),
        realized_pnl=sum(float(r["realized_pnl"]) for r in rows if r["realized_pnl"] is not None),
        close_quote=first.get("close_quote"),
        premium_ccy=first.get("premium_ccy"),
        margin_mode=first.get("margin_mode"),
    )
