"""T6 创作工具箱 · 7 个新生成器经 `run_generator` 的驱动测试 + 章上下文构造。

注入 mock 网关（按 schema 路由 / 单一 parsed），无真 LLM、无真 Postgres。验证通用执行器
对每个 spec：透传 tier、非流式、output_schema 据 spec 绑定、system[0] 缓存、parsed 校验，
网关失败上抛。asyncio_mode=auto。
"""

from __future__ import annotations

import uuid

import pytest
from fakes_orchestrator import FailingRunGateway, FakeRunGateway, SchemaRoutingRunGateway
from pydantic import BaseModel
from ww_agents import (
    AgentSpec,
    Blurb,
    BlurbResult,
    DetailedOutlineResult,
    GlossaryResult,
    GlossaryTerm,
    GoldenFinger,
    GoldenFingerResult,
    NameListResult,
    NameSuggestion,
    Opening,
    OpeningResult,
    Scene,
    Title,
    TitleListResult,
    blurb_spec,
    book_title_spec,
    fine_outline_spec,
    glossary_spec,
    golden_finger_spec,
    name_spec,
    opening_spec,
)
from ww_core.orchestrator import build_outline_chapter_context, run_generator

PROJECT = uuid.UUID("00000000-0000-0000-0000-000000000001")
USER = uuid.UUID("00000000-0000-0000-0000-0000000000aa")

# (spec, parsed 实例, schema 类型)
_CASES: list[tuple[AgentSpec, BaseModel, type[BaseModel]]] = [
    (
        book_title_spec,
        TitleListResult(titles=[Title(title="吞噬", rationale="爽")]),
        TitleListResult,
    ),
    (blurb_spec, BlurbResult(variants=[Blurb(text="文案", angle="反差")]), BlurbResult),
    (name_spec, NameListResult(names=[NameSuggestion(name="玄阴宗", kind="势力")]), NameListResult),
    (
        golden_finger_spec,
        GoldenFingerResult(
            systems=[GoldenFinger(name="瞳", mechanism="m", growth="g", limits="l")]
        ),
        GoldenFingerResult,
    ),
    (
        glossary_spec,
        GlossaryResult(terms=[GlossaryTerm(name="炼气", type="境界", definition="d")]),
        GlossaryResult,
    ),
    (opening_spec, OpeningResult(variants=[Opening(text="雷光劈下")]), OpeningResult),
    (
        fine_outline_spec,
        DetailedOutlineResult(scenes=[Scene(idx=0, beat="b", purpose="p", conflict="c", hook="h")]),
        DetailedOutlineResult,
    ),
]


@pytest.mark.parametrize(("spec", "parsed", "schema"), _CASES)
async def test_run_generator_returns_each_toolbox_schema(
    spec: AgentSpec, parsed: BaseModel, schema: type[BaseModel]
) -> None:
    gateway = FakeRunGateway(parsed)

    result = await run_generator(
        spec, context="x", gateway=gateway, user_id=USER, project_id=PROJECT
    )

    assert isinstance(result, schema)


@pytest.mark.parametrize(("spec", "parsed", "schema"), _CASES)
async def test_run_generator_builds_request_per_spec(
    spec: AgentSpec, parsed: BaseModel, schema: type[BaseModel]
) -> None:
    # 不变量 #2/#9：透传 spec.tier、非流式、output_schema 据 spec 绑定、system[0] 缓存
    gateway = FakeRunGateway(parsed)

    await run_generator(spec, context="x", gateway=gateway, user_id=USER, project_id=PROJECT)

    req = gateway.last_request
    assert req is not None
    assert req.tier == spec.tier
    assert req.stream is False
    assert req.output_schema is schema
    assert req.system[0].cache is True


async def test_run_generator_routes_by_schema_across_toolbox() -> None:
    # 一个按 schema 路由的网关同时服务 7 个生成器，各拿自己 schema 的 parsed
    by_schema = {schema: parsed for _, parsed, schema in _CASES}
    gateway = SchemaRoutingRunGateway(by_schema)

    for spec, _parsed, schema in _CASES:
        result = await run_generator(
            spec, context="x", gateway=gateway, user_id=USER, project_id=PROJECT
        )
        assert isinstance(result, schema)


@pytest.mark.parametrize("spec", [row[0] for row in _CASES])
async def test_run_generator_raises_on_gateway_failure(spec: AgentSpec) -> None:
    gateway = FailingRunGateway(RuntimeError("boom"))

    with pytest.raises(RuntimeError, match="boom"):
        await run_generator(spec, context="x", gateway=gateway, user_id=USER, project_id=PROJECT)


# ---- build_outline_chapter_context：确定性、含设定/章号/节拍、空节拍降级 ----


def test_build_outline_chapter_context_is_deterministic_and_shaped() -> None:
    a = build_outline_chapter_context(
        chapter_no=3, beats=["主角觉醒", "强敌登场"], project_context="题材：玄幻"
    )
    b = build_outline_chapter_context(
        chapter_no=3, beats=["主角觉醒", "强敌登场"], project_context="题材：玄幻"
    )
    assert a == b
    assert "题材：玄幻" in a
    assert "第 3 章" in a
    assert "主角觉醒" in a
    assert "强敌登场" in a


def test_build_outline_chapter_context_empty_beats_falls_back() -> None:
    ctx = build_outline_chapter_context(chapter_no=1, beats=[], project_context="题材：都市")
    assert "本章暂无大纲节拍" in ctx


def test_build_outline_chapter_context_empty_project_falls_back() -> None:
    ctx = build_outline_chapter_context(chapter_no=2, beats=["x"], project_context="")
    assert "暂无设定" in ctx
