"""币安期权 / 永续符号解析 → 中性合约行。"""

from __future__ import annotations

import re
from typing import Any

from ..expiry import expiry_ms_from_ymd, ymd_from_expiry_ms

_OPT_RE = re.compile(
    r"^(?P<under>[A-Z0-9]+)-(?P<ymd>\d{6})-(?P<strike>\d+(?:\.\d+)?)-(?P<side>[CP])$",
    re.IGNORECASE,
)


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 parse_option_symbol(symbol: str) -> tuple[str | None, float | None, str | None]:
    """ETH-250726-1860-C → (YYMMDD, strike, C|P)."""
    m = _OPT_RE.match((symbol or "").strip())
    if not m:
        return None, None, None
    ymd = m.group("ymd")
    strike = safe_float(m.group("strike"))
    side = m.group("side").upper()
    return ymd, strike, side


def is_option_symbol(symbol: str) -> bool:
    y, s, o = parse_option_symbol(symbol)
    return y is not None and s is not None and o in ("C", "P")


def rows_to_option_contracts(rows: list[dict[str, Any]]) -> list[dict[str, Any]]:
    """
    归一化：
    {inst_id, expiry_ymd, expiry_ms, strike, side, ct_mult}
    """
    out: list[dict[str, Any]] = []
    for row in rows:
        if not isinstance(row, dict):
            continue
        status = str(row.get("status") or "TRADING").upper()
        if status and status not in ("TRADING", "LIVE", ""):
            continue
        inst_id = str(row.get("symbol") or row.get("inst_id") or "")
        y, stk, opt = parse_option_symbol(inst_id)

        exp_ms = None
        raw_exp = row.get("expiryDate") or row.get("expiration") or row.get("expiry_ms")
        if raw_exp is not None:
            try:
                exp_ms = int(float(raw_exp))
                if exp_ms < 10_000_000_000:  # seconds
                    exp_ms *= 1000
            except (TypeError, ValueError):
                exp_ms = None

        if y is None and exp_ms is not None:
            y = ymd_from_expiry_ms(exp_ms)
        if stk is None:
            stk = safe_float(row.get("strikePrice") or row.get("strike"))
        if opt is None:
            side_raw = str(row.get("side") or row.get("optionSide") or "").upper()
            if side_raw in ("CALL", "C"):
                opt = "C"
            elif side_raw in ("PUT", "P"):
                opt = "P"

        if not inst_id or not y or stk is None or opt not in ("C", "P"):
            continue

        if exp_ms is None:
            try:
                exp_ms = expiry_ms_from_ymd(y)
            except ValueError:
                continue

        unit = safe_float(row.get("unit") or row.get("ct_mult"))
        out.append(
            {
                "inst_id": inst_id,
                "expiry_ymd": y,
                "expiry_ms": int(exp_ms),
                "strike": float(stk),
                "side": opt,
                "ct_mult": float(unit) if unit and unit > 0 else 1.0,
            }
        )
    return out
