"""Main Edge SDK client."""

from __future__ import annotations

from datetime import date, datetime

import pandas as pd

from edge._types import DateInput, SymbolInput
from edge.client.base import (
    BaseClient,
    _expand_symbols,
    _infer_exchange,
)
from edge.config import EdgeConfig
from edge.services.futures import FuturesService
from edge.services.symbology import SymbologyService


class Edge(BaseClient):
    """Top-level client for the Edge market data platform.

    Usage::

        from edge import Edge

        edge = Edge()                          # local dev, no auth
        edge = Edge(api_key="edge_abc123")     # production

        # Bare roots auto-expand to front-month in convenience methods
        df = edge.history("ZC", start="1Y")          # ZC → ZC*1
        df = edge.history("ZCZ26", start="2025-01-01")  # explicit — no expansion

        # Use service layer directly to skip expansion
        df = edge.futures.historical_bars("ZC", exchange="CBOT", start_date="2025-01-01")

        # Price matrix (date × symbol)
        matrix = edge.prices(["ZC", "ZS", "LE"], start="1Y")

        # Quick last price
        edge.last("ZC")             # → float
        edge.last(["ZC", "ZS"])     # → Series

        # Analytics
        ret = edge.returns(["ZC", "ZS"], start="1Y")
        corr = edge.correlation(["ZC", "ZS", "LE", "CL"], start="3Y")
    """

    def __init__(
        self,
        base_url: str | None = None,
        api_key: str | None = None,
        timeout: float = EdgeConfig.DEFAULT_TIMEOUT,
    ):
        config = EdgeConfig(base_url=base_url, api_key=api_key, timeout=timeout)
        super().__init__(config)

        self.futures = FuturesService(self)
        self.symbology = SymbologyService(self)

    # ── Internal helpers ─────────────────────────────────────────────────────

    def _resolve(self, symbols: SymbolInput) -> tuple[list[str], str]:
        """Expand symbols and infer exchange. Returns (expanded, exchange)."""
        expanded = _expand_symbols(symbols)
        exchange = _infer_exchange(expanded)
        return expanded, exchange

    # ── Core convenience methods ─────────────────────────────────────────────

    def history(
        self,
        symbols: SymbolInput,
        start: DateInput = "1Y",
        end: DateInput = None,
        exchange: str | None = None,
        max_records: int | None = None,
    ) -> pd.DataFrame:
        """Historical daily OHLCV bars.

        ``start`` defaults to ``"1Y"`` (one year ago).
        Bare roots expand to front-month (``"ZC"`` → ``"ZC*1"``).
        Qualified symbols pass through unchanged (``"ZCZ26"``, ``"ZC*1"``).
        Use ``edge.futures.historical_bars()`` to skip expansion entirely.
        """
        expanded, inferred_ex = self._resolve(symbols)
        return self.futures.historical_bars(
            symbols=expanded,
            exchange=exchange or inferred_ex,
            start_date=start,
            end_date=end,
            max_records=max_records,
        )

    def quote(
        self,
        symbols: SymbolInput,
        exchange: str | None = None,
    ) -> pd.DataFrame:
        """Latest price snapshots."""
        expanded, inferred_ex = self._resolve(symbols)
        return self.futures.latest_quotes(
            symbols=expanded,
            exchange=exchange or inferred_ex,
        )

    def search(self, query: str, limit: int = 20) -> pd.DataFrame:
        """Search futures products by name or symbol."""
        return self.futures.search_symbols(query=query, limit=limit)

    # ── Price matrix ─────────────────────────────────────────────────────────

    def prices(
        self,
        symbols: SymbolInput,
        start: DateInput = "1Y",
        end: DateInput = None,
        field: str = "close",
    ) -> pd.DataFrame:
        """Pivoted price matrix — rows=date, columns=symbol.

        Returns a clean DataFrame ready for analysis::

            matrix = edge.prices(["ZC", "ZS", "LE"], start="1Y")
            matrix.corr()
        """
        df = self.history(symbols, start=start, end=end)
        if df.empty:
            return df
        return df.pivot_table(index="date", columns="symbol", values=field)

    # ── Quick lookups ────────────────────────────────────────────────────────

    def last(self, symbols: SymbolInput) -> float | pd.Series:
        """Most recent close price.

        Tries real-time quotes first, falls back to the last historical
        bar if streaming data is unavailable.

        Returns a scalar for a single symbol, or a Series for multiple.
        """
        try:
            df = self.quote(symbols)
            if not df.empty:
                if "last_price" in df.columns:
                    price_col = "last_price"
                elif "session_close" in df.columns:
                    price_col = "session_close"
                else:
                    price_col = "close"
                series = df.set_index("symbol")[price_col]
                if isinstance(symbols, str) and "," not in symbols:
                    return float(series.iloc[0]) if len(series) > 0 else float("nan")
                return series
        except Exception:
            pass

        # Fallback: last historical close
        df = self.history(symbols, start="1W")
        if df.empty:
            return pd.Series(dtype=float) if isinstance(symbols, list) else float("nan")
        latest = df.sort_values("date").groupby("symbol").last()["close"]
        if isinstance(symbols, str) and "," not in symbols:
            return float(latest.iloc[0]) if len(latest) > 0 else float("nan")
        return latest

    # ── Analytics helpers ────────────────────────────────────────────────────

    def returns(
        self,
        symbols: SymbolInput,
        start: DateInput = "1Y",
        end: DateInput = None,
    ) -> pd.DataFrame:
        """Daily percentage returns matrix (date × symbol)."""
        matrix = self.prices(symbols, start=start, end=end)
        if matrix.empty:
            return matrix
        return matrix.pct_change().dropna()

    def correlation(
        self,
        symbols: SymbolInput,
        start: DateInput = "1Y",
        end: DateInput = None,
    ) -> pd.DataFrame:
        """Correlation matrix of daily returns."""
        ret = self.returns(symbols, start=start, end=end)
        if ret.empty:
            return ret
        return ret.corr()

    # ── Health ───────────────────────────────────────────────────────────────

    def health_check(self) -> dict:
        """Hit the ``/health`` endpoint on the market-data-service."""
        return self._get("/health")
