"""Agent 声明式抽象（ARCH §5.1）+ 续审 Agent 实例（Prompt 外置方案A · 步3/5）。

`AgentSpec` 是内置 Agent 与用户 Skill 的同构声明：一份只读声明，由编排器加载、
经网关按 `tier` 执行。不变量 #2：agent 只声明 `tier`，**不**写具体 model。
不变量 #3：四审 `writes=()`（只读），任何 AI 产出入库必经验收事务。

prompt 散文已外置至 `prompts/<spec.name>.md`：`system_prompt=load_prompt(name)`
import 期读盘+缓存+规整（去BOM/LF/NFC/rstrip尾LF/fail-fast）。output schema 由
`SCHEMA_CATALOG[name]` 单向派生（唯一真相源）。`AgentSpec` 从 `spec_model` re-export
（import 路径不变，现有消费方不受影响）。

不可变：`AgentSpec` 为 frozen Pydantic 模型——加载后不得改动。
"""

from __future__ import annotations

from collections.abc import Mapping
from types import MappingProxyType
from typing import Final

from .prompt_loader import load_prompt
from .schema_catalog import SCHEMA_CATALOG
from .spec_model import AgentSpec

__all__ = [
    "REVIEW_RESERVED_NAMES",
    "SPECS",
    "AgentSpec",
    "blurb_spec",
    "book_title_spec",
    "brainstorm_spec",
    "character_gen_spec",
    "characterization_spec",
    "continue_spec",
    "continuity_spec",
    "de_ai_spec",
    "expand_spec",
    "fine_outline_spec",
    "foreshadow_spec",
    "glossary_spec",
    "golden_finger_spec",
    "name_spec",
    "opening_spec",
    "outliner_spec",
    "pace_spec",
    "project_plan_spec",
    "refiner_spec",
    "style_drift_spec",
    "style_extract_spec",
    "teardown_spec",
    "worldbuilder_spec",
]


continuity_spec = AgentSpec(
    name="continuity",
    tier="analyst",
    system_prompt=load_prompt("continuity"),
    input_schema=None,  # 注入材料为序列化文本（经记忆 assemble），非结构化入参
    output_schema=SCHEMA_CATALOG["continuity"],
    reads=("chapter_digests", "characters", "world_entities"),
    writes=(),  # 只读（不变量 #3）
    scope="builtin",
)

outliner_spec = AgentSpec(
    name="outliner",
    tier="analyst",  # 不变量 #2：只声明档位，不写 model
    system_prompt=load_prompt("outliner"),
    input_schema=None,  # 注入材料为序列化文本（设定/伏笔/人物/世界观），非结构化入参
    output_schema=SCHEMA_CATALOG["outliner"],
    reads=("projects", "foreshadow", "characters", "world_entities"),
    writes=("outline",),  # 声明式（经验收/T3.5 才真写库，不变量 #3）
    scope="builtin",
)

foreshadow_spec = AgentSpec(
    name="foreshadow",
    tier="analyst",  # 不变量 #2：只声明档位，不写 model
    system_prompt=load_prompt("foreshadow"),
    input_schema=None,  # 注入材料为序列化文本（已登记伏笔 + 草稿），非结构化入参
    output_schema=SCHEMA_CATALOG["foreshadow"],
    reads=("foreshadow",),
    writes=(),  # 只读（不变量 #3）
    scope="builtin",
)

pace_spec = AgentSpec(
    name="pace",
    tier="light",  # 不变量 #2：只声明档位，不写 model（节奏审用轻量档）
    system_prompt=load_prompt("pace"),
    input_schema=None,  # 注入材料为序列化文本（题材模板 + 草稿），非结构化入参
    output_schema=SCHEMA_CATALOG["pace"],
    reads=("rules",),  # genre 模板 DSL 经 rules（genre 级）注入，复用 review_context 规则合并
    writes=(),  # 只读（不变量 #3）
    scope="builtin",
)

# ---- 文风提取轨（style-auditor「提取」轨；analyst 档；独立生成，仿 outliner，不进 review 图）----
style_extract_spec = AgentSpec(
    name="style_extract",
    tier="analyst",  # 不变量 #2：只声明档位，不写 model
    system_prompt=load_prompt("style_extract"),
    input_schema=None,  # 注入材料为序列化样本文本，非结构化入参
    output_schema=SCHEMA_CATALOG["style_extract"],
    reads=("style_fingerprint",),
    writes=("style_fingerprint",),  # 声明式（真写库经 T4.3 端点，不变量 #3）
    scope="builtin",
)

# ---- 文风漂移轨（style-auditor「打分」轨 = 第四审；light 档；并入 REVIEW_SPECS）----
style_drift_spec = AgentSpec(
    name="style",  # 第四审 section/列名 = "style"
    tier="light",  # 不变量 #2：只声明档位，不写 model（漂移打分用轻量档）
    system_prompt=load_prompt("style"),
    input_schema=None,  # 注入材料为序列化文本（指纹 + 草稿），非结构化入参
    output_schema=SCHEMA_CATALOG["style"],
    reads=("style_fingerprint",),  # 指纹经 assemble 的 stable_core 注入 review_context
    writes=(),  # 只读（不变量 #3）
    scope="builtin",
)

# ---- 人物塑造续审（灵感③；analyst 档；advisory 单维——仅建议、不阻断验收，D2）----
characterization_spec = AgentSpec(
    name="characterization",
    tier="analyst",  # 不变量 #2：只声明档位，不写 model（人物塑造诊断用分析档）
    system_prompt=load_prompt("characterization"),
    input_schema=None,  # 注入材料为序列化文本（人物卡 + 草稿，经 assemble），非结构化入参
    output_schema=SCHEMA_CATALOG["characterization"],
    reads=(
        "characters",
    ),  # 以人物卡 motive/appearance 为客观锚点（经 assemble 的 stable_core 注入）
    writes=(),  # 只读（不变量 #3）；advisory 唯一落库=collect 留痕，不进 conflicts/验收 gate
    scope="builtin",
)

# ---- 回炉（refiner；writer 档；纯文本重写；非 Agent 流水线、非持久，M4-e）----
refiner_spec = AgentSpec(
    name="refiner",
    tier="writer",  # 不变量 #2：重写正文用 writer 档
    system_prompt=load_prompt("refiner"),
    input_schema=None,  # 注入材料为序列化文本（选中段 + 指令 + 上下文）
    output_schema=SCHEMA_CATALOG["refiner"],  # 纯文本产出（重写段），无结构化 schema
    reads=(),
    writes=(),  # 非持久（不变量 #3）：端点同步返回 {original, refined}，不写库
    scope="builtin",
)

# ---- worldbuilder（世界观设计师；写手档；独立生成，仿 outliner，不进 review 图）----
worldbuilder_spec = AgentSpec(
    name="worldbuilder",
    tier="writer",  # 不变量 #2：只声明档位，不写 model（世界观创意用写手档）
    system_prompt=load_prompt("worldbuilder"),
    input_schema=None,  # 注入材料为序列化文本（设定 + 需求），非结构化入参
    output_schema=SCHEMA_CATALOG["worldbuilder"],
    reads=("projects",),
    writes=("world_entities",),  # 声明式（真写库经 T5.2 入库端点，不变量 #3）
    scope="builtin",
)

# ---- character-gen（角色设计师；写手档；单/批量；群像防雷同；独立生成）----
character_gen_spec = AgentSpec(
    name="character-gen",
    tier="writer",  # 不变量 #2：只声明档位，不写 model（角色创意用写手档）
    system_prompt=load_prompt("character-gen"),
    input_schema=None,  # 注入材料为序列化文本（需求 + 约束 + 已有 + 已生成），非结构化入参
    output_schema=SCHEMA_CATALOG["character-gen"],
    reads=("world_entities", "characters"),
    writes=("characters",),  # 声明式（真写库经 T5.2 入库端点 + continuity 校验，不变量 #3）
    scope="builtin",
)

# ---- brainstorm（脑洞生成器；轻量档；创作工具箱通用框架首个最简生成器，T6）----
brainstorm_spec = AgentSpec(
    name="brainstorm",
    tier="light",  # 不变量 #2：只声明档位，不写 model（脑洞发散用轻量档）
    system_prompt=load_prompt("brainstorm"),
    input_schema=None,  # 注入材料为序列化文本（作品设定 + 一句话需求），非结构化入参
    output_schema=SCHEMA_CATALOG["brainstorm"],
    reads=("projects",),
    writes=(),  # 纯预览，不写库（不变量 #3）
    scope="builtin",
)

# ---- book-title（书名生成器；轻量档；纯预览，T6 创作工具箱）----
book_title_spec = AgentSpec(
    name="book-title",
    tier="light",  # 不变量 #2：只声明档位，不写 model（书名发散用轻量档）
    system_prompt=load_prompt("book-title"),
    input_schema=None,  # 注入材料为序列化文本（作品设定 + 一句话需求），非结构化入参
    output_schema=SCHEMA_CATALOG["book-title"],
    reads=("projects",),
    writes=(),  # 纯预览，不写库（不变量 #3）
    scope="builtin",
)

# ---- blurb（简介生成器；分析档；纯预览，T6 创作工具箱）----
blurb_spec = AgentSpec(
    name="blurb",
    tier="analyst",  # 不变量 #2：只声明档位，不写 model（文案打磨用分析档）
    system_prompt=load_prompt("blurb"),
    input_schema=None,  # 注入材料为序列化文本（作品设定 + 一句话需求），非结构化入参
    output_schema=SCHEMA_CATALOG["blurb"],
    reads=("projects",),
    writes=(),  # 纯预览，不写库（不变量 #3）
    scope="builtin",
)

# ---- name（取名生成器；轻量档；纯预览，T6 创作工具箱）----
name_spec = AgentSpec(
    name="name",
    tier="light",  # 不变量 #2：只声明档位，不写 model（取名发散用轻量档）
    system_prompt=load_prompt("name"),
    input_schema=None,  # 注入材料为序列化文本（设定 + 世界观 + 已有角色 + 需求）
    output_schema=SCHEMA_CATALOG["name"],
    reads=("projects", "world_entities", "characters"),
    writes=(),  # 纯预览，不写库（不变量 #3）
    scope="builtin",
)

# ---- golden-finger（金手指生成器；写手档；声明式 writes=world_entities，T6 创作工具箱）----
golden_finger_spec = AgentSpec(
    name="golden-finger",
    tier="writer",  # 不变量 #2：只声明档位，不写 model（力量体系创意用写手档）
    system_prompt=load_prompt("golden-finger"),
    input_schema=None,  # 注入材料为序列化文本（设定 + 世界观 + 需求），非结构化入参
    output_schema=SCHEMA_CATALOG["golden-finger"],
    reads=("projects", "world_entities"),
    writes=("world_entities",),  # 声明式（真写库经入库端点，不变量 #3）
    scope="builtin",
)

# ---- glossary（术语表生成器；分析档；声明式 writes=world_entities，T6 创作工具箱）----
glossary_spec = AgentSpec(
    name="glossary",
    tier="analyst",  # 不变量 #2：只声明档位，不写 model（术语梳理用分析档）
    system_prompt=load_prompt("glossary"),
    input_schema=None,  # 注入材料为序列化文本（设定 + 世界观 + 需求），非结构化入参
    output_schema=SCHEMA_CATALOG["glossary"],
    reads=("projects", "world_entities"),
    writes=("world_entities",),  # 声明式（真写库经入库端点，不变量 #3）
    scope="builtin",
)

# ---- opening（开篇生成器；写手档；纯预览，T6 创作工具箱）----
opening_spec = AgentSpec(
    name="opening",
    tier="writer",  # 不变量 #2：只声明档位，不写 model（正文创作用写手档）
    system_prompt=load_prompt("opening"),
    input_schema=None,  # 注入材料为序列化文本（设定 + 大纲 + 需求），非结构化入参
    output_schema=SCHEMA_CATALOG["opening"],
    reads=("projects", "outline"),
    writes=(),  # 纯预览，不写库（不变量 #3）
    scope="builtin",
)

# ---- fine-outline（细纲生成器；分析档；声明式 writes=outline，T6 创作工具箱）----
fine_outline_spec = AgentSpec(
    name="fine-outline",
    tier="analyst",  # 不变量 #2：只声明档位，不写 model（细纲拆解用分析档）
    system_prompt=load_prompt("fine-outline"),
    input_schema=None,  # 注入材料为序列化文本（设定 + 本章大纲 + 需求），非结构化入参
    output_schema=SCHEMA_CATALOG["fine-outline"],
    reads=("projects", "outline"),
    writes=("outline",),  # 声明式（真写库经入库端点，不变量 #3）
    scope="builtin",
)

# ---- continue（续写生成器；写手档；纯预览，Scope B 竞品快赢）----
continue_spec = AgentSpec(
    name="continue",
    tier="writer",  # 不变量 #2：只声明档位，不写 model（正文续写用写手档）
    system_prompt=load_prompt("continue"),
    input_schema=None,  # 注入材料为序列化文本（设定 + 前文 + 节拍 + 需求），非结构化入参
    output_schema=SCHEMA_CATALOG["continue"],
    reads=("projects", "outline", "chapters"),
    writes=(),  # 纯预览，不写库（不变量 #3）
    scope="builtin",
)

# ---- expand（扩写生成器；写手档；纯预览，Scope B 竞品快赢）----
expand_spec = AgentSpec(
    name="expand",
    tier="writer",  # 不变量 #2：只声明档位，不写 model（正文扩写用写手档）
    system_prompt=load_prompt("expand"),
    input_schema=None,  # 注入材料为序列化文本（设定 + 原文 + 需求），非结构化入参
    output_schema=SCHEMA_CATALOG["expand"],
    reads=("projects",),
    writes=(),  # 纯预览，不写库（不变量 #3）
    scope="builtin",
)

# ---- de-ai（降 AI 率生成器；分析档；纯预览，Scope B 竞品快赢）----
de_ai_spec = AgentSpec(
    name="de-ai",
    tier="analyst",  # 不变量 #2：只声明档位，不写 model（文风改写用分析档）
    system_prompt=load_prompt("de-ai"),
    input_schema=None,  # 注入材料为序列化文本（设定 + 原文 + 需求），非结构化入参
    output_schema=SCHEMA_CATALOG["de-ai"],
    reads=("projects",),
    writes=(),  # 纯预览，不写库（不变量 #3）
    scope="builtin",
)

# ---- teardown（拆书生成器；分析档；纯预览，Scope B 竞品快赢）----
teardown_spec = AgentSpec(
    name="teardown",
    tier="analyst",  # 不变量 #2：只声明档位，不写 model（拆解分析用分析档）
    system_prompt=load_prompt("teardown"),
    input_schema=None,  # 注入材料为序列化文本（设定 + 样本 + 需求），非结构化入参
    output_schema=SCHEMA_CATALOG["teardown"],
    reads=("projects",),
    writes=("rules",),  # F1：拆书结论可落库为项目 rules（入库仍经 ingest 白名单 gate，#3）
    scope="builtin",
)

# ---- project-plan（立项方案师；分析档；纯预览 writes=()，灵感⑤）----
project_plan_spec = AgentSpec(
    name="project-plan",
    tier="analyst",  # 不变量 #2：只声明档位，不写 model（立项方案构思用分析档）
    system_prompt=load_prompt("project-plan"),
    input_schema=None,  # 向导阶段 project 未建；材料为序列化的向导草稿（端点注入），非结构化入参
    output_schema=SCHEMA_CATALOG["project-plan"],
    reads=(),  # 立项前 project 未建、无库可读；种子经端点序列化注入
    writes=(),  # 纯预览，不写库（不变量 #3）
    scope="builtin",
)


# 集中注册表：name → spec（同一实例，兼容期 *_spec 与 SPECS[name] 为同对象，不变量）
# MappingProxyType：只读视图，运行时 `SPECS[x] = ...` / `del SPECS[x]` 抛 TypeError
# （`Final` 仅静态检查，挡不住运行时变异；注册表是单一真相源，须运行时不可变）。
_SPECS_BY_NAME: dict[str, AgentSpec] = {
    s.name: s
    for s in (
        continuity_spec,
        outliner_spec,
        foreshadow_spec,
        pace_spec,
        characterization_spec,
        style_extract_spec,
        style_drift_spec,
        refiner_spec,
        worldbuilder_spec,
        character_gen_spec,
        brainstorm_spec,
        book_title_spec,
        blurb_spec,
        name_spec,
        golden_finger_spec,
        glossary_spec,
        opening_spec,
        fine_outline_spec,
        continue_spec,
        expand_spec,
        de_ai_spec,
        teardown_spec,
        project_plan_spec,
    )
}
assert len(_SPECS_BY_NAME) == 23, "SPECS name 冲突或缺失"  # 唯一性 + 数量自检（import 期）
SPECS: Final[Mapping[str, AgentSpec]] = MappingProxyType(_SPECS_BY_NAME)

# 四审受信保留名 —— 独立显式白名单（安全边界锚在此，不依附派生集合）
REVIEW_RESERVED_NAMES: Final[frozenset[str]] = frozenset(
    {"continuity", "foreshadow", "style", "pace"}
)
