"""写章图工厂 + Postgres checkpointer（C4 / ARCH §5.2）。

M1：单 `write` 节点图（四审/collect/interrupt 验收属 M2）。

checkpointer 纪律（CLAUDE.md「LangGraph」）：
- 用 **Postgres checkpointer**（durable/resumable，跨 write→accept 的请求间隙）。
- `setup()` **只在 migrations/CI 跑**，绝不在 import/app-runtime 跑——故本模块只
  暴露 `setup_checkpointer` 入口，build 函数不触碰 DDL。
- 单测用 `MemorySaver`（或直接测节点函数），永不连真 Postgres。
"""

from __future__ import annotations

from collections.abc import Sequence
from typing import TYPE_CHECKING, Any

from langgraph.graph import END, START, StateGraph
from ww_agents import (
    AgentSpec,
    characterization_spec,
    continuity_spec,
    foreshadow_spec,
    pace_spec,
    style_drift_spec,
)

from ._protocols import GatewayRun
from .collect import ReviewRecorder, collect_reviews
from .review_node import run_review
from .state import ChapterState

if TYPE_CHECKING:
    from langgraph.checkpoint.base import BaseCheckpointSaver
    from langgraph.graph.state import CompiledStateGraph

# write 节点名（write_node 本体保留，被多章链 `chain.nodes.write_chapter` 复用；M1 的
# `build_write_graph` 单节点图已删——链图取代之，YAGNI，见 chain-workflow 设计 §3.3）。
WRITE_NODE = "write"
COLLECT_NODE = "collect"


# M4 四审齐（continuity + foreshadow + pace + style）+ 灵感③ advisory 人物塑造第五审。
# characterization 是 advisory（仅建议、不阻断验收）：并行扇出自动汇入 collect，
# 不进 conflicts、不碰验收 gate、不入 REVIEW_RESERVED_NAMES（灵感 D2）。
REVIEW_SPECS: tuple[AgentSpec, ...] = (
    continuity_spec,
    foreshadow_spec,
    pace_spec,
    style_drift_spec,
    characterization_spec,
)


def build_review_graph(
    gateway: GatewayRun,
    review_repo: ReviewRecorder,
    *,
    review_specs: Sequence[AgentSpec] = REVIEW_SPECS,
    checkpointer: BaseCheckpointSaver[Any] | None = None,
) -> CompiledStateGraph[ChapterState, None, ChapterState, ChapterState]:
    """构建并编译审稿子图：`START →`（各 review spec 并行节点）`→ collect → END`。

    M3 默认三审齐（continuity + foreshadow + pace）；`review_specs` 仍可扩（M4 加 style
    第四审），图按这组 spec 循环加并行分支，全部汇入 collect（任一审失败不阻塞，§5.2——
    失败隔离在 `run_review` 内、标 incomplete 占位）。`run_review`/`make_review_node` 对 spec
    泛型，三审天然支持（按 `spec.output_schema` 产 parsed，无 spec-specific 假设）。

    `gateway` / `review_repo` 经闭包绑进节点（langgraph 不收 functools.partial，见 gotcha）。
    **accept 不在本图**：本任务只交付到 collect（审稿留痕落 `chapter_reviews`）+ review SSE；
    `interrupt_before=["accept"]` / accept 节点属 T2.4（验收事务，确定性代码读领域表，R3）。
    """

    async def _collect(state: ChapterState) -> dict[str, Any]:
        return await collect_reviews(state, review_repo=review_repo)

    builder: StateGraph[ChapterState, None, ChapterState, ChapterState] = StateGraph(ChapterState)
    builder.add_node(COLLECT_NODE, _collect)
    for spec in review_specs:
        # spec 经默认参绑定（避免闭包晚绑定 + langgraph 不收 functools.partial，见 gotcha）。
        async def _review(state: ChapterState, *, _spec: AgentSpec = spec) -> dict[str, Any]:
            return await run_review(_spec, state, gateway=gateway)

        builder.add_node(spec.name, _review)
        builder.add_edge(START, spec.name)
        builder.add_edge(spec.name, COLLECT_NODE)
    builder.add_edge(COLLECT_NODE, END)
    return builder.compile(checkpointer=checkpointer)


async def setup_checkpointer(conn_string: str) -> None:
    """在 **migrations/CI** 创建 checkpointer 所需表（绝不在 app-runtime 调用）。

    用 async saver 的上下文管理器拿连接后跑一次性 `setup()` DDL。
    """
    from langgraph.checkpoint.postgres.aio import AsyncPostgresSaver

    async with AsyncPostgresSaver.from_conn_string(conn_string) as saver:
        await saver.setup()
