from __future__ import annotations

import json
from pathlib import Path
from typing import Any

import config_manager


def _normalize_direction(value: Any) -> str:
    text = str(value or "NEUTRAL").strip().upper()
    aliases = {
        "BUY": "LONG",
        "BULL": "LONG",
        "LONG": "LONG",
        "SELL": "SHORT",
        "BEAR": "SHORT",
        "SHORT": "SHORT",
        "NONE": "NEUTRAL",
        "FLAT": "NEUTRAL",
        "NEUTRAL": "NEUTRAL",
    }
    return aliases.get(text, "NEUTRAL")


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


def resolve_macro_authority(
    direction: Any,
    *,
    state: str = "UNKNOWN",
    degraded: bool = False,
    bias_weight: float = 0.0,
    disempowered: bool = False,
) -> str:
    normalized_direction = _normalize_direction(direction)
    normalized_state = str(state or "UNKNOWN").strip().upper()
    effective_bias = max(0.0, min(1.0, _safe_float(bias_weight, 0.0)))

    # ── 修复1: 惩罚性开关（用户主动关闭 / guard 硬惩罚）→ DISABLED ──
    if disempowered:
        return "DISABLED"

    # ── 修复2: NEUTRAL 不再直接 DISABLED ──
    # 理由: LLM 判断 NEUTRAL 意味着"当前无显著方向优势"，
    # 策略应收到 SOFT_BIAS（允许双向，只降权），而非完全封锁宏观通道。
    if normalized_direction == "NEUTRAL":
        return "SOFT_BIAS"

    # ── 修复3: degraded/stale 场景仍然是 SOFT_BIAS ──
    if degraded or normalized_state in {"DEGRADED", "STALE", "ERROR", "UNKNOWN"}:
        return "SOFT_BIAS"

    # ── HARD_BIND 阈值 0.55：只有高置信度方向才能限单边 ──
    # 低于此阈值的所有方向设为 SOFT_BIAS（方向为加权偏好，不锁边）。
    # 历史 0.35 阈值导致低置信度方向也锁死，系统无法根据市况切换。
    if effective_bias >= 0.55:
        return "HARD_BIND"

    return "SOFT_BIAS"


def resolve_allowed_side(binding_direction: Any, macro_authority: Any) -> str:
    authority = str(macro_authority or "DISABLED").strip().upper()
    direction = _normalize_direction(binding_direction)
    if authority != "HARD_BIND":
        return "BOTH"
    if direction == "LONG":
        return "BUY"
    if direction == "SHORT":
        return "SELL"
    return "BOTH"


def build_macro_direction_snapshot(
    macro_payload: dict[str, Any] | None,
    policy: dict[str, Any] | None,
) -> dict[str, Any]:
    payload = macro_payload if isinstance(macro_payload, dict) else {}
    macro_policy = policy if isinstance(policy, dict) else {}

    raw_direction = payload.get(
        "direction",
        payload.get("binding_direction", payload.get("effective_direction", "NEUTRAL")),
    )
    direction = _normalize_direction(raw_direction)

    degraded = bool(payload.get("degraded", False))
    state = str(payload.get("state", payload.get("macro_state", "")) or "").upper()
    # 当 Commander 未显式设置 state 时，从 degraded / phase 推导，避免 UNKNOWN 永远 SOFT_BIAS
    if not state:
        if degraded:
            state = "DEGRADED"
        else:
            phase = str(payload.get("phase", "") or "").upper()
            state = "FRESH" if phase in {"FRESH", "ACTIVE"} else "UNKNOWN"
    bias_weight = max(
        0.0,
        min(
            1.0,
            _safe_float(
                payload.get(
                    "bias_weight",
                    payload.get("confidence",
                        payload.get("probability",
                            macro_policy.get("default_bias_weight", 0.5))),
                ),
                0.5,
            ),
        ),
    )
    direction_guard = payload.get("direction_guard", {})
    if not isinstance(direction_guard, dict):
        direction_guard = {}
    disempowered = bool(payload.get("direction_disempowered", False))
    if str(direction_guard.get("state", "inactive") or "inactive").lower() == "hard_penalty":
        disempowered = True

    authority = resolve_macro_authority(
        direction,
        state=state,
        degraded=degraded,
        bias_weight=bias_weight,
        disempowered=disempowered,
    )

    effective_direction = _normalize_direction(
        payload.get("effective_direction", direction if authority != "DISABLED" else "NEUTRAL")
    )
    if authority == "DISABLED":
        effective_direction = "NEUTRAL"

    binding_direction = _normalize_direction(payload.get("binding_direction", effective_direction))
    # ── 修复5: SOFT_BIAS 透传方向，不再强制清零 ──
    # HARD_BIND → 策略必须遵守方向
    # SOFT_BIAS → 方向作为建议传递给策略（可降权、可不遵守）
    # DISABLED → 完全关闭宏观通道
    if authority == "DISABLED":
        binding_direction = "NEUTRAL"
    elif authority == "SOFT_BIAS":
        # 保留 LLM 判断的方向作为"软建议"，不强制 NEUTRAL
        binding_direction = effective_direction if effective_direction != "NEUTRAL" else direction
    # else HARD_BIND: binding_direction stays as resolved

    return {
        "direction": direction,
        "effective_direction": effective_direction,
        "binding_direction": binding_direction,
        "macro_authority": authority,
        "allowed_side": resolve_allowed_side(binding_direction, authority),
        "state": state,
        "degraded": degraded,
        "bias_weight": round(bias_weight, 4),
        "direction_guard": direction_guard,
        "direction_disempowered": disempowered,
    }


def load_decision_snapshot() -> dict[str, Any]:
    """Load the latest macro direction decision snapshot from disk."""
    p = config_manager.get_data_dir() / "bridge" / "state" / "agent_state.json"
    if not p.exists():
        return build_macro_direction_snapshot(None, None)
    try:
        data = json.loads(p.read_text(encoding="utf-8"))
        if not isinstance(data, dict):
            return build_macro_direction_snapshot(None, None)
        direction_data = data.get("agent_direction")
        if isinstance(direction_data, dict):
            return build_macro_direction_snapshot(direction_data, data.get("policy"))
        # Only fallback to entire data if it looks like a macro payload (has direction key)
        if isinstance(data.get("direction"), str):
            return build_macro_direction_snapshot(data, None)
        return build_macro_direction_snapshot(None, None)
    except Exception:
        return build_macro_direction_snapshot(None, None)
