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

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

from bridge.strategy import _read_bars
from config_manager import get_logs_dir
from src.agent_role_router import ROLE_TREND_ANALYST, call_llm
from src.market_structure import compute_local_structure, get_structure_memory_context, merge_structure_snapshot, write_structure_snapshot
from src.trading_context import get_trading_context as _get_ctx

_ctx = None  # lazy init — resolved per-instrument at call time


def _resolve_ctx():
    global _ctx
    if _ctx is None:
        _ctx = _get_ctx()  # auto-detects active symbol
    return _ctx

def extract_json_block(text):
    """Extract JSON from LLM output (delegates to shared utility)."""
    try:
        from src.json_utils import extract_json_block as _extract
        return _extract(text)
    except Exception as e:
        print(f"Error parsing JSON: {e}")
        return None

def run_market_interpreter():
    print(f"[{datetime.now()}] Gathering data for Market Interpreter...")

    m5_bars = _read_bars(count=40, timeframe="M5")
    h1_bars = _read_bars(count=20, timeframe="H1")

    if not m5_bars or len(m5_bars) < 10:
        print("Not enough M5 bars fetched. Aborting.")
        return

    # 宏观方向读取：contract → direction_file → blackboard → 默认 NEUTRAL
    macro_data = {"direction": "NEUTRAL", "confidence": 0.5}
    try:
        from agent_decision_contract import load_macro_contract_payload
        contract = load_macro_contract_payload()
        if contract and contract.get("direction") in ("LONG", "SHORT", "NEUTRAL"):
            macro_data = {"direction": contract["direction"], "confidence": contract.get("confidence", 0.5)}
    except Exception:
        pass
    if macro_data["direction"] == "NEUTRAL":
        direction_file = get_logs_dir() / "agent_decisions" / f"direction_{time.strftime('%Y%m%d')}.json"
        if direction_file.exists():
            try:
                macro_data = json.loads(direction_file.read_text(encoding="utf-8"))
            except Exception:
                pass

    def format_bars(bars):
        res = []
        for b in bars:
            res.append({
                "time": b.get("time", ""),
                "open": b.get("open", 0),
                "high": b.get("high", 0),
                "low": b.get("low", 0),
                "close": b.get("close", 0),
                "vol": b.get("tick_volume", 0)
            })
        return res

    m5_formatted = format_bars(m5_bars[-20:])
    h1_formatted = format_bars(h1_bars[-10:])
    local_snapshot = compute_local_structure(m5_bars, h1_bars, macro_data)
    structure_memory = get_structure_memory_context()

    prompt = f"""
You are the Real-time Market Interpreter for a {_resolve_ctx().role_identity_en} algorithmic trading system.
Your job is to read recent M5 and H1 price action and identify structure that math rules might miss, especially:
- opening drive / open extension
- event impulse / shock repricing
- high-open or low-open continuation traps
- first pullback after a violent move
- fake continuation versus healthy pullback

Current Macro Direction: {macro_data.get('direction')} (Confidence: {macro_data.get('confidence')})

Rule-based structure snapshot:
{json.dumps(local_snapshot, indent=2)}

Recent structure memory:
{json.dumps(structure_memory, indent=2)}

Recent 10 H1 Bars (Older to Newer):
{json.dumps(h1_formatted, indent=2)}

Recent 20 M5 Bars (Older to Newer):
{json.dumps(m5_formatted, indent=2)}

Analyze the price action:
1. Classify the market structure.
2. Determine the dominant direction.
3. Determine whether there is a shock direction and whether the market is in the first pullback phase.
4. Judge pullback depth, pullback quality, and confirmation state.
5. Judge the counter-trend risk.
6. Decide whether execution should block counter-trend, wait, or limit-only.
7. Classify special structures like OPEN_DRIVE, FAILED_BREAKOUT, LIQUIDITY_SWEEP, GAP_CONTINUATION, IMPULSE_EXHAUSTION when applicable.
8. Set BUY/SELL multipliers for the next 10-15 minutes.
9. Output fine-grained regime labels for execution routing.

Output EXACTLY in this JSON format:
{{
  "view": "BULLISH_REPRICING",
  "market_structure": "SHOCK_REPRICING",
  "dominant_direction": "BULLISH",
  "shock_direction": "BULLISH",
  "pullback_phase": "FIRST_PULLBACK",
  "pullback_depth_bucket": "NORMAL",
  "pullback_quality": "HEALTHY",
  "confirmation_state": "READY",
  "retrace_ratio": 0.38,
  "session_name": "ASIA_LONDON_OVERLAP",
  "session_execution_bias": "OPEN_DRIVE_WINDOW",
  "regime_subtype": "FLAG_COMPRESSION",
  "volatility_state": "LOW",
  "liquidity_state": "THIN",
  "continuation_bias": "BULLISH",
  "entry_timing_hint": "WAIT_PULLBACK",
  "llm_confidence": 0.76,
  "counter_trend_risk": "HIGH",
  "execution_advice": "BLOCK_COUNTER_TREND",
  "buy_multiplier": 0.95,
  "sell_multiplier": 0.45,
  "reason": "..."
}}
"""

    print("Calling LLM Market Interpreter...")
    payload = None
    last_error = ""
    max_retries = 3
    for attempt in range(1, max_retries + 1):
        try:
            content = call_llm(ROLE_TREND_ANALYST, prompt, temperature=0.1, max_tokens=1500, timeout=120)
            if not content:
                raise ValueError("LLM returned empty output.")

            data = extract_json_block(content)
            if not data:
                raise ValueError(f"Invalid JSON returned by LLM: {content[:300]}")

            payload = merge_structure_snapshot(data, local_snapshot)
            payload["source"] = "market_interpreter_llm"
            break  # success — exit retry loop

        except Exception as e:
            last_error = str(e)
            print(f"Market Interpreter attempt {attempt}/{max_retries} failed: {last_error[:120]}")
            if attempt < max_retries:
                import time as _time
                _time.sleep(1.0 * attempt)

    if payload is None:
        print(f"Market Interpreter all {max_retries} attempts failed: {last_error[:200]}")
        payload = dict(local_snapshot)

    # 乘数边界校验：防止 LLM 返回极端值
    buy_mult = float(payload.get("buy_multiplier", 1.0) or 1.0)
    sell_mult = float(payload.get("sell_multiplier", 1.0) or 1.0)
    payload["buy_multiplier"] = round(max(0.10, min(2.00, buy_mult)), 3)
    payload["sell_multiplier"] = round(max(0.10, min(2.00, sell_mult)), 3)

    payload["timestamp"] = time.time()
    payload["valid_until"] = time.time() + 30  # 30s validity — stale by design, triggers async refresh
    write_structure_snapshot(payload)
    print(f"Successfully saved signal override: {payload}")

    try:
        from src.swarm.blackboard import global_blackboard
        global_blackboard.update("Market_Interpreter", "view", payload["view"])
        global_blackboard.update("Market_Interpreter", "buy_multiplier", payload["buy_multiplier"])
        global_blackboard.update("Market_Interpreter", "sell_multiplier", payload["sell_multiplier"])
        global_blackboard.update("Market_Interpreter", "reason", payload["reason"])
        global_blackboard.update("Market_Interpreter", "market_structure", payload.get("market_structure", "UNCERTAIN"))
        global_blackboard.update("Market_Interpreter", "dominant_direction", payload.get("dominant_direction", "MIXED"))
        global_blackboard.update("Market_Interpreter", "shock_direction", payload.get("shock_direction", "NONE"))
        global_blackboard.update("Market_Interpreter", "pullback_phase", payload.get("pullback_phase", "NONE"))
        global_blackboard.update("Market_Interpreter", "counter_trend_risk", payload.get("counter_trend_risk", "LOW"))
        global_blackboard.update("Market_Interpreter", "execution_advice", payload.get("execution_advice", "ALLOW_FOLLOW"))
        global_blackboard.update("Market_Interpreter", "regime_subtype", payload.get("regime_subtype", "STRUCTURE_UNCLEAR"))
        global_blackboard.update("Market_Interpreter", "volatility_state", payload.get("volatility_state", "NORMAL"))
        global_blackboard.update("Market_Interpreter", "liquidity_state", payload.get("liquidity_state", "NORMAL"))
        global_blackboard.update("Market_Interpreter", "continuation_bias", payload.get("continuation_bias", "NEUTRAL"))
        global_blackboard.update("Market_Interpreter", "entry_timing_hint", payload.get("entry_timing_hint", "WAIT_CLEAR_SIGNAL"))
        global_blackboard.update("Market_Interpreter", "llm_confidence", payload.get("llm_confidence", 0.55))
        global_blackboard.update("Market_Interpreter", "status", "DONE")
        global_blackboard.update("Market_Interpreter", "last_updated", time.time())
        global_blackboard.add_log(
            "Market_Interpreter",
            "ANALYSIS",
            f"盘面解读: {payload['market_structure']}->{payload.get('regime_subtype', 'STRUCTURE_UNCLEAR')} {payload.get('shock_direction', 'NONE')} {payload.get('pullback_phase', 'NONE')} (BUY x{payload['buy_multiplier']} SELL x{payload['sell_multiplier']})",
        )
    except Exception as e:
        print(f"Failed to sync to blackboard: {e}")

if __name__ == "__main__":
    run_market_interpreter()
