"""Commodity exchange options provider (IV by series).

商品期权交易所数据提供方，用于对接 AkShare 的交易所日频隐含波动率数据。
"""

from typing import Literal

import akshare as ak
import pandas as pd

from ..cache import cache
from .greeks_base import OptionsGreeksDataProvider

CommodityExchange = Literal["shfe", "dce", "czce", "gfex"]


class ExchangeOptionsProvider(OptionsGreeksDataProvider):
    """Commodity exchange options provider (IV by series).

    商品期权交易所数据提供方。交易所通常仅披露 Delta 和隐含波动率，
    不提供完整希腊值集合，因此仅实现 ``get_iv_by_series``。
    """

    def __init__(self, exchange: CommodityExchange, underlying_symbol: str | None = None) -> None:
        """Initialize exchange options provider.

        Args:
            exchange: Exchange code ('shfe', 'dce', 'czce', 'gfex').
            underlying_symbol: Optional underlying symbol context.
        """
        super().__init__(underlying_symbol=underlying_symbol)
        self.exchange: CommodityExchange = exchange

    def get_greeks(self, symbol_or_date: str) -> pd.DataFrame:
        """Not supported: exchanges only provide Delta, not full Greeks.

        Raises:
            NotImplementedError: Always.
        """
        raise NotImplementedError(
            "Exchange options source does not provide full greeks; use local GreeksCalculator."
        )

    def get_risk_indicators(self, date: str) -> pd.DataFrame:
        """Not supported for exchange commodity options.

        Raises:
            NotImplementedError: Always.
        """
        raise NotImplementedError("Exchange options source does not provide risk indicator table.")

    def get_value_analysis(self) -> pd.DataFrame:
        """Not supported for exchange commodity options.

        Raises:
            NotImplementedError: Always.
        """
        raise NotImplementedError("Exchange options source does not provide value analysis table.")

    @cache(
        "commodity_options_daily_cache",
        key=lambda self, symbol, date: f"exchange_options_iv_{self.exchange}_{symbol}_{date}",
    )
    def get_iv_by_series(self, symbol: str, date: str) -> pd.DataFrame:
        """Fetch IV by contract series for commodity options.

        获取商品期权按合约系列披露的隐含波动率数据。

        Args:
            symbol: Commodity option product name (e.g., '铝期权').
            date: Trade date, format 'YYYYMMDD' or 'YYYY-MM-DD'.

        Returns:
            pd.DataFrame with columns: symbol, date, series, iv, volume, open_interest

        Raises:
            ValueError: If request fails.
        """
        try:
            trade_date = pd.to_datetime(date, errors="raise").strftime("%Y%m%d")
            out_date = pd.to_datetime(date, errors="coerce").date().isoformat()

            raw_df: pd.DataFrame | None
            if self.exchange == "shfe":
                raw_df = ak.option_vol_shfe(symbol=symbol, trade_date=trade_date)
            elif self.exchange == "gfex":
                raw_df = ak.option_vol_gfex(symbol=symbol, trade_date=trade_date)
            elif self.exchange == "dce":
                raw_df = ak.option_hist_dce(symbol=symbol, trade_date=trade_date)
            elif self.exchange == "czce":
                raw_df = ak.option_hist_czce(symbol=symbol, trade_date=trade_date)
            else:
                raise ValueError(f"Unsupported exchange: {self.exchange}")

            if raw_df is None or raw_df.empty:
                return pd.DataFrame(
                    columns=["symbol", "date", "series", "iv", "volume", "open_interest"]
                )

            series_col, iv_col, volume_col, oi_col = self._resolve_columns(raw_df)

            result = pd.DataFrame({
                "symbol": symbol,
                "date": out_date,
                "series": raw_df[series_col].astype(str) if series_col else None,
                "iv": raw_df[iv_col] if iv_col else None,
                "volume": raw_df[volume_col] if volume_col else None,
                "open_interest": raw_df[oi_col] if oi_col else None,
            })

            result["iv"] = pd.to_numeric(result["iv"], errors="coerce")
            # Some exchanges expose IV as percentage; normalize to decimal
            if iv_col and "%" in iv_col:
                result["iv"] = result["iv"] / 100.0

            result["volume"] = pd.to_numeric(result["volume"], errors="coerce")
            result["open_interest"] = pd.to_numeric(result["open_interest"], errors="coerce")

            return result[["symbol", "date", "series", "iv", "volume", "open_interest"]]
        except Exception as e:
            raise ValueError(
                f"Failed to fetch commodity options IV for exchange={self.exchange}: {e}"
            ) from e

    @staticmethod
    def _pick_column(df: pd.DataFrame, candidates: list[str]) -> str | None:
        """Pick first existing column from candidates.

        Args:
            df: Input DataFrame.
            candidates: Candidate column names.

        Returns:
            str | None: Selected column name or None.
        """
        for col in candidates:
            if col in df.columns:
                return col
        return None

    def _resolve_columns(
        self, df: pd.DataFrame
    ) -> tuple[str | None, str | None, str | None, str | None]:
        """Resolve exchange-specific columns for normalization.

        Args:
            df: Raw DataFrame.

        Returns:
            tuple: (series_col, iv_col, volume_col, oi_col)
        """
        if self.exchange in ("shfe", "gfex"):
            return (
                self._pick_column(df, ["合约系列"]),
                self._pick_column(df, ["隐含波动率", "隐含波动率(%)"]),
                self._pick_column(df, ["成交量"]),
                self._pick_column(df, ["持仓量"]),
            )

        if self.exchange == "dce":
            return (
                self._pick_column(df, ["合约", "合约名称"]),
                self._pick_column(df, ["隐含波动率(%)", "隐含波动率"]),
                self._pick_column(df, ["成交量"]),
                self._pick_column(df, ["持仓量"]),
            )

        if self.exchange == "czce":
            return (
                self._pick_column(df, ["合约代码"]),
                self._pick_column(df, ["隐含波动率", "隐含波动率(%)"]),
                self._pick_column(df, ["成交量(手)", "成交量"]),
                self._pick_column(df, ["持仓量"]),
            )

        raise ValueError(f"Unsupported exchange: {self.exchange}")
