"""OKX 交易所适配器：只负责行情与合约，不含策略选约。"""

from __future__ import annotations

import logging
from typing import Any, Sequence

from ...config import Settings, get_settings
from ..book_cache import BookCache
from ..types import BookLevel, MarketSnapshot, OptionPair, Quote
from .parse import rows_to_option_contracts, safe_float
from .rest import OkxRestClient
from .ws import OkxPublicWs

logger = logging.getLogger(__name__)


class OkxExchange:
    name = "okx"

    def __init__(self, settings: Settings | None = None) -> None:
        self.settings = settings or get_settings()
        self.cache = BookCache()
        proxy = self.settings.okx_http_proxy or None
        self.rest = OkxRestClient(self.settings.okx_rest_base, proxy=proxy)
        self.ws = OkxPublicWs(self.settings.okx_ws_public, self.cache, proxy=proxy)
        self._started = False
        self._ct_cache: dict[str, float] = {}

    async def start(self) -> None:
        if self._started:
            return
        self._started = True
        await self.ws.start()
        logger.info("OKX exchange started")

    async def stop(self) -> None:
        self._started = False
        await self.ws.stop()
        self.rest.close()
        logger.info("OKX exchange stopped")

    def list_option_contracts(self, family: str) -> list[dict[str, Any]]:
        rows = self.rest.fetch_option_instruments(family)
        contracts = rows_to_option_contracts(rows)
        for c in contracts:
            if c.get("ct_mult"):
                self._ct_cache[str(c["inst_id"])] = float(c["ct_mult"])
        return contracts

    def fetch_index(self, index_id: str) -> float | None:
        return self.rest.fetch_index_ticker(index_id)

    def fetch_mark(self, inst_id: str) -> float | None:
        return self.rest.fetch_mark_price(inst_id)

    def fetch_book(
        self, inst_id: str, depth: int = 5
    ) -> tuple[list[BookLevel], list[BookLevel], int | None]:
        return self.rest.fetch_books(inst_id, sz=depth)

    def get_ct_mult(self, option_inst_id: str, family: str, default: float) -> float:
        if option_inst_id in self._ct_cache:
            return self._ct_cache[option_inst_id]
        try:
            rows = self.rest.fetch_instruments(inst_type="OPTION", inst_family=family)
            for r in rows:
                if str(r.get("instId")) == option_inst_id:
                    m = safe_float(r.get("ctMult"))
                    if m and m > 0:
                        self._ct_cache[option_inst_id] = float(m)
                        return float(m)
        except Exception:
            pass
        return float(default)

    def set_pair(self, pair: OptionPair | None) -> None:
        self.cache.set_pair(pair)

    def warm_and_subscribe(self, inst_ids: Sequence[str]) -> None:
        ids = [i for i in inst_ids if i]
        for inst in ids:
            bids, asks, ts = self.rest.fetch_books(inst, sz=5)
            self.cache.upsert_book(inst, bids=bids, asks=asks, ts_ms=ts)
            mp = self.rest.fetch_mark_price(inst)
            if mp:
                self.cache.set_mark_px(inst, mp)
        keep = set(ids)
        self.cache.drop_except(keep)
        self.ws.set_instruments(ids)

    async def resubscribe(self, inst_ids: Sequence[str]) -> None:
        await self.ws.resubscribe([i for i in inst_ids if i])

    def quote(self, inst_id: str) -> Quote | None:
        return self.cache.get(inst_id)

    def snapshot(self, perp_inst_id: str) -> MarketSnapshot:
        return self.cache.snapshot(perp_inst_id)

    def snapshot_dict(self, perp_inst_id: str) -> dict[str, Any]:
        return self.snapshot(perp_inst_id).to_dict()

    def set_index_px(self, px: float | None) -> None:
        self.cache.set_index_px(px)

    def set_mark_px(self, inst_id: str, mark_px: float | None) -> None:
        self.cache.set_mark_px(inst_id, mark_px)
