"""
PromptTemplateEngine — centralized LLM prompt template management.

Replaces scattered hardcoded strings across 4+ modules.
Loads persona JSON files and provides template rendering with variable substitution.
"""

from __future__ import annotations

import json
from pathlib import Path
from typing import Any, Dict, Optional

from src.trading_context import get_trading_context, TradingContext


class PromptTemplateEngine:
    """Unified manager for all LLM prompt templates.

    Usage:
        engine = PromptTemplateEngine()
        prompt = engine.render("trend_analyst", ctx=trading_ctx)
    """

    # ── Role system prompts (template vars: {symbol}, {asset_name}, {display_name}) ──
    ROLE_PROMPTS: Dict[str, str] = {
        "trend_analyst": (
            "You are a strategic macro commander for a {asset_name} ({symbol}) "
            "algorithmic trading system. You make directional decisions based on "
            "technical data. Always output valid JSON."
        ),
        "risk_manager": (
            "你是一个专业的{symbol}交易信号验证与风险管理专家。只返回JSON，不做解释。"
        ),
        "m2_brain": (
            "你是一位专业的量化交易策略师，负责深度分析策略参数调整建议。"
        ),
        "trainer": (
            "你是一位专业的量化交易策略师与培训师，负责生成和改进交易策略代码。"
            "当前交易品种: {symbol} ({asset_name})"
        ),
        "local_patrol": (
            "You are a local patrol agent checking system health and trade validity "
            "for {symbol} ({asset_name}). Output valid JSON only."
        ),
    }

    # ── Market interpreter prompt ──
    MARKET_INTERPRETER_TEMPLATE = (
        "You are the Real-time Market Interpreter for a {asset_name} ({symbol}) "
        "algorithmic trading system. Your job is to produce a structured JSON "
        "interpretation of the current market state using ONLY the provided data.\n\n"
        "## Current Market Data\n{session_data}\n\n"
        "## Instructions\n"
        "1. Classify the market structure (trending/ranging/transition/extreme)\n"
        "2. Identify the dominant direction and strength\n"
        "3. Detect pullback phases if present\n"
        "4. Provide execution advice (buy/sell multipliers)\n\n"
        "Output JSON with: structure, direction, strength, pullback_phase, "
        "buy_multiplier, sell_multiplier, execution_advice, risk_flags"
    )

    # ── Commander (macro direction) prompt ──
    COMMANDER_TEMPLATE = (
        "You are the Commander (Macro Strategic Agent) for a {asset_name} ({symbol}) "
        "algorithmic trading system.\n\n"
        "## Regime Context\n{regime_context}\n\n"
        "## Multi-Session Analysis\n{session_analysis}\n\n"
        "## Macro Correlation\n{correlation_section}\n\n"
        "## Position Context\n{position_context}\n\n"
        "## Direction Rules\n{direction_rules}\n\n"
        "Output JSON with: direction (LONG/SHORT/NEUTRAL), confidence (0.0-1.0), "
        "probability (0.0-1.0), phase (FRESH/DEGRADED), reason, conviction, "
        "timeframe, risk_appetite, macro_regime, key_levels"
    )

    # ── Verifier prompt ──
    VERIFIER_TEMPLATE = (
        "你是一个专业的{symbol}交易信号验证专家。\n\n"
        "## 待验证信号\n{signal_data}\n\n"
        "## 市场特征\n{market_features}\n\n"
        "请逐项检查：RSI超买超卖、ADX趋势强度、连续亏损风险、spread异常。\n"
        "输出JSON: verified(bool), confidence_adjustment(float), risk_flags(list), recommendation(str)"
    )

    # ── Morning briefing prompt ──
    MORNING_BRIEFING_TEMPLATE = (
        "Generate a daily trading briefing for {asset_name} ({symbol}).\n\n"
        "## Yesterday's Performance\n{yesterday_stats}\n\n"
        "## Current Market State\n{market_state}\n\n"
        "## Today's Outlook\nProvide: expected_range, key_levels, session_notes, risk_alerts"
    )

    def __init__(self, persona_dir: Optional[Path] = None):
        self._ctx = get_trading_context()
        self._personas: Dict[str, dict] = {}
        if persona_dir and persona_dir.exists():
            self._load_personas(persona_dir)

    def _load_personas(self, persona_dir: Path) -> None:
        """Load persona JSON files for role-specific overrides."""
        for fp in persona_dir.glob("*.json"):
            try:
                data = json.loads(fp.read_text(encoding="utf-8"))
                role_key = data.get("role_key", fp.stem)
                self._personas[role_key] = data
            except Exception:
                pass

    def render(self, template_name: str, **extra_vars) -> str:
        """Render a named template with TradingContext variables + extras."""
        ctx: TradingContext = self._ctx
        vars_dict: Dict[str, Any] = {
            "symbol": ctx.symbol,
            "asset_name": ctx.asset_name,
            "display_name": ctx.display_name,
        }
        vars_dict.update(extra_vars)

        # Check persona override first
        persona = self._personas.get(template_name, {})
        if persona and persona.get("system_prompt"):
            raw = str(persona["system_prompt"])
        else:
            raw = self.ROLE_PROMPTS.get(template_name, "")

        if not raw:
            # Try specialized templates
            specialized = {
                "market_interpreter": self.MARKET_INTERPRETER_TEMPLATE,
                "commander": self.COMMANDER_TEMPLATE,
                "verifier": self.VERIFIER_TEMPLATE,
                "morning_briefing": self.MORNING_BRIEFING_TEMPLATE,
            }
            raw = specialized.get(template_name, "")

        if not raw:
            return ""

        try:
            return raw.format(**vars_dict)
        except KeyError:
            return raw  # Return raw template if vars missing

    def get_output_schema(self, role_key: str) -> Optional[dict]:
        """Get the JSON output schema for a role (from persona file)."""
        persona = self._personas.get(role_key, {})
        return persona.get("output_schema")

    def get_role_config(self, role_key: str) -> dict:
        """Get full role config: model, temperature, max_tokens, timeout."""
        persona = self._personas.get(role_key, {})
        return {
            "model": persona.get("model", ""),
            "temperature": persona.get("temperature", 0.5),
            "max_tokens": persona.get("max_tokens", 600),
            "timeout_sec": persona.get("timeout_sec", 30),
        }


# ── Singleton ──

_engine: Optional[PromptTemplateEngine] = None


def get_template_engine(persona_dir: Optional[Path] = None) -> PromptTemplateEngine:
    """Get or create the singleton PromptTemplateEngine."""
    global _engine
    if _engine is None:
        # Auto-detect persona directory
        if persona_dir is None:
            try:
                from config_manager import get_app_dir as _get_app_dir
                default_dir = _get_app_dir() / "config" / "personas"
                if default_dir.exists():
                    persona_dir = default_dir
            except Exception:
                pass
        _engine = PromptTemplateEngine(persona_dir=persona_dir)
    return _engine
