"""OKX REST 只读行情。不调用任何交易类接口。"""

from __future__ import annotations

from typing import Any

import httpx

from ..types import BookLevel
from .parse import safe_float


class OkxRestClient:
    def __init__(
        self,
        base_url: str = "https://www.okx.com",
        timeout: float = 15.0,
        proxy: str | None = None,
    ) -> None:
        self.base_url = base_url.rstrip("/")
        self.proxy = (proxy or "").strip() or None
        self._client = httpx.Client(
            base_url=self.base_url,
            timeout=timeout,
            proxy=self.proxy,
            headers={"Accept": "application/json", "User-Agent": "eth-hedge-sim/0.1"},
        )

    def close(self) -> None:
        self._client.close()

    def __enter__(self) -> OkxRestClient:
        return self

    def __exit__(self, *args: object) -> None:
        self.close()

    def _get(self, path: str, params: dict[str, Any] | None = None) -> list[dict[str, Any]]:
        r = self._client.get(path, params=params or {})
        r.raise_for_status()
        body = r.json()
        if str(body.get("code")) != "0":
            raise RuntimeError(f"OKX REST error code={body.get('code')} msg={body.get('msg')}")
        data = body.get("data") or []
        return [x for x in data if isinstance(x, dict)]

    def fetch_instruments(self, *, inst_type: str, inst_family: str | None = None) -> list[dict[str, Any]]:
        params: dict[str, Any] = {"instType": inst_type}
        if inst_family:
            params["instFamily"] = inst_family
        return self._get("/api/v5/public/instruments", params)

    def fetch_option_instruments(self, inst_family: str) -> list[dict[str, Any]]:
        rows = self.fetch_instruments(inst_type="OPTION", inst_family=inst_family)
        return [r for r in rows if str(r.get("state") or "").lower() == "live"]

    def fetch_index_ticker(self, inst_id: str) -> float | None:
        rows = self._get("/api/v5/market/index-tickers", {"instId": inst_id})
        if not rows:
            return None
        return safe_float(rows[0].get("idxPx"))

    def fetch_mark_price(self, inst_id: str) -> float | None:
        rows = self._get("/api/v5/public/mark-price", {"instId": inst_id})
        if not rows:
            t = self._get("/api/v5/market/ticker", {"instId": inst_id})
            if not t:
                return None
            return safe_float(t[0].get("markPx")) or safe_float(t[0].get("last"))
        return safe_float(rows[0].get("markPx"))

    def fetch_books(self, inst_id: str, sz: int = 5) -> tuple[list[BookLevel], list[BookLevel], int | None]:
        rows = self._get(
            "/api/v5/market/books",
            {"instId": inst_id, "sz": str(max(1, min(int(sz), 400)))},
        )
        if not rows:
            return [], [], None
        row = rows[0]
        ts = safe_float(row.get("ts"))
        ts_ms = int(ts) if ts is not None else None
        return (
            _levels(row.get("bids") or []),
            _levels(row.get("asks") or []),
            ts_ms,
        )


def _levels(raw: list[Any]) -> list[BookLevel]:
    out: list[BookLevel] = []
    for item in raw:
        if not isinstance(item, (list, tuple)) or len(item) < 2:
            continue
        px = safe_float(item[0])
        sz = safe_float(item[1])
        if px is None or sz is None or px <= 0 or sz <= 0:
            continue
        out.append(BookLevel(px=px, sz=sz))
    return out
