"""LangGraph 大脑的结构化输出 schema(Pydantic)+ 到运行时 Decision 的转换。

设计:Pydantic 模型镜像既有 ACTION_DOC / DIALOGUE_DOC 的 JSON 形状,交给
`ChatLiteLLM.with_structured_output` 做原生函数调用约束(MiniMax 已验证支持),
省掉脆弱的正则解析;再转成引擎契约 `Decision` 数据类。校验失败一律降级为空决策,
世界不因单次模型抽风停摆(沿用旧大脑的鲁棒性哲学)。
"""

from __future__ import annotations

from pydantic import BaseModel, Field

from genesis.runtime.cognition import Decision


class SpeechSpec(BaseModel):
    to: str = Field(default="", description="对方名字;空字符串=自言自语/对在场所有人")
    text: str = Field(default="", description="说的话(纯口语台词,不夹带动作描写)")


class ActionSpec(BaseModel):
    kind: str = Field(default="", description="动作类型:move/search/take/destroy/use/examine/eavesdrop/assemble/accuse/shout/sleep/idle,凶手另有 lure/kill/sabotage")
    arg: str = Field(default="", description="动作参数(目的地名/物品名/目标名等),按动作语义填,可空")


class GoalUpdate(BaseModel):
    index: int = Field(description="目标序号(从1开始)")
    status: str = Field(default="doing", description="todo|doing|done|blocked")
    note: str = Field(default="", description="一句话进度备注")


class ActionDecisionOut(BaseModel):
    """L2 慢想的结构化输出(镜像 ACTION_DOC)。"""

    scheme: str = Field(default="", description="先盘算(一句话,≤25字):为了终极目的,眼下最该做什么")
    thought: str = Field(default="", description="内心想法(简短,≤25字)")
    intent: str = Field(default="", description="当前意图(一句话,≤25字)")
    speech: SpeechSpec | None = Field(default=None, description="要说的话,不说则 null")
    gesture: str = Field(default="", description="神态或小动作(不是台词),可空")
    action: ActionSpec | None = Field(default=None, description="要做的动作,无动作则 null")
    minutes: float = Field(default=10.0, description="动作预计分钟数")
    goal_update: GoalUpdate | None = Field(default=None, description="目标进度自评,无则 null")
    goals_replan: list[str] | None = Field(
        default=None, description="局势剧变时重列接下来要做的2-4件事,否则 null")


class DialogueDecisionOut(BaseModel):
    """L1 对话快想的结构化输出(镜像 DIALOGUE_DOC)。"""

    thought: str = Field(default="", description="内心想法(简短)")
    speech: str = Field(default="", description="你接的话(纯口语,一两句)")
    gesture: str = Field(default="", description="神态或小动作(可空)")
    leave: bool = Field(default=False, description="是否结束交谈抽身去办正事")


class BootstrapOut(BaseModel):
    """目标自举(G9/G10):把 secret 落成 2-4 条今晚必须办成的私人目标。"""

    goals: list[str] = Field(default_factory=list,
                             description="2-4条具体可执行目标(写明去哪/做什么),按优先级排序")


class RelationDelta(BaseModel):
    name: str = Field(description="某人(必须是在岛人员)")
    impression: str = Field(default="", description="一句话新印象")
    trust: float = Field(default=0.0, description="信任增减,-1.0到1.0之间")


class ReflectOut(BaseModel):
    """反思(G7):沉淀判断 + 修正对人的信任。"""

    insights: list[str] = Field(default_factory=list, description="对人或局势的判断,2-3条,每条≤25字")
    relations: list[RelationDelta] = Field(default_factory=list, description="对人的印象/信任增减")


def action_to_decision(out: ActionDecisionOut, *, fallback_intent: str, raw: str = "") -> Decision:
    """结构化 L2 输出 → 引擎契约 Decision(与旧 _to_decision 的裁剪规则保持一致)。"""
    sp = out.speech
    act = out.action
    try:
        minutes = float(out.minutes)
    except (TypeError, ValueError):
        minutes = 10.0
    gu = out.goal_update.model_dump() if out.goal_update else None
    replan = [str(s) for s in out.goals_replan][:4] if out.goals_replan else None
    return Decision(
        scheme=out.scheme or "",
        thought=out.thought or "",
        intent=(out.intent or "") or fallback_intent,
        speech_to=(sp.to if sp else "") or "",
        speech=(sp.text if sp else "") or "",
        gesture=out.gesture or "",
        action_kind=(act.kind if act else "") or "",
        action_arg=(act.arg if act else "") or "",
        minutes=max(2.0, min(10.0, minutes)),   # 单段动作≤10分钟,世界保持流动(同旧大脑)
        goal_update=gu,
        replan=replan or None,
        raw=raw or out.model_dump_json(),
    )


def dialogue_to_decision(out: DialogueDecisionOut, *, dialogue_with: str,
                         fallback_intent: str, raw: str = "") -> Decision:
    """结构化 L1 输出 → Decision(对话只接话,不带动作)。"""
    return Decision(
        thought=out.thought or "",
        speech=out.speech or "",
        gesture=out.gesture or "",
        speech_to=dialogue_with,
        leave_dialogue=bool(out.leave),
        intent=fallback_intent,
        raw=raw or out.model_dump_json(),
    )
