from __future__ import annotations

import re
from copy import deepcopy
from datetime import datetime, timezone
from typing import Any, Dict

from src.market_context import normalize_regime_label, normalize_session_bucket


TREND_REGIMES = {"STRONG_TREND", "WEAK_TREND", "STEALTH_TREND", "TRENDING"}
RANGE_REGIMES = {"RANGING", "LOW_VOLATILITY", "CONSOLIDATION"}
BLOCKED_REGIMES = {"FAKE_TREND", "VOLATILITY_SPIKE", "EXTREME", "SHOCK_REPRICING"}
TRANSITION_REGIMES = {"TRANSITION", "UNCERTAIN", "UNKNOWN"}


def _safe_float(value: Any, default: float = 0.0) -> float:
    try:
        return float(value or 0.0)
    except (TypeError, ValueError):
        return float(default)


def _normalize_direction(value: Any) -> str:
    direction = str(value or "").strip().upper()
    return direction if direction in {"LONG", "SHORT", "NEUTRAL"} else "NEUTRAL"


def _extract_flag_float(flags: list[Any], key: str) -> float | None:
    pattern = re.compile(rf"{re.escape(key)}=([-+]?\d+(?:\.\d+)?)", re.IGNORECASE)
    for item in flags:
        text = str(item or "").strip()
        if not text:
            continue
        match = pattern.search(text)
        if not match:
            continue
        try:
            return float(match.group(1))
        except (TypeError, ValueError):
            return None
    return None


def _infer_raw_metrics(market_context: Dict[str, Any], interpreter_context: Dict[str, Any]) -> dict[str, float]:
    flags = market_context.get("regime_flags", []) if isinstance(market_context.get("regime_flags", []), list) else []
    raw_adx = _safe_float(market_context.get("adx", interpreter_context.get("adx", 0.0)), 0.0)
    if raw_adx <= 0.0:
        inferred_adx = _extract_flag_float(flags, "adx")
        if inferred_adx is not None:
            raw_adx = inferred_adx

    raw_volume_ratio = _safe_float(
        market_context.get("volume_ratio", interpreter_context.get("volume_ratio", 0.0)),
        0.0,
    )
    if raw_volume_ratio <= 0.0:
        inferred_volume = _extract_flag_float(flags, "vol_ratio")
        if inferred_volume is None:
            inferred_volume = _extract_flag_float(flags, "volume_ratio")
        if inferred_volume is not None:
            raw_volume_ratio = inferred_volume

    raw_ema_slope = _safe_float(
        market_context.get("ema_slope", interpreter_context.get("ema_slope", 0.0)),
        0.0,
    )
    if raw_ema_slope == 0.0:
        inferred_slope = _extract_flag_float(flags, "ema_slope")
        if inferred_slope is not None:
            raw_ema_slope = inferred_slope
        else:
            direction_flag = next(
                (str(item or "").strip().upper() for item in flags if str(item or "").startswith("direction=")),
                "",
            )
            if direction_flag.endswith("BULLISH"):
                raw_ema_slope = 0.0001
            elif direction_flag.endswith("BEARISH"):
                raw_ema_slope = -0.0001

    return {
        "adx": round(raw_adx, 4),
        "ema_slope": round(raw_ema_slope, 6),
        "volume_ratio": round(raw_volume_ratio, 4),
    }


def _normalize_volatility_state(raw_regime: str, atr_ratio: float, subtype: str) -> str:
    try:
        from bridge.garch_volatility import get_vol_multiplier
        vol = get_vol_multiplier(refresh=False)
        if isinstance(vol, dict) and vol.get("status") in ("ok", "stale"):
            garch_regime = str(vol.get("regime", "NORMAL")).upper()
            if garch_regime in ("HIGH", "ELEVATED"):
                return "HIGH"
            if garch_regime == "LOW":
                return "LOW"
    except Exception:
        pass
    subtype_upper = str(subtype or "").upper()
    if raw_regime in {"VOLATILITY_SPIKE", "EXTREME"} or atr_ratio >= 1.35:
        return "HIGH"
    if raw_regime in {"LOW_VOLATILITY", "RANGING"} or "ASIA" in subtype_upper or atr_ratio <= 0.85:
        return "LOW"
    return "NORMAL"


def _normalize_structure_state(
    market_structure: str,
    market_family: str,
    volatility_state: str,
) -> str:
    structure = str(market_structure or "").strip().upper()
    if structure in {"RANGE", "RANGING", "CLEAR_RANGE", "BOX"}:
        return "CLEAR_RANGE"
    if structure in {"TREND", "TRENDING", "EMA_STACK", "STRUCTURE_BREAK"}:
        return "TREND_CONTINUATION"
    if structure in {"PULLBACK", "PULLBACK_READY", "TREND_PULLBACK"}:
        return "TREND_PULLBACK"
    if structure in {"BREAKOUT", "BREAKOUT_READY"}:
        return "BREAKOUT_READY"
    if market_family == "RANGE":
        return "CLEAR_RANGE" if volatility_state != "HIGH" else "CHAOTIC"
    if market_family == "TREND":
        return "TREND_CONTINUATION"
    if market_family == "BLOCKED":
        return "CHAOTIC"
    return "STRUCTURE_UNCLEAR"


def _normalize_market_family(
    raw_regime: str,
    raw_subtype: str,
    regime_confidence: float,
    session_bucket: str,
) -> str:
    if raw_regime in BLOCKED_REGIMES:
        return "BLOCKED"
    if raw_regime in TREND_REGIMES:
        return "TREND"
    if raw_regime in RANGE_REGIMES:
        return "RANGE"
    subtype_upper = str(raw_subtype or "").upper()
    if raw_regime in TRANSITION_REGIMES:
        if session_bucket == "ASIA" and subtype_upper in {"ASIA_NORMAL", "ASIA_DEFENSIVE", "ASIA_NOISE"}:
            return "RANGE"
        if regime_confidence < 0.45:
            return "TRANSITION"
    return "TRANSITION"


def _normalize_market_phase(
    market_family: str,
    structure_state: str,
    raw_subtype: str,
) -> str:
    subtype_upper = str(raw_subtype or "").upper()
    if market_family == "TREND":
        if structure_state == "TREND_PULLBACK":
            return "TREND_PULLBACK"
        if "BREAKOUT" in subtype_upper or structure_state == "BREAKOUT_READY":
            return "BREAKOUT_READY"
        return "TREND_CONTINUATION"
    if market_family == "RANGE":
        if structure_state == "CLEAR_RANGE":
            return "OSCILLATION"
        if "EXTREME" in subtype_upper:
            return "RANGE_EXTREME"
        return "OSCILLATION"
    if market_family == "BLOCKED":
        return "CHAOTIC"
    return "TRANSITION"


def _normalize_entry_family(market_family: str, regime_confidence: float) -> str:
    if market_family == "TREND":
        return "trend"
    if market_family == "RANGE":
        return "range"
    if market_family == "BLOCKED":
        return "hold"
    if regime_confidence >= 0.7:
        return "trend"
    return "hold"


def build_canonical_market_snapshot(
    market_context: Dict[str, Any],
    *,
    macro_context: Dict[str, Any] | None = None,
    interpreter_context: Dict[str, Any] | None = None,
    symbol: str = "XAUUSD",
    timeframe: str = "M5",
) -> Dict[str, Any]:
    market_context = market_context if isinstance(market_context, dict) else {}
    macro_context = macro_context if isinstance(macro_context, dict) else {}
    interpreter_context = interpreter_context if isinstance(interpreter_context, dict) else {}

    raw_regime = normalize_regime_label(
        market_context.get("regime")
        or interpreter_context.get("market_regime")
        or interpreter_context.get("market_structure")
        or "UNKNOWN"
    )
    raw_subtype = str(
        market_context.get("regime_subtype")
        or interpreter_context.get("regime_subtype")
        or ""
    ).strip().upper()
    regime_confidence = max(
        0.0,
        min(
            1.0,
            _safe_float(
                market_context.get("effective_regime_confidence")
                or market_context.get("regime_confidence", interpreter_context.get("regime_confidence", 0.0)),
                0.0,
            ),
        ),
    )
    session_bucket = normalize_session_bucket(
        market_context.get("session_bucket")
        or market_context.get("session_mode")
        or interpreter_context.get("session_name")
        or ""
    )
    atr_ratio = _safe_float(
        market_context.get("atr_ratio", interpreter_context.get("atr_ratio", 0.0)),
        0.0,
    )
    market_family = _normalize_market_family(raw_regime, raw_subtype, regime_confidence, session_bucket)
    volatility_state = _normalize_volatility_state(raw_regime, atr_ratio, raw_subtype)
    structure_state = _normalize_structure_state(
        str(
            interpreter_context.get("market_structure")
            or market_context.get("market_structure")
            or ""
        ),
        market_family,
        volatility_state,
    )
    market_phase = _normalize_market_phase(market_family, structure_state, raw_subtype)
    entry_family = _normalize_entry_family(market_family, regime_confidence)
    direction_bias = _normalize_direction(
        macro_context.get("binding_direction")
        or macro_context.get("effective_direction")
        or macro_context.get("direction")
    )
    snapshot_id = (
        f"{datetime.now(timezone.utc).strftime('%Y%m%dT%H%M%SZ')}_"
        f"{str(symbol or 'XAUUSD').upper()}_{str(timeframe or 'M5').upper()}"
    )
    raw_metrics = _infer_raw_metrics(market_context, interpreter_context)

    return {
        "snapshot_id": snapshot_id,
        "generated_at": datetime.now(timezone.utc).isoformat().replace("+00:00", "Z"),
        "symbol": str(symbol or "XAUUSD").upper(),
        "timeframe": str(timeframe or "M5").upper(),
        "raw": {
            "regime": raw_regime,
            "regime_subtype": raw_subtype,
            "market_structure": str(
                interpreter_context.get("market_structure")
                or market_context.get("market_structure")
                or "UNKNOWN"
            ).strip().upper(),
            "adx": raw_metrics["adx"],
            "ema_slope": raw_metrics["ema_slope"],
            "atr_ratio": atr_ratio,
            "volume_ratio": raw_metrics["volume_ratio"],
        },
        "normalized": {
            "market_family": market_family,
            "market_phase": market_phase,
            "entry_family": entry_family,
            "session_bucket": session_bucket or "UNKNOWN",
            "direction_bias": direction_bias,
            "volatility_state": volatility_state,
            "structure_state": structure_state,
            "confidence": regime_confidence,
            "normalization_reason": (
                f"{raw_regime or 'UNKNOWN'}:"
                f"{raw_subtype or 'NO_SUBTYPE'}:"
                f"{session_bucket or 'UNKNOWN'}:"
                f"{market_family}:{market_phase}"
            ),
        },
        "source_context": {
            "market_context": deepcopy(market_context),
            "macro_context": deepcopy(macro_context),
            "interpreter_context": deepcopy(interpreter_context),
        },
    }
