"""direction_registry.py — Direction file read/write operations.

Extracted from gold_trader.py to eliminate circular import coupling
between deepseek_agent.py -> gold_trader.py.

This module is self-contained: no imports from bridge/ or src/.
"""

from __future__ import annotations

import calendar
import json
import sys
import time
import pathlib
from datetime import datetime

ROOT = pathlib.Path(__file__).resolve().parent.parent
sys.path.insert(0, str(ROOT))

import config_manager

LOGS_DIR = config_manager.get_logs_dir()
AGENT_DIR = LOGS_DIR / "agent_decisions"


def _parse_updated_iso_to_ts(raw: object) -> float:
    text = str(raw or "").strip()
    if not text:
        return 0.0
    for fmt in ("%Y-%m-%dT%H:%M:%SZ", "%Y-%m-%d %H:%M:%S"):
        try:
            dt = datetime.strptime(text, fmt)
            return float(calendar.timegm(dt.timetuple()))
        except ValueError:
            continue
    return 0.0


def _effective_updated_ts(payload: dict, decision_file: pathlib.Path | None = None) -> float:
    candidates: list[float] = []
    try:
        payload_ts = float(payload.get("updated_ts", 0.0) or 0.0)
    except Exception:
        payload_ts = 0.0
    if payload_ts > 0:
        candidates.append(payload_ts)
    parsed_iso_ts = _parse_updated_iso_to_ts(payload.get("updated"))
    if parsed_iso_ts > 0:
        candidates.append(parsed_iso_ts)
    if not candidates and decision_file is not None:
        try:
            candidates.append(float(decision_file.stat().st_mtime))
        except Exception:
            pass
    return max(candidates) if candidates else 0.0


def save_agent_direction(
    direction, reason, confidence,
    indicators=None, probability=None, phase=None, degraded=False, metadata=None,
    conviction=None, timeframe=None, risk_appetite=None, key_levels=None, macro_regime=None,
) -> dict:
    """保存 Agent 方向决策到 direction_{today}.json（核心文件写入，不含 side effects）.

    返回写入的 data dict.
    """
    AGENT_DIR.mkdir(parents=True, exist_ok=True)
    today = time.strftime("%Y%m%d")
    decision_file = AGENT_DIR / f"direction_{today}.json"
    normalized_direction = str(direction or "NEUTRAL").upper()
    normalized_probability = float(probability if probability is not None else confidence or 0.5)
    normalized_probability = max(0.05, min(0.99, normalized_probability))
    if degraded:
        normalized_probability = min(normalized_probability, 0.49)

    data = {
        "date": today,
        "direction": normalized_direction,
        "reason": reason,
        "confidence": confidence,
        "probability": normalized_probability,
        "phase": str(phase or ("DEGRADED" if degraded else "FRESH") or "FRESH").upper(),
        "degraded": bool(degraded),
        "indicators": indicators or {},
        "metadata": metadata if isinstance(metadata, dict) else {},
        "updated": time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()),
        "updated_ts": time.time(),
        "expires": int(time.time()) + 86400,
    }
    vector = {}
    if conviction is not None:
        vector["conviction"] = conviction
    if timeframe is not None:
        vector["timeframe"] = timeframe
    if risk_appetite is not None:
        vector["risk_appetite"] = risk_appetite
    if key_levels is not None:
        vector["key_levels"] = key_levels
    if macro_regime is not None:
        vector["macro_regime"] = macro_regime
    if vector:
        data["commander_vector"] = vector

    try:
        decision_file.write_text(json.dumps(data, indent=2))
    except Exception:
        pass
    return data


def load_macro_direction_record() -> tuple[str, dict]:
    """读取 Agent 方向决策（核心文件读取）.

    返回: (direction_str, data_dict)
    """
    AGENT_DIR.mkdir(parents=True, exist_ok=True)
    today = time.strftime("%Y%m%d")
    decision_file = AGENT_DIR / f"direction_{today}.json"

    if not decision_file.exists():
        return "NEUTRAL", {
            "engine": "macro_llm_direction",
            "direction": "NEUTRAL",
            "probability": 0.5,
            "phase": "UNKNOWN",
            "reason": "macro_direction_missing",
            "updated_ts": 0.0,
            "macro_age_sec": 999999.0,
            "macro_stale": True,
            "degraded": True,
            "source": "file",
        }

    try:
        payload = json.loads(decision_file.read_text(encoding="utf-8"))
    except Exception as e:
        return "NEUTRAL", {
            "engine": "macro_llm_direction",
            "direction": "NEUTRAL",
            "probability": 0.5,
            "phase": "UNKNOWN",
            "reason": f"macro_direction_parse_error:{e}",
            "updated_ts": 0.0,
            "macro_age_sec": 999999.0,
            "macro_stale": True,
            "degraded": True,
            "source": "file",
        }

    direction = str(payload.get("direction", "NEUTRAL") or "NEUTRAL").upper()
    if direction not in {"LONG", "SHORT", "NEUTRAL"}:
        direction = "NEUTRAL"
    probability = float(payload.get("probability", payload.get("confidence", 0.5)) or 0.5)
    degraded = bool(payload.get("degraded", False))
    updated_ts = _effective_updated_ts(payload, decision_file)
    macro_age_sec = max(0.0, time.time() - updated_ts) if updated_ts > 0 else 999999.0
    stale_sec = float(config_manager.get_effective_param("macro_direction_stale_sec", 7200) or 7200)
    macro_stale = degraded or macro_age_sec > max(900.0, stale_sec)
    phase = str(payload.get("phase", "") or "").upper()
    if not phase:
        phase = "DEGRADED" if degraded else ("LATE" if macro_stale else "FRESH")
    probability = max(0.05, min(0.99, probability))
    if degraded:
        probability = min(probability, 0.49)

    data = {
        "engine": "macro_llm_direction",
        "direction": direction,
        "probability": probability,
        "phase": phase,
        "reason": str(payload.get("reason", "macro_llm") or "macro_llm"),
        "updated": str(payload.get("updated", "") or ""),
        "updated_ts": updated_ts,
        "macro_age_sec": round(macro_age_sec, 1),
        "macro_stale": bool(macro_stale),
        "degraded": degraded,
        "source": "file",
        "indicators": payload.get("indicators", {}) if isinstance(payload.get("indicators", {}), dict) else {},
    }
    # Parse commander_vector sub-dict (Phase 1 vectorization)
    vector = payload.get("commander_vector", {})
    if isinstance(vector, dict) and vector:
        cv = {}
        _conv = vector.get("conviction")
        if _conv is not None:
            try:
                cv["conviction"] = max(0.0, min(1.0, float(_conv)))
            except (TypeError, ValueError):
                pass
        _tf = str(vector.get("timeframe", "")) or None
        if _tf:
            cv["timeframe"] = _tf
        _ra = str(vector.get("risk_appetite", "")) or None
        if _ra:
            cv["risk_appetite"] = _ra
        _mr = str(vector.get("macro_regime", "")) or None
        if _mr:
            cv["macro_regime"] = _mr
        _kl = vector.get("key_levels")
        if isinstance(_kl, dict):
            cv["key_levels"] = {k: [float(v) for v in val] if isinstance(val, list) else val
                                for k, val in _kl.items()}
        if cv:
            data["commander_vector"] = cv

    return direction, data


def get_agent_direction() -> tuple[str, dict]:
    """读取 Agent 当天方向决策.

    返回: (LONG/SHORT/NEUTRAL, data_dict)
    """
    try:
        from src.runtime_state.decision_snapshot import load_decision_snapshot

        snapshot = load_decision_snapshot()
        macro = snapshot.get("macro", {}) if isinstance(snapshot.get("macro", {}), dict) else {}
        contract = snapshot.get("contract", {}) if isinstance(snapshot.get("contract", {}), dict) else {}
        direction = str(
            macro.get("binding_direction", macro.get("direction", "NEUTRAL")) or "NEUTRAL"
        ).upper()
        data = {
            "engine": "macro_llm_direction",
            "direction": direction,
            "binding_direction": direction,
            "effective_direction": str(macro.get("effective_direction", "NEUTRAL") or "NEUTRAL").upper(),
            "allowed_side": str(snapshot.get("execution", {}).get("allowed_side", "BOTH") or "BOTH"),
            "probability": float(macro.get("probability", 0.5) or 0.5),
            "confidence": float(macro.get("confidence", macro.get("probability", 0.5)) or 0.5),
            "phase": str(macro.get("phase", macro.get("state", "UNKNOWN")) or "UNKNOWN").upper(),
            "reason": str(macro.get("reason", "") or ""),
            "updated": str(macro.get("updated", "") or ""),
            "updated_ts": float(macro.get("updated_ts", 0.0) or 0.0),
            "macro_age_sec": float(macro.get("age_seconds", 999999.0) or 999999.0),
            "macro_stale": str(macro.get("state", "UNKNOWN") or "UNKNOWN").upper() in {"STALE", "DEGRADED"},
            "degraded": bool(macro.get("degraded", False)),
            "source": str(macro.get("source", "macro_llm_direction") or "macro_llm_direction"),
            "lineage": contract.get("lineage", {}) if isinstance(contract.get("lineage", {}), dict) else {},
            "indicators": macro.get("indicators", {}) if isinstance(macro.get("indicators", {}), dict) else {},
            "metadata": macro.get("metadata", {}) if isinstance(macro.get("metadata", {}), dict) else {},
        }
        return direction, data
    except Exception:
        return load_macro_direction_record()
