from __future__ import annotations
import numpy as np
from ocf.surfaces.interpolators import interpolate_sigma


def map_sigma(
    *,
    row: dict,
    strike: float,
    spot: float,
    days_to_expiry: float,
    method: str = "auto",
) -> float:
    """
    Maps an option to implied volatility (decimal).
    """

    if days_to_expiry <= 0 or spot <= 0 or strike <= 0:
        return np.nan

    # Smile interpolation
    if method in {"auto", "moneyness", "strict"}:
        try:
            sigma_pct = interpolate_sigma(
                row=row,
                strike=strike,
                spot=spot,
                t_days=days_to_expiry,
            )
            if np.isfinite(sigma_pct):
                return sigma_pct / 100.0
        except Exception:
            if method in {"moneyness", "strict"}:
                raise

    # ATM term structure fallback
    if method in {"auto"}:
        iv30 = row.get("iv_30_m100")
        iv60 = row.get("iv_60_m100")

        if iv30 and iv60 and iv30 > 0 and iv60 > 0:
            w = np.clip(days_to_expiry / 30.0, 0.0, 2.0)
            sigma_pct = iv30 + (iv60 - iv30) * w
            return sigma_pct / 100.0

    # Historical ATM fallback
    iv_hist = row.get("iv_atm_hist")
    if iv_hist and iv_hist > 0:
        return iv_hist / 100.0

    return np.nan