"""garbage_detector.py — Market garbage classification and strategy routing.

Centralizes all "should we trade right now" decisions into a single classifier
that evaluates market conditions and returns a trading posture.

Garbage types and responses:
  FAKE_TREND        — trend looks strong but has no volume backing → block trend
  WHIPSAW_ZONE      — price oscillating rapidly in tight range → block all
  EXTREME_VOLATILITY — sudden ATR expansion, panic → block all
  SPREAD_ANOMALY    — liquidity crisis, wide spreads → block all
  DEAD_ZONE         — ultra-low vol, no direction → allow mean_reversion only
  ASIA_NOISE        — Asian session low liquidity → reduce size, limit entry
  CHOP_ZONE         — ADX 18-24, normal vol, no clear direction → block trend, allow mean_reversion only

Usage::

    from src.learning.garbage_detector import classify_market
    posture = classify_market(interpreter_context, engine_request)
    if posture["block_all"]:
        return  # don"t trade
    allowed = posture["allowed_types"]  # {"trend", "mean_reversion", "hybrid"}
"""

from __future__ import annotations

from typing import Any


# ── Garbage classification ─────────────────────────────────────────

def classify_market(
    interpreter: dict[str, Any] | None = None,
    engine_request: dict[str, Any] | None = None,
) -> dict[str, Any]:
    """Classify current market into trading posture.

    Returns:
        {
            "garbage_type": str or None,     # which garbage condition, if any
            "block_all": bool,                # True = no strategy should trade
            "allowed_types": set[str],        # {"trend", "mean_reversion", "hybrid"}
            "size_multiplier": float,         # 0.0-1.0 position size adjustment
            "confidence_floor_delta": float,   # added to confidence threshold
            "entry_style": str,               # "MARKET" or "LIMIT"
            "reason": str,                    # human-readable explanation
        }
    """
    interp = interpreter or {}
    req = engine_request or {}

    adx = float(interp.get("adx", 0) or 0)
    vol_ratio = float(interp.get("vol_ratio", req.get("vol_ratio", 1.0)) or 1.0)
    # Data not loaded yet — don"t block, let the next cycle decide
    if adx == 0 and vol_ratio == 1.0:
        return {
            "garbage_type": None, "block_all": False,
            "allowed_types": {"trend", "mean_reversion", "hybrid"},
            "size_multiplier": 1.0, "confidence_floor_delta": 0.0,
            "entry_style": "MARKET", "reason": "data_loading",
        }
    spread = float(interp.get("spread", req.get("spread", 0)) or 0)
    body_pct = float(interp.get("body_pct", 0) or 0)
    session_mode = str(interp.get("session_mode", req.get("session_mode", "NORMAL")) or "NORMAL").upper()

    # Default: normal trading
    result: dict[str, Any] = {
        "garbage_type": None,
        "block_all": False,
        "allowed_types": {"trend", "mean_reversion", "hybrid"},
        "size_multiplier": 1.0,
        "confidence_floor_delta": 0.0,
        "entry_style": "MARKET",
        "reason": "normal_market",
    }

    # ── Priority 1: EXTREME_VOLATILITY (panic, stop-hunting) ──
    if vol_ratio > 2.0 or body_pct > 0.08:
        result.update({
            "garbage_type": "EXTREME_VOLATILITY",
            "block_all": True,
            "allowed_types": set(),
            "size_multiplier": 0.0,
            "reason": f"extreme_volatility(vol_ratio={vol_ratio:.2f},body={body_pct:.3f})",
        })
        return result

    # ── Priority 2: SPREAD_ANOMALY (liquidity crisis) ──
    if spread > 50:
        result.update({
            "garbage_type": "SPREAD_ANOMALY",
            "block_all": True,
            "allowed_types": set(),
            "size_multiplier": 0.0,
            "reason": f"spread_anomaly(spread={spread:.0f})",
        })
        return result

    # ── Priority 3: WHIPSAW_ZONE (choppy, shreds both sides) ──
    # ADX < 18 AND vol_ratio normal = no direction + normal vol = whipsaw
    # Note: CHOP_ZONE below catches the ADX 18-24 gap.
    if adx < 18 and 0.7 <= vol_ratio <= 1.5:
        result.update({
            "garbage_type": "WHIPSAW_ZONE",
            "block_all": True,
            "allowed_types": set(),
            "size_multiplier": 0.0,
            "reason": f"whipsaw_zone(adx={adx:.0f},vol={vol_ratio:.2f})",
        })
        return result

    # ── Priority 3.5: CHOP_ZONE (ADX 18-24, normal vol = range chop) ──
    # ADX between 18-24 with normal volatility means the market lacks a clear
    # directional bias. Trend-following gets shredded here. Only mean reversion
    # at half size. WHIPSAW_ZONE (priority 3) already catches ADX < 18, so this
    # effectively targets 18 <= ADX < 24.
    if adx < 24 and 0.7 <= vol_ratio <= 1.5:
        result.update({
            "garbage_type": "CHOP_ZONE",
            "block_all": False,
            "allowed_types": {"mean_reversion"},
            "size_multiplier": 0.5,
            "confidence_floor_delta": 0.10,
            "entry_style": "LIMIT",
            "reason": f"chop_zone(adx={adx:.0f},vol={vol_ratio:.2f})",
        })
        return result

    # ── Priority 4: FAKE_TREND (looks strong but no volume) ──
    if adx >= 25 and vol_ratio < 0.7:
        result.update({
            "garbage_type": "FAKE_TREND",
            "block_all": False,
            "allowed_types": {"mean_reversion"},  # only counter-trend
            "size_multiplier": 0.5,
            "confidence_floor_delta": 0.05,
            "entry_style": "LIMIT",
            "reason": f"fake_trend(adx={adx:.0f},vol={vol_ratio:.2f})",
        })
        return result

    # ── Priority 5: DEAD_ZONE (no direction, no vol) ──
    if adx < 15 and vol_ratio < 0.5:
        result.update({
            "garbage_type": "DEAD_ZONE",
            "block_all": False,
            "allowed_types": {"mean_reversion"},
            "size_multiplier": 0.3,
            "confidence_floor_delta": 0.08,
            "entry_style": "LIMIT",
            "reason": f"dead_zone(adx={adx:.0f},vol={vol_ratio:.2f})",
        })
        return result

    # ── Priority 6: ASIA_NOISE (low liquidity session) ──
    if session_mode in ("ASIA_NOISE", "ASIA_DEFENSIVE"):
        result.update({
            "garbage_type": "ASIA_NOISE",
            "block_all": False,
            "allowed_types": {"mean_reversion", "hybrid"},
            "size_multiplier": 0.35,
            "confidence_floor_delta": 0.08,
            "entry_style": "LIMIT",
            "reason": f"asia_noise(session={session_mode},adx={adx:.0f})",
        })
        # If ADX is actually strong in ASIA, allow trend too
        if adx >= 25:
            result["allowed_types"].add("trend")
            result["size_multiplier"] = 0.5
            result["reason"] += "_strong_adx_exception"
        return result

    return result
