"""Futures market data service — wraps ``/futures/*`` endpoints."""

from __future__ import annotations

from datetime import date, datetime

import pandas as pd

from edge._types import Interval, SortOrder, SymbolInput, VolumeType
from edge.client.base import BaseClient


class FuturesService:
    """Access historical bars, real-time quotes, intraday OHLC, options EOD,
    and raw market events via the market-data-service ``/futures`` API."""

    def __init__(self, client: BaseClient):
        self._c = client

    # ── GET /futures/bars/historical ─────────────────────────────────────────

    def historical_bars(
        self,
        symbols: SymbolInput,
        exchange: str = "CME",
        start_date: str | date | datetime | None = None,
        end_date: str | date | datetime | None = None,
        max_records: int | None = None,
    ) -> pd.DataFrame:
        """Daily OHLCV bars (multi-symbol)."""
        params: dict = {
            "symbols": self._c._format_symbols(symbols),
            "exchange": exchange,
        }
        if start_date is not None:
            params["start_date"] = self._c._format_date(start_date)
        if end_date is not None:
            params["end_date"] = self._c._format_date(end_date)
        if max_records is not None:
            params["max_records"] = max_records

        resp = self._c._get("/futures/bars/historical", params=params)
        df = self._c._to_dataframe(resp.get("data", []))
        if not df.empty and "ts_event" in df.columns:
            df = df.rename(columns={"ts_event": "date"})
            df = df.sort_values(["symbol", "date"]).reset_index(drop=True)
        return df

    # ── GET /futures/quotes/latest ───────────────────────────────────────────

    def latest_quotes(
        self,
        symbols: SymbolInput,
        exchange: str = "CME",
        start: str | datetime | None = None,
        end: str | datetime | None = None,
    ) -> pd.DataFrame:
        """Latest price snapshots from the streaming table."""
        params: dict = {
            "symbols": self._c._format_symbols(symbols),
            "exchange": exchange,
        }
        if start is not None:
            params["start"] = start if isinstance(start, str) else start.isoformat()
        if end is not None:
            params["end"] = end if isinstance(end, str) else end.isoformat()

        resp = self._c._get("/futures/quotes/latest", params=params)
        return self._c._to_dataframe(resp.get("data", []))

    # ── GET /futures/bars/intraday ───────────────────────────────────────────

    def intraday_bars(
        self,
        symbols: SymbolInput,
        exchange: str = "CME",
        interval: Interval = "5m",
        start: str | datetime | None = None,
        end: str | datetime | None = None,
    ) -> pd.DataFrame:
        """Intraday OHLC bars at configurable intervals."""
        params: dict = {
            "symbols": self._c._format_symbols(symbols),
            "exchange": exchange,
            "interval": interval,
        }
        if start is not None:
            params["start"] = start if isinstance(start, str) else start.isoformat()
        if end is not None:
            params["end"] = end if isinstance(end, str) else end.isoformat()

        resp = self._c._get("/futures/bars/intraday", params=params)
        return self._c._to_dataframe(resp.get("data", []))

    # ── GET /futures/symbols/search ──────────────────────────────────────────

    def search_symbols(
        self,
        query: str,
        exchange: str | None = None,
        limit: int = 20,
    ) -> pd.DataFrame:
        """Search for futures products."""
        params: dict = {"q": query, "limit": limit}
        if exchange is not None:
            params["exchange"] = exchange
        resp = self._c._get("/futures/symbols/search", params=params)
        return self._c._to_dataframe(resp.get("results", []))

    # ── GET /futures/options/search ──────────────────────────────────────────

    def search_options(
        self,
        query: str,
        exchange: str | None = None,
        limit: int = 10,
    ) -> pd.DataFrame:
        """Search and normalize futures option contracts."""
        params: dict = {"q": query, "limit": limit}
        if exchange is not None:
            params["exchange"] = exchange
        resp = self._c._get("/futures/options/search", params=params)
        return self._c._to_dataframe(resp.get("results", []))

    # ── GET /futures/options/eod ─────────────────────────────────────────────

    def option_eod(
        self,
        symbol: str,
        max_records: int = 30,
        order: SortOrder = "desc",
        volume: VolumeType = "contract",
    ) -> pd.DataFrame:
        """End-of-day data for an explicit futures option symbol."""
        params: dict = {
            "symbol": symbol,
            "max_records": max_records,
            "order": order,
            "volume": volume,
        }
        resp = self._c._get("/futures/options/eod", params=params)
        return self._c._to_dataframe(resp.get("data", []))

    # ── GET /futures/events ──────────────────────────────────────────────────

    def events(
        self,
        symbols: SymbolInput,
        exchange: str = "CME",
        since: str | datetime | None = None,
        lookback_seconds: int = 300,
        msg_types: str = "marketUpdate",
        limit: int = 1000,
    ) -> pd.DataFrame:
        """Raw market events / ticks from the streaming table."""
        params: dict = {
            "symbols": self._c._format_symbols(symbols),
            "exchange": exchange,
            "lookback_seconds": lookback_seconds,
            "msg_types": msg_types,
            "limit": limit,
        }
        if since is not None:
            params["since"] = since if isinstance(since, str) else since.isoformat()
        resp = self._c._get("/futures/events", params=params)
        return self._c._to_dataframe(resp.get("data", []))
