"""期权目标位委托:指数目标价仅用于监控触发;触发后按买一限价平仓(无止损,到期结算)."""
from __future__ import annotations

import sqlite3
import time
from typing import Any, Callable

from lib.options.options_db import init_options_tables
from lib.options.options_pricing_lib import close_ref_prices, fetch_option_mark_px


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 _pos_close_refs(ex: Any, pos: dict[str, Any], quote: dict[str, Any] | None = None) -> tuple[float | None, float | None]:
    from lib.exchange.okx_options_lib import option_fields_from_inst_id

    inst_id = str(pos.get("instId") or pos.get("inst_id") or "")
    mark = _safe_float(pos.get("markPx")) or _safe_float((quote or {}).get("mark_px") or (quote or {}).get("mark"))
    if mark is None:
        mark = fetch_option_mark_px(ex, inst_id)
    opt_type = pos.get("optType") or (quote or {}).get("opt_type")
    strike = _safe_float(pos.get("stk")) or _safe_float((quote or {}).get("strike"))
    if not opt_type or strike is None:
        pt, ps = option_fields_from_inst_id(inst_id)
        opt_type = opt_type or pt
        if strike is None:
            strike = ps
    idx = _safe_float(pos.get("idxPx")) or _safe_float((quote or {}).get("index_px"))
    return close_ref_prices(mark_px=mark, opt_type=str(opt_type or ""), strike=strike, index_px=idx)


def ensure_target_tables(conn: sqlite3.Connection) -> None:
    init_options_tables(conn)
    conn.execute(
        """
        CREATE TABLE IF NOT EXISTS options_target_monitors (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            inst_id TEXT NOT NULL,
            underlying TEXT,
            opt_type TEXT,
            target_index REAL NOT NULL,
            trade_id INTEGER,
            sheets INTEGER,
            status TEXT DEFAULT 'active',
            trigger_idx REAL,
            close_ord_id TEXT,
            message TEXT,
            created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
            triggered_at TIMESTAMP
        )
        """
    )
    conn.execute(
        """
        CREATE INDEX IF NOT EXISTS idx_options_target_monitors_status
        ON options_target_monitors(status)
        """
    )


def target_hit(*, opt_type: str | None, index_px: float, target_index: float) -> bool:
    """Call:指数涨到/超过目标平仓;Put:指数跌到/低于目标平仓."""
    ot = (opt_type or "").strip().upper()
    if ot == "P":
        return index_px <= target_index
    return index_px >= target_index


def upsert_target_monitor(
    conn: sqlite3.Connection,
    *,
    inst_id: str,
    target_index: float,
    underlying: str | None = None,
    opt_type: str | None = None,
    trade_id: int | None = None,
    sheets: int | None = None,
) -> dict[str, Any]:
    ensure_target_tables(conn)
    inst_id = (inst_id or "").strip()
    if not inst_id:
        return {"ok": False, "msg": "缺少 inst_id"}
    if target_index is None or float(target_index) <= 0:
        return {"ok": False, "msg": "目标位无效"}
    target_index = float(target_index)
    row = conn.execute(
        """
        SELECT id FROM options_target_monitors
        WHERE inst_id = ? AND status IN ('active', 'closing')
        ORDER BY CASE status WHEN 'active' THEN 0 WHEN 'closing' THEN 1 ELSE 2 END, id DESC
        LIMIT 1
        """,
        (inst_id,),
    ).fetchone()
    if row:
        conn.execute(
            """
            UPDATE options_target_monitors
            SET target_index = ?,
                underlying = COALESCE(?, underlying),
                opt_type = COALESCE(?, opt_type),
                trade_id = COALESCE(?, trade_id),
                sheets = COALESCE(?, sheets),
                status = 'active',
                trigger_idx = NULL,
                close_ord_id = NULL,
                message = NULL,
                triggered_at = NULL
            WHERE id = ?
            """,
            (target_index, underlying, opt_type, trade_id, sheets, int(row["id"])),
        )
        mon_id = int(row["id"])
        # 同一合约其他进行中的委托取消,避免双轨触发重复推送
        conn.execute(
            """
            UPDATE options_target_monitors
            SET status = 'cancelled', message = '被新目标位覆盖'
            WHERE inst_id = ? AND id != ? AND status IN ('active', 'closing')
            """,
            (inst_id, mon_id),
        )
    else:
        cur = conn.execute(
            """
            INSERT INTO options_target_monitors
            (inst_id, underlying, opt_type, target_index, trade_id, sheets, status)
            VALUES (?, ?, ?, ?, ?, ?, 'active')
            """,
            (inst_id, underlying, opt_type, target_index, trade_id, sheets),
        )
        mon_id = int(cur.lastrowid)
    return {"ok": True, "id": mon_id, "inst_id": inst_id, "target_index": target_index}


def cancel_target_monitor(conn: sqlite3.Connection, *, inst_id: str | None = None, monitor_id: int | None = None) -> int:
    ensure_target_tables(conn)
    if monitor_id is not None:
        cur = conn.execute(
            """
            UPDATE options_target_monitors
            SET status = 'cancelled', message = '手动取消'
            WHERE id = ? AND status IN ('active', 'closing')
            """,
            (int(monitor_id),),
        )
        return int(cur.rowcount or 0)
    if inst_id:
        cur = conn.execute(
            """
            UPDATE options_target_monitors
            SET status = 'cancelled', message = '手动取消'
            WHERE inst_id = ? AND status IN ('active', 'closing')
            """,
            (inst_id.strip(),),
        )
        return int(cur.rowcount or 0)
    return 0


def _row_to_target(r: sqlite3.Row) -> dict[str, Any]:
    return {
        "id": int(r["id"]),
        "inst_id": r["inst_id"],
        "underlying": r["underlying"],
        "opt_type": r["opt_type"],
        "target_index": _safe_float(r["target_index"]),
        "trade_id": r["trade_id"],
        "sheets": r["sheets"],
        "status": r["status"],
        "message": r["message"],
        "created_at": r["created_at"],
    }


def list_active_targets(conn: sqlite3.Connection) -> list[dict[str, Any]]:
    ensure_target_tables(conn)
    rows = conn.execute(
        """
        SELECT id, inst_id, underlying, opt_type, target_index, trade_id, sheets,
               status, message, created_at
        FROM options_target_monitors
        WHERE status = 'active'
        ORDER BY id DESC
        """
    ).fetchall()
    return [_row_to_target(r) for r in rows]


def list_closing_targets(conn: sqlite3.Connection) -> list[dict[str, Any]]:
    """已挂出平仓单、等待成交的目标(不再重复推送微信)."""
    ensure_target_tables(conn)
    rows = conn.execute(
        """
        SELECT id, inst_id, underlying, opt_type, target_index, trade_id, sheets,
               status, message, created_at
        FROM options_target_monitors
        WHERE status = 'closing'
        ORDER BY id DESC
        """
    ).fetchall()
    return [_row_to_target(r) for r in rows]


def targets_by_inst(conn: sqlite3.Connection) -> dict[str, dict[str, Any]]:
    """UI/持仓挂载:active 与 closing 都算进行中."""
    out: dict[str, dict[str, Any]] = {}
    for t in list_closing_targets(conn) + list_active_targets(conn):
        inst = str(t.get("inst_id") or "")
        if inst and inst not in out:
            out[inst] = t
    return out


def mark_monitor(
    conn: sqlite3.Connection,
    monitor_id: int,
    *,
    status: str,
    trigger_idx: float | None = None,
    close_ord_id: str | None = None,
    message: str | None = None,
) -> None:
    conn.execute(
        """
        UPDATE options_target_monitors
        SET status = ?,
            trigger_idx = COALESCE(?, trigger_idx),
            close_ord_id = COALESCE(?, close_ord_id),
            message = COALESCE(?, message),
            triggered_at = CASE
                WHEN ? IN ('triggered', 'expired', 'closing') THEN COALESCE(triggered_at, CURRENT_TIMESTAMP)
                ELSE triggered_at
            END
        WHERE id = ?
        """,
        (status, trigger_idx, close_ord_id, message, status, int(monitor_id)),
    )


def cancel_orphans_without_position(
    conn: sqlite3.Connection,
    *,
    live_inst_ids: set[str],
) -> int:
    """持仓已消失的目标委托标记为 expired(到期/已平),不挂止损."""
    ensure_target_tables(conn)
    rows = list_active_targets(conn) + list_closing_targets(conn)
    n = 0
    for t in rows:
        inst = str(t.get("inst_id") or "")
        if inst and inst not in live_inst_ids:
            mark_monitor(conn, int(t["id"]), status="expired", message="持仓已平/到期,委托结束")
            n += 1
    return n


def _commit_monitor(conn: sqlite3.Connection) -> None:
    """状态变更立刻落库,避免后续 sync 异常回滚后重复触发/推送."""
    try:
        conn.commit()
    except Exception:
        pass


def close_option_by_bid_depth(
    cfg: dict[str, Any],
    ex: Any,
    inst_id: str,
    *,
    sheets: int | None = None,
) -> dict[str, Any]:
    """目标触发后只锁买一限价卖出;需过 2×门控(通过后同仓续批只验流动性)."""
    from lib.options.options_close_exec_lib import close_option_by_bid1

    return close_option_by_bid1(
        cfg,
        ex,
        inst_id,
        sheets=sheets,
        require_recycle_gate=True,
        signal_note="目标位平仓",
    )



def _notify_target_close(
    send_wechat: Callable[[str], None] | None,
    *,
    account_label: str,
    inst_id: str,
    target: float,
    idx: float,
    result: dict[str, Any],
) -> None:
    if not send_wechat:
        return
    try:
        send_wechat(
            "\n".join(
                [
                    "【OKX期权·目标位平仓】",
                    f"账户:{account_label}",
                    f"合约:{inst_id}",
                    f"目标指数:{target:g}",
                    f"触发指数:{idx:g}",
                    f"提交张数:{result.get('submitted_sheets') or '—'}",
                    f"预估收回:{result.get('premium_received') if result.get('premium_received') is not None else '—'} USDC",
                ]
            )
        )
    except Exception:
        pass


def _result_fully_done(result: dict[str, Any]) -> bool:
    if result.get("already_flat"):
        return True
    if result.get("fully_closed"):
        return True
    remaining = result.get("remaining_sheets")
    if remaining is not None and int(remaining) <= 0 and result.get("ok"):
        return True
    return False


def run_options_target_closes(
    conn: sqlite3.Connection,
    positions: list[dict[str, Any]],
    *,
    close_fn: Callable[[str], dict[str, Any]],
    index_fn: Callable[[dict[str, Any]], float | None] | None = None,
    send_wechat: Callable[[str], None] | None = None,
    account_label: str = "OKX期权",
) -> int:
    """
    扫描 active 目标委托;指数到位后限价平仓.
    状态先 commit 再推微信,避免 sync 失败回滚导致同一笔反复推送.
    未完全成交进入 closing,仅重试平仓不再推送.
    返回本次新触发(并推送)的条数.
    """
    ensure_target_tables(conn)
    pos_by_inst = {str(p.get("inst_id") or p.get("instId") or ""): p for p in positions}
    live_ids = {k for k in pos_by_inst if k}
    cancel_orphans_without_position(conn, live_inst_ids=live_ids)
    _commit_monitor(conn)

    # 先处理已挂单等待成交的,绝不再发微信
    for mon in list_closing_targets(conn):
        inst_id = str(mon.get("inst_id") or "")
        if not inst_id:
            continue
        if inst_id not in pos_by_inst:
            mark_monitor(conn, int(mon["id"]), status="expired", message="持仓已平")
            _commit_monitor(conn)
            continue
        result = close_fn(inst_id)
        idx = _safe_float(pos_by_inst[inst_id].get("idx_px") or pos_by_inst[inst_id].get("idxPx"))
        if result.get("already_flat") or _result_fully_done(result):
            mark_monitor(
                conn,
                int(mon["id"]),
                status="triggered",
                trigger_idx=idx,
                close_ord_id=result.get("close_ord_id"),
                message="目标位限价平仓完成",
            )
            _commit_monitor(conn)
            continue
        mark_monitor(
            conn,
            int(mon["id"]),
            status="closing",
            trigger_idx=idx,
            close_ord_id=result.get("close_ord_id"),
            message=str(result.get("msg") or result.get("stopped_reason") or "等待买一成交"),
        )
        _commit_monitor(conn)

    triggered = 0
    for mon in list_active_targets(conn):
        inst_id = str(mon.get("inst_id") or "")
        target = _safe_float(mon.get("target_index"))
        if not inst_id or target is None:
            continue
        pos = pos_by_inst.get(inst_id)
        if not pos:
            continue
        if index_fn is not None:
            idx = index_fn(pos)
        else:
            idx = _safe_float(pos.get("idx_px") or pos.get("idxPx"))
        if idx is None:
            continue
        opt_type = mon.get("opt_type") or pos.get("opt_type") or pos.get("optType")
        if not target_hit(opt_type=str(opt_type) if opt_type else None, index_px=idx, target_index=target):
            continue

        result = close_fn(inst_id)
        if result.get("already_flat"):
            mark_monitor(conn, int(mon["id"]), status="expired", trigger_idx=idx, message="持仓已平")
            _commit_monitor(conn)
            continue
        if not result.get("ok"):
            mark_monitor(
                conn,
                int(mon["id"]),
                status="active",
                trigger_idx=idx,
                message=str(result.get("msg") or result.get("stopped_reason") or "平仓未完成,将重试"),
            )
            _commit_monitor(conn)
            continue

        done = _result_fully_done(result)
        status = "triggered" if done else "closing"
        mark_monitor(
            conn,
            int(mon["id"]),
            status=status,
            trigger_idx=idx,
            close_ord_id=result.get("close_ord_id"),
            message="目标位触发限价平仓" if done else "目标位已挂买一限价,等待成交",
        )
        # 关键:先落库,再推送——否则后续 sync 异常回滚会让同一笔反复推微信
        _commit_monitor(conn)
        triggered += 1
        _notify_target_close(
            send_wechat,
            account_label=account_label,
            inst_id=inst_id,
            target=target,
            idx=idx,
            result=result,
        )
    return triggered
