from __future__ import annotations

import json
from typing import Any

from backend.llm import transport as llm_transport


class HeavenAgentError(RuntimeError):
    pass


def interpret_heaven(
    mode: str,
    context: dict[str, Any],
    api_key: str,
    base_url: str,
    model: str,
    timeout: int = 90,
) -> dict[str, Any]:
    if mode not in {"trend", "fortune", "heart"}:
        raise HeavenAgentError("不支持的问天解读模式。")
    if not api_key or not model:
        raise HeavenAgentError("LLM API Key 或模型尚未配置。")
    system_prompt = _system_prompt(mode)
    messages = [
        {"role": "system", "content": system_prompt},
        {
            "role": "user",
            "content": json.dumps(context, ensure_ascii=False, separators=(",", ":")),
        },
    ]
    try:
        result = llm_transport.chat_completion(
            api_key=api_key,
            base_url=base_url,
            model=model,
            messages=messages,
            timeout=timeout,
            user_agent="XiaobaiReviewWeb/0.7",
        )
        answer = str(result.content).strip()
        if not answer:
            raise KeyError("empty response")
    except llm_transport.OpenAIHTTPError as exc:
        raise HeavenAgentError(exc.describe("问天模型调用失败")) from exc
    except (llm_transport.OpenAITransportError, KeyError) as exc:
        raise HeavenAgentError(f"问天模型调用失败：{exc}") from exc
    return {
        "answer": answer,
        "model": model,
        "latency_ms": result.latency_ms,
    }


def _system_prompt(mode: str) -> str:
    common = """
你是“小白复盘”的问天解读器。所有历法、卦象、爻位和市场指标已经由确定性程序计算，你只能解释提供的数据，不得改卦、改爻、改干支或编造行情。
问天属于传统文化与娱乐化观察，不是预测模型，不承诺应验，不输出无条件买卖指令，不用神秘话术制造确定性。
使用中文，先给核心判断，再解释结构。引用市场数字时标明数据日期。输出纯文本，可使用简短标题。
""".strip()
    if mode == "trend":
        return common + """

当前任务是“观势·解势”。六爻从初爻到上爻依次是个股内核、个股外显、板块内核、板块外显、指数内核、指数外显；初二为地、三四为人、五上为天。
行情数据只负责生成六爻，本次解势必须以卦象本身为主，不得根据指数涨跌、板块强弱、涨停家数、成交量或个股表现直接推演方向。context中不会提供这些数字，也不会提供爻位对应的市场角色。
先解释本卦卦名的核心义、上下卦组合及大象；再只解释实际动爻所代表的转折，并说明本卦如何走向之卦；最后可把这一组卦势翻译成克制的市场语言。
重点是“本卦为当下之势，动爻为变化关节，之卦为所趋之势”。不要说明某一动爻对应指数、板块或个股，也不要输出“一看指数、二看涨停家数”一类行情观察条件。
全文控制在300至450个中文字符，最多四小段。卦理约占九成，市场翻译最多一句，只能落到节制、等待、守信、辨伪等行为态度，不得据此预测市场下一阶段、涨跌方向或动能变化。不直接荐股，不使用Markdown表格。
不要使用“必然、确定、必涨、必跌、后续将、进入某阶段”等断语；天机只点出势的性质与变化关系，不替用户宣布结果。
""".strip()
    if mode == "fortune":
        return common + """

当前任务是“观气·解运”。严格区分五运、六气、节气、月令和日干，不把丙午简单解释为火年。
严格服从five_phase_field.framework提供的确定性结构，不自行重新计算五行：年纲由中运与司天在泉构成；岁半以前司天为主、在泉为辅，岁半以后在泉为主、司天为辅；当前六气层以客气加临主气为核心；日辰只负责触发。节气只用于定位当前六气阶段，不得再次叠加为独立力量。
重点解释framework.relations中的客主同气、客生主、主生客、客克主或主克客，以及客胜为从、主胜为逆、司天在泉同位、天符岁会等已经判定的关系。不得把司天、在泉、主气、客气视为彼此独立的证据重复计权，也不得自行增删传统格局。
首要解释当日气场容易放大参与者的哪些情绪、判断偏差和操作冲动，例如急躁、恐惧、迟疑、追涨、过早止损或路径依赖；再给出一至两个调节动作。
如有personal_profile，结合其日主、十神、五行平衡倾向说明当日对该用户主观状态的影响，但不得把简化平衡倾向说成唯一喜用神，也不得复述或猜测出生日期。
不得引用市场上涨下跌家数、涨跌停数量、成交额、板块强度或个股表现来证明气场。industry_affinity只是五行行业取象示例，不是行情旁证；行业契合度最多在末尾用一句话说明，不得写“当日共振”或暗示相关行业必然涨跌。
全文控制在420至600个中文字符，按“三层气机、人的状态、操作偏向、个人影响（如有）、制衡动作”组织，标题必须写“三层气机”。明确这些是传统历法框架下的观察语言，不宣称气候或五行直接导致股价。
""".strip()
    return common + """

当前任务是“观心·解卦”。用户的问题始终只在心中，没有输入给你，因此你不能猜测问题内容，也不能替用户作具体决定。
全文控制在180至350个中文字符。只写一句卦意；一小段动爻与之卦；最后三句极短的问心句。
不要重述六条爻辞，不猜用户未说出口的问题，不以吉凶二字替代思考，不给出股票涨跌预测。语气安静、克制，越短越有余味。
""".strip()
