from __future__ import annotations

from typing import Annotated, Any

from fastapi import APIRouter, Depends, HTTPException

from ..models.db import get_db
from ..sim.pnl import summarize_fills_pnl
from .auth import require_user
from .hold_timing import hold_timing

router = APIRouter(prefix="/api/trades", tags=["trades"])


def _row(r: Any) -> dict:
    return dict(r)


def _expiry_settle_info(g: dict, fills: list) -> dict | None:
    """到期结算口径：期权价 = 内在价值（指数 vs 行权价），非盘口。"""
    if str(g.get("close_reason") or "") != "expiry":
        return None
    strike = g.get("strike")
    side = str(g.get("option_side") or "").lower()
    settle_index = g.get("settle_index_px")
    if settle_index is None and strike is not None:
        for raw in fills:
            f = dict(raw) if not isinstance(raw, dict) else raw
            if str(f.get("leg")) != "option" or str(f.get("action")) != "close":
                continue
            if abs(float(f.get("slip") or 0)) > 1e-12:
                continue
            px = float(f.get("fill_px") or 0)
            k = float(strike)
            if side in ("call", "c"):
                settle_index = k + px
            elif side in ("put", "p"):
                settle_index = k - px
            break
    intrinsic = None
    if settle_index is not None and strike is not None:
        s = float(settle_index)
        k = float(strike)
        if side in ("call", "c"):
            intrinsic = max(s - k, 0.0)
        elif side in ("put", "p"):
            intrinsic = max(k - s, 0.0)
    formula = (
        "Call: max(指数−行权价, 0)"
        if side in ("call", "c")
        else "Put: max(行权价−指数, 0)"
        if side in ("put", "p")
        else ""
    )
    return {
        "settle_index_px": float(settle_index) if settle_index is not None else None,
        "strike": float(strike) if strike is not None else None,
        "intrinsic": intrinsic,
        "formula": formula,
        "perp_note": "永续仍按市价平仓（非指数交割）",
    }


def _close_index_px(g: dict, fills: list) -> float | None:
    """平仓时标的指数：优先 settle_index_px，否则用永续平仓价近似。"""
    raw = g.get("settle_index_px")
    if raw is not None:
        try:
            v = float(raw)
            if v > 0:
                return v
        except (TypeError, ValueError):
            pass
    for row in fills:
        f = dict(row) if not isinstance(row, dict) else row
        if str(f.get("leg") or "") == "perp" and str(f.get("action") or "") == "close":
            try:
                v = float(f.get("fill_px") or 0)
                if v > 0:
                    return v
            except (TypeError, ValueError):
                pass
            break
    return None


def _move_points(g: dict, fills: list) -> float | None:
    """开仓指数 → 平仓指数的点数（带符号：上涨为正）。持仓中无平仓价则空。"""
    entry = g.get("entry_index_px")
    if entry is None:
        return None
    try:
        e = float(entry)
    except (TypeError, ValueError):
        return None
    if e <= 0:
        return None
    close_px = _close_index_px(g, fills)
    if close_px is None:
        return None
    return round(float(close_px) - e, 2)


def _option_entry_px(fills: list) -> float | None:
    for row in fills:
        f = dict(row) if not isinstance(row, dict) else row
        if str(f.get("leg") or "") != "option" or str(f.get("action") or "") != "open":
            continue
        try:
            v = float(f.get("fill_px") or 0)
            if v > 0:
                return v
        except (TypeError, ValueError):
            pass
        break
    return None


def _option_leverage(g: dict, fills: list) -> float | None:
    """开仓期权杠杆 = 开仓指数 ÷ 期权开仓均价（与选约门限口径一致）。"""
    from ..strategy.selection import option_leverage

    try:
        entry = float(g.get("entry_index_px") or 0)
    except (TypeError, ValueError):
        return None
    opt_px = _option_entry_px(fills)
    if entry <= 0 or opt_px is None:
        return None
    lev = option_leverage(entry, opt_px)
    return round(float(lev), 1) if lev is not None else None


def _enrich_group(g: dict, fills: list) -> dict:
    summary = summarize_fills_pnl(fills)
    # LIVE：优先 groups.realized_pnl（已按交易所回写，含资金费）
    if str(g.get("exec_mode") or "").upper() == "LIVE" and g.get("realized_pnl") is not None:
        summary = dict(summary)
        summary["net_pnl"] = float(g["realized_pnl"])
        if g.get("funding_usdt") is not None:
            summary["funding_usdt"] = float(g["funding_usdt"])
        summary["pnl_source"] = "live_exchange"
    g["pnl_summary"] = summary
    if summary.get("net_pnl") is not None:
        g["net_pnl"] = summary["net_pnl"]
    elif g.get("realized_pnl") is not None:
        g["net_pnl"] = float(g["realized_pnl"])
    g.update(hold_timing(g, fills))
    info = _expiry_settle_info(g, fills)
    if info:
        g["expiry_settle"] = info
        if g.get("settle_index_px") is None and info.get("settle_index_px") is not None:
            g["settle_index_px"] = info["settle_index_px"]
    mp = _move_points(g, fills)
    g["move_points"] = mp
    g["close_index_px"] = _close_index_px(g, fills)
    g["option_leverage"] = _option_leverage(g, fills)
    return g


@router.get("/groups")
async def list_groups(_user: Annotated[str, Depends(require_user)]) -> dict:
    db = get_db()
    rows = db.fetchall("SELECT * FROM groups ORDER BY open_at_ms DESC LIMIT 200")
    groups = []
    for r in rows:
        g = _row(r)
        fills = db.fetchall(
            "SELECT * FROM fills WHERE group_id=? ORDER BY id ASC",
            (g["group_id"],),
        )
        groups.append(_enrich_group(g, fills))
    return {"groups": groups}


@router.get("/groups/{group_id}")
async def group_detail(
    group_id: str, _user: Annotated[str, Depends(require_user)]
) -> dict:
    db = get_db()
    g = db.fetchone("SELECT * FROM groups WHERE group_id=?", (group_id,))
    if g is None:
        raise HTTPException(status_code=404, detail="group not found")
    fills = db.fetchall(
        "SELECT * FROM fills WHERE group_id=? ORDER BY id ASC", (group_id,)
    )
    gr = _enrich_group(_row(g), fills)
    return {
        "group": gr,
        "fills": [_row(x) for x in fills],
        "pnl_summary": gr.get("pnl_summary"),
    }
