"""Options indicators (Track B).

期权衍生指标（Track B，本地计算），包含 PCR、Max Pain、IV Rank/Percentile/Skew/Term Structure、
Greeks 计算与 IV 反解等功能。
"""

import pandas as pd

from .modules.derivatives.greeks.base import GreeksCalculator, GreeksModel, OptionType
from .modules.derivatives.options.base import OptionsIndicatorCalculator, PCRBy


def _get_options_indicator_calculator() -> OptionsIndicatorCalculator:
    """Create an OptionsIndicatorCalculator via factory."""
    from .modules.derivatives.options.factory import OptionsIndicatorFactory

    return OptionsIndicatorFactory.get_calculator()


def _get_greeks_calculator() -> GreeksCalculator:
    """Create a GreeksCalculator via factory."""
    from .modules.derivatives.greeks.factory import GreeksFactory

    return GreeksFactory.get_calculator()


def get_options_pcr(chain_df: pd.DataFrame, by: PCRBy = "open_interest") -> pd.DataFrame:
    """Calculate Put-Call Ratio.

    计算 PCR，支持按成交量或持仓量。

    Args:
        chain_df: Options chain DataFrame.
        by: 'volume' or 'open_interest', default 'open_interest'.

    Returns:
        pd.DataFrame: [pcr, put_value, call_value]
    """
    return _get_options_indicator_calculator().get_pcr(chain_df, by)


def get_options_max_pain(chain_df: pd.DataFrame) -> pd.DataFrame:
    """Calculate Max Pain.

    计算最大痛点。

    Args:
        chain_df: Options chain DataFrame.

    Returns:
        pd.DataFrame: [max_pain_strike, total_pain]
    """
    return _get_options_indicator_calculator().get_max_pain(chain_df)


def get_iv_rank(iv_series: pd.DataFrame, window: int = 252) -> pd.DataFrame:
    """Calculate IV Rank: (current - min) / (max - min) within rolling window.

    Args:
        iv_series: Historical IV with columns [timestamp, iv].
        window: Lookback window, default 252.

    Returns:
        pd.DataFrame: [timestamp, iv, iv_rank] where iv_rank is in [0, 1].
    """
    return _get_options_indicator_calculator().get_iv_rank(iv_series, window)


def get_iv_percentile(iv_series: pd.DataFrame, window: int = 252) -> pd.DataFrame:
    """Calculate IV Percentile: fraction of prior values below current within window.

    Args:
        iv_series: Historical IV with columns [timestamp, iv].
        window: Lookback window, default 252.

    Returns:
        pd.DataFrame: [timestamp, iv, iv_percentile] where iv_percentile is in [0, 1].
    """
    return _get_options_indicator_calculator().get_iv_percentile(iv_series, window)


def get_iv_smile(
    chain_df: pd.DataFrame,
    expiration: str,
    underlying_price: float | None = None,
) -> pd.DataFrame:
    """Get IV smile curve for a specific expiration using OTM contracts.

    使用 OTM call (K >= ATM) + OTM put (K <= ATM) 构建标准 IV 微笑曲线。
    深度 ITM 合约被过滤（行业标准做法）。

    Args:
        chain_df: Options chain DataFrame (needs option_type column).
        expiration: Target expiration date.
        underlying_price: Spot price for moneyness and OTM filtering.

    Returns:
        pd.DataFrame: [expiration, strike, iv, moneyness, iv_source]
    """
    return _get_options_indicator_calculator().get_iv_smile(
        chain_df, expiration, underlying_price=underlying_price
    )


def get_iv_term_structure(
    chain_df: pd.DataFrame, underlying_price: float | None = None
) -> pd.DataFrame:
    """Calculate IV term structure across expirations.

    提取各到期日 ATM IV 构建期限结构。若提供 underlying_price 则用最接近标的价格
    的行权价作为 ATM；否则回退至 median strike 近似。

    Args:
        chain_df: Options chain DataFrame.
        underlying_price: Underlying/spot price for ATM identification. Optional.

    Returns:
        pd.DataFrame: [expiration, iv_atm]
    """
    return _get_options_indicator_calculator().get_iv_term_structure(
        chain_df, underlying_price=underlying_price
    )


def calculate_greeks(
    S: float,
    K: float,
    T: float,
    r: float,
    sigma: float,
    option_type: OptionType = "call",
    model: GreeksModel = "black76",
) -> pd.DataFrame:
    """Calculate Greeks and theoretical value for a single option.

    计算单个期权的希腊值与理论价值。

    Args:
        S: Underlying price (spot for BS, futures/forward for Black-76).
        K: Strike price.
        T: Time to expiry in years.
        r: Risk-free rate (annualized).
        sigma: Volatility (annualized).
        option_type: 'call' or 'put', default 'call'.
        model: 'black76' or 'bs', default 'black76'.

    Returns:
        pd.DataFrame (single row): delta/gamma/theta/vega/rho/iv/theoretical_value
    """
    return _get_greeks_calculator().calculate_greeks(
        S=S, K=K, T=T, r=r, sigma=sigma, option_type=option_type, model=model,
    )


def calculate_iv(
    S: float,
    K: float,
    T: float,
    r: float,
    option_price: float,
    option_type: OptionType = "call",
    model: GreeksModel = "black76",
) -> float:
    """Solve implied volatility from option market price.

    根据期权市场价格反解隐含波动率。

    Args:
        S: Underlying price.
        K: Strike price.
        T: Time to expiry in years.
        r: Risk-free rate (annualized).
        option_price: Option market price.
        option_type: 'call' or 'put', default 'call'.
        model: 'black76' or 'bs', default 'black76'.

    Returns:
        float: Implied volatility (annualized).
    """
    return _get_greeks_calculator().calculate_iv(
        S=S, K=K, T=T, r=r, option_price=option_price,
        option_type=option_type, model=model,
    )
