import math
from typing import Any, Dict, Optional, Tuple


def _safe_float(v: Any) -> Optional[float]:
    try:
        if v is None or isinstance(v, bool):
            return None
        f = float(v)
        if math.isnan(f) or math.isinf(f):
            return None
        return f
    except Exception:
        return None


def score_market_data(market_data: Dict[str, Any]) -> Dict[str, Any]:
    """Compute a deterministic opportunity score from computed indicators/strategies.

    Returns
    -------
    {
      bull: float,
      bear: float,
      opportunity: float,
      direction: 'BULLISH'|'BEARISH'|'NEUTRAL',
      quality: float (0..1),
      reasons: list[str]
    }

    Notes
    -----
    - Uses only computed fields: market_data['indicators'] and market_data['strategies'].
    - Does not require LTP/quote.
    - Designed to be stable + cheap; not meant to be a perfect trading model.
    """

    indicators = market_data.get("indicators") if isinstance(market_data, dict) else None
    strategies = market_data.get("strategies") if isinstance(market_data, dict) else None

    reasons = []

    bull = 0.0
    bear = 0.0

    def _tf_score(tf: str, w: float) -> Tuple[float, float, int]:
        if not isinstance(indicators, dict):
            return 0.0, 0.0, 0
        ind = indicators.get(tf)
        if not isinstance(ind, dict):
            return 0.0, 0.0, 0

        close = _safe_float(ind.get("close"))
        vwap = _safe_float(ind.get("vwap"))
        rsi = _safe_float(ind.get("rsi"))
        macd_hist = _safe_float(ind.get("macd_hist"))

        b = 0.0
        s = 0.0
        used = 0

        if close is not None and vwap is not None and vwap > 0:
            used += 1
            if close > vwap:
                b += 1.0 * w
            elif close < vwap:
                s += 1.0 * w

        if rsi is not None:
            used += 1
            # Keep this conservative; avoid chasing extreme RSI.
            if 52 <= rsi <= 68:
                b += 1.0 * w
            elif 32 <= rsi <= 48:
                s += 1.0 * w
            elif rsi >= 75:
                s += 0.25 * w
            elif rsi <= 25:
                b += 0.25 * w

        if macd_hist is not None:
            used += 1
            if macd_hist > 0:
                b += 1.0 * w
            elif macd_hist < 0:
                s += 1.0 * w

        return b, s, used

    # Multi-timeframe weights: intraday > daily
    b5, s5, u5 = _tf_score("5minute", 1.0)
    b15, s15, u15 = _tf_score("15minute", 1.2)
    bd, sd, ud = _tf_score("day", 0.7)

    bull += b5 + b15 + bd
    bear += s5 + s15 + sd

    used_total = u5 + u15 + ud

    # Strategy alignment bonus (if available)
    if isinstance(strategies, dict):
        mt = strategies.get("multi_timeframe")
        if isinstance(mt, dict):
            align = str(mt.get("alignment") or "").strip().lower()
            strength = _safe_float(mt.get("strength"))
            if align in ("bullish", "buy"):
                bull += 2.0
                reasons.append("MTF alignment bullish")
            elif align in ("bearish", "sell"):
                bear += 2.0
                reasons.append("MTF alignment bearish")
            if strength is not None:
                # small stabilizer; keep bounded
                if strength > 0:
                    bull += min(1.5, strength / 10.0)
                elif strength < 0:
                    bear += min(1.5, abs(strength) / 10.0)

    # Decide direction by stronger side
    opportunity = max(bull, bear)
    if opportunity <= 0.5:
        direction = "NEUTRAL"
    else:
        direction = "BULLISH" if bull >= bear else "BEARISH"

    # Data quality heuristic
    quality = 0.0
    if used_total >= 6:
        quality = 1.0
    elif used_total >= 3:
        quality = 0.7
    elif used_total >= 1:
        quality = 0.4

    if not reasons:
        # Add a small human hint to ease debugging.
        if direction == "BULLISH":
            reasons.append("indicators suggest bullish bias")
        elif direction == "BEARISH":
            reasons.append("indicators suggest bearish bias")
        else:
            reasons.append("insufficient signal")

    return {
        "bull": round(bull, 4),
        "bear": round(bear, 4),
        "opportunity": round(opportunity, 4),
        "direction": direction,
        "quality": quality,
        "reasons": reasons[:5],
    }
