"""指数/永续 K 线高低点：期期对冲振幅回看。"""

from __future__ import annotations

import logging
from dataclasses import dataclass
from typing import Any

from .okx.parse import safe_float as okx_safe_float
from .binance.parse import safe_float as bn_safe_float

logger = logging.getLogger(__name__)


@dataclass(frozen=True, slots=True)
class AmplitudeHL:
    high: float
    low: float
    mid: float
    hours: float
    bar_count: int

    @property
    def range_pct(self) -> float:
        if self.mid <= 0:
            return 0.0
        return (self.high - self.low) / self.mid * 100.0


def _hl_from_okx_candles(rows: list[Any]) -> tuple[float, float] | None:
    """OKX candle row: [ts, o, h, l, c, ...] newest first."""
    highs: list[float] = []
    lows: list[float] = []
    for row in rows:
        if not isinstance(row, (list, tuple)) or len(row) < 5:
            continue
        h = okx_safe_float(row[2])
        lo = okx_safe_float(row[3])
        if h is None or lo is None or h <= 0 or lo <= 0:
            continue
        highs.append(float(h))
        lows.append(float(lo))
    if not highs or not lows:
        return None
    return max(highs), min(lows)


def _hl_from_binance_klines(rows: list[Any]) -> tuple[float, float] | None:
    """Binance kline: [openTime, o, h, l, c, ...] oldest first."""
    highs: list[float] = []
    lows: list[float] = []
    for row in rows:
        if not isinstance(row, (list, tuple)) or len(row) < 5:
            continue
        h = bn_safe_float(row[2])
        lo = bn_safe_float(row[3])
        if h is None or lo is None or h <= 0 or lo <= 0:
            continue
        highs.append(float(h))
        lows.append(float(lo))
    if not highs or not lows:
        return None
    return max(highs), min(lows)


def fetch_okx_amplitude_hl(
    *,
    inst_id: str,
    hours: float,
    base_url: str = "https://www.okx.com",
    proxy: str | None = None,
) -> AmplitudeHL | None:
    """用 1H K 线回看 hours；inst 可用指数 ETH-USD 或永续 ETH-USDT-SWAP。"""
    import math

    import httpx

    hrs = max(1.0, float(hours))
    limit = int(min(300, max(2, math.ceil(hrs) + 1)))
    try:
        with httpx.Client(
            base_url=base_url.rstrip("/"),
            timeout=15.0,
            proxy=(proxy or "").strip() or None,
            headers={"Accept": "application/json", "User-Agent": "eth-hedge-sim/0.1"},
        ) as client:
            r = client.get(
                "/api/v5/market/candles",
                params={"instId": inst_id, "bar": "1H", "limit": str(limit)},
            )
            r.raise_for_status()
            body = r.json()
            if str(body.get("code")) != "0":
                logger.warning("OKX candles error: %s", body.get("msg"))
                return None
            data = body.get("data") or []
    except Exception as e:
        logger.warning("OKX candles fetch failed: %s", e)
        return None
    hl = _hl_from_okx_candles(data)
    if hl is None:
        return None
    high, low = hl
    mid = (high + low) / 2.0
    return AmplitudeHL(
        high=high, low=low, mid=mid, hours=hrs, bar_count=len(data)
    )


def fetch_binance_amplitude_hl(
    *,
    symbol: str,
    hours: float,
    fapi_base: str = "https://fapi.binance.com",
    proxy: str | None = None,
) -> AmplitudeHL | None:
    """USDT 永续 1h klines。"""
    import math

    import httpx

    hrs = max(1.0, float(hours))
    limit = int(min(500, max(2, math.ceil(hrs) + 1)))
    sym = str(symbol or "ETHUSDT").upper().replace("-", "")
    try:
        with httpx.Client(
            base_url=fapi_base.rstrip("/"),
            timeout=15.0,
            proxy=(proxy or "").strip() or None,
            headers={"Accept": "application/json", "User-Agent": "eth-hedge-sim/0.1"},
            trust_env=False,
        ) as client:
            r = client.get(
                "/fapi/v1/klines",
                params={"symbol": sym, "interval": "1h", "limit": limit},
            )
            r.raise_for_status()
            data = r.json()
            if not isinstance(data, list):
                return None
    except Exception as e:
        logger.warning("Binance klines fetch failed: %s", e)
        return None
    hl = _hl_from_binance_klines(data)
    if hl is None:
        return None
    high, low = hl
    mid = (high + low) / 2.0
    return AmplitudeHL(
        high=high, low=low, mid=mid, hours=hrs, bar_count=len(data)
    )


def fetch_amplitude_hl_for_runtime(hours: float) -> AmplitudeHL | None:
    """按当前交易所 runtime 拉振幅高低点。"""
    from ..config import get_settings
    from .runtime import load_runtime_settings

    s = get_settings()
    rt = load_runtime_settings()
    ex = str(rt.exchange or "okx").strip().lower()
    hrs = float(hours)
    if ex in ("binance", "bn"):
        return fetch_binance_amplitude_hl(
            symbol=str(rt.perp_inst_id or "ETHUSDT"),
            hours=hrs,
            fapi_base=s.binance_fapi_base,
            proxy=s.binance_http_proxy or None,
        )
    # OKX：优先指数，失败再试永续
    idx = str(rt.index_inst_id or "ETH-USD")
    amp = fetch_okx_amplitude_hl(
        inst_id=idx,
        hours=hrs,
        base_url=s.okx_rest_base,
        proxy=s.okx_http_proxy or None,
    )
    if amp is not None:
        return amp
    return fetch_okx_amplitude_hl(
        inst_id=str(rt.perp_inst_id or "ETH-USDT-SWAP"),
        hours=hrs,
        base_url=s.okx_rest_base,
        proxy=s.okx_http_proxy or None,
    )
