"""润色预检澄清节点（WFW-9 M1 · 路线A 两阶段之「问题」阶段）。

`run_clarify` 是**独立非流式**预检——不在写章/审稿流水线（不接进 review 图），仿
`run_generator`。润色端点在真正回炉改写前调它：判「作者这条意见清不清楚」，含糊则反问
一个问题 + 给选项，清楚则确认理解、放行。正文改写仍走既有 refine 端点，本节点**不改稿**。

确定性边界（CLAUDE.md「LangGraph」纪律）：节点逻辑无 LLM 非确定性——不确定性藏在网关后，
节点只做 `AgentSpec + 上下文 → LlmRequest` 的纯构造 + 转发 `Gateway.run` 的 `parsed`。
注入 mock 网关（产 `ClarifyDecision`）即可单测，无需图运行时、无需真 Postgres。

不变量 #2：agent 只声明 `tier`（analyst），绝不传具体 model（spec.tier 透传）。
不变量 #3：节点**只读、不写库**——只产澄清决策返回调用方，绝不改正文/写任何表。
不变量 #9：`spec.system_prompt` 进缓存断点前块（cache=True）；注入材料进 `input`（断点后），
system 前缀只放教条、无历史/时间戳/UUID。

韧性降级：判别/校验失败（网关返回非 `ClarifyDecision`）**确定性回退**为
`need_clarification=false`（不问、直接放行 refine），绝不阻塞润色主链路。网关本体的瞬时
失败/回退/熔断/instructor 重试均归网关（§4.5），本节点只感知干净的最终 parsed。
"""

from __future__ import annotations

import uuid

import structlog
from ww_agents import AgentSpec, ClarifyDecision
from ww_llm_gateway.types import Block, LlmRequest, Scope

from ._protocols import GatewayRun

log = structlog.get_logger(__name__)


def build_clarify_request(
    spec: AgentSpec,
    *,
    context: str,
    user_id: uuid.UUID,
    project_id: uuid.UUID | None,
) -> LlmRequest:
    """据 spec + 注入材料构造预检请求（纯函数）。

    `spec.system_prompt` → `system` 缓存断点前块（cache=True，只放教条，不变量 #9）；
    `context`（序列化的选段 + 再沟通意见）→ `input`（断点后，易变内容）。
    预检要结构化产物（`ClarifyDecision`）、非流式正文——`stream=False`、带 `output_schema`。
    """
    return LlmRequest(
        tier=spec.tier,  # 不变量 #2：只传档位，不传 model
        system=[Block(text=spec.system_prompt, cache=True)],
        input=context,
        stream=False,
        output_schema=spec.output_schema,
        scope=Scope(user_id=user_id, project_id=project_id),
    )


async def run_clarify(
    spec: AgentSpec,
    *,
    context: str,
    gateway: GatewayRun,
    user_id: uuid.UUID,
    project_id: uuid.UUID | None = None,
) -> ClarifyDecision:
    """跑润色预检：构请求 → `gateway.run` → 校验 parsed 为 `ClarifyDecision`。

    **判别/校验失败确定性回退**：网关返回非 `ClarifyDecision`（instructor 重试上限后仍
    畸形 / None）时不上抛，降级为 `need_clarification=false`（不问、直接放行 refine），
    守「clarify 是非关键预检、绝不阻塞润色」的产品语义。网关本体失败（瞬时/回退耗尽）
    仍上抛给端点（映射 503，同 refine 记账/凭据纪律）。**只读、不写库**（不变量 #3）。
    """
    req = build_clarify_request(spec, context=context, user_id=user_id, project_id=project_id)
    resp = await gateway.run(req)
    parsed = resp.parsed
    if not isinstance(parsed, ClarifyDecision):
        # 判别失败：确定性回退不问、放行（不阻塞润色主链路）。
        log.warning(
            "clarify_node_fallback_no_clarification",
            spec=spec.name,
            project_id=str(project_id) if project_id else None,
            parsed_type=type(parsed).__name__,
        )
        return ClarifyDecision(need_clarification=False)
    return parsed
