from __future__ import annotations

from concurrent.futures import ThreadPoolExecutor, as_completed
from datetime import datetime, timezone
from typing import Any

import httpx
from tenacity import retry, stop_after_attempt, wait_exponential

from uforia.config import AppConfig, get_config


class DeribitPublicClient:
    def __init__(self, config: AppConfig | None = None) -> None:
        self.config = config or get_config()
        self.client = httpx.Client(
            base_url=self.config.deribit_api_base,
            timeout=self.config.ingestion.request_timeout_seconds,
        )

    @retry(
        wait=wait_exponential(multiplier=1, min=1, max=8), stop=stop_after_attempt(3), reraise=True
    )
    def _get(self, method: str, params: dict[str, Any]) -> Any:
        response = self.client.get(method, params=params)
        response.raise_for_status()
        payload = response.json()
        if payload.get("error"):
            raise RuntimeError(payload["error"])
        return payload["result"]

    def get_book_summaries(self, currency: str, kind: str = "option") -> list[dict[str, Any]]:
        result = self._get(
            "/public/get_book_summary_by_currency", {"currency": currency, "kind": kind}
        )
        return list(result)

    def get_ticker(self, instrument_name: str) -> dict[str, Any]:
        return dict(self._get("/public/ticker", {"instrument_name": instrument_name}))

    def get_tickers(
        self, instrument_names: list[str], *, max_workers: int = 4
    ) -> list[dict[str, Any]]:
        if not instrument_names:
            return []
        worker_count = min(max_workers, len(instrument_names))
        results: dict[str, dict[str, Any]] = {}
        with ThreadPoolExecutor(max_workers=worker_count) as executor:
            futures = {
                executor.submit(self.get_ticker, instrument_name): instrument_name
                for instrument_name in instrument_names
            }
            for future in as_completed(futures):
                instrument_name = futures[future]
                try:
                    results[instrument_name] = future.result()
                except Exception:
                    continue
        return [results[name] for name in instrument_names if name in results]

    def get_instruments(
        self, currency: str, kind: str = "option", expired: bool = False
    ) -> list[dict[str, Any]]:
        result = self._get(
            "/public/get_instruments",
            {"currency": currency, "kind": kind, "expired": str(expired).lower()},
        )
        return list(result)

    def get_volatility_index_data(
        self,
        currency: str,
        start: datetime,
        end: datetime,
        resolution: str = "3600",
    ) -> dict[str, Any]:
        params = {
            "currency": currency,
            "start_timestamp": int(start.timestamp() * 1000),
            "end_timestamp": int(end.timestamp() * 1000),
            "resolution": resolution,
        }
        return dict(self._get("/public/get_volatility_index_data", params))

    def get_funding_rate_history(
        self,
        instrument_name: str,
        start: datetime,
        end: datetime,
    ) -> list[dict[str, Any]]:
        params = {
            "instrument_name": instrument_name,
            "start_timestamp": int(start.timestamp() * 1000),
            "end_timestamp": int(end.timestamp() * 1000),
        }
        result = self._get("/public/get_funding_rate_history", params)
        return list(result)

    def now(self) -> datetime:
        return datetime.now(tz=timezone.utc)
