"""T3.4 大纲 Agent 契约测试：OutlineResult schema + outliner_spec 声明（C6 扩）。

契约测试——构造符合 schema 的 mock 响应，校验伏笔窗口字段、章节结构、
声明式 writes（验收/T3.5 才真写库，不变量 #3）。不联网、无 DB。
"""

from __future__ import annotations

import pytest
from pydantic import ValidationError
from ww_agents import (
    AgentSpec,
    ForeshadowWindow,
    OutlineChapter,
    OutlineResult,
    outliner_spec,
)


def test_outline_result_parses_mock_response() -> None:
    # Arrange：模拟网关 instructor 校验后的结构化大纲产出
    mock = {
        "chapters": [
            {
                "no": 1,
                "beats": ["主角登场，立下目标", "埋下身世伏笔"],
                "foreshadow_windows": [
                    {
                        "code": "FS-BIRTH",
                        "plant_chapter": 1,
                        "expected_close_from": 20,
                        "expected_close_to": 30,
                    }
                ],
            },
            {
                "no": 2,
                "beats": ["遭遇对手"],
                "foreshadow_windows": [],
            },
        ]
    }

    # Act
    outline = OutlineResult.model_validate(mock)

    # Assert
    assert len(outline.chapters) == 2
    assert outline.chapters[0].no == 1
    assert outline.chapters[0].beats == ["主角登场，立下目标", "埋下身世伏笔"]
    window = outline.chapters[0].foreshadow_windows[0]
    assert window.code == "FS-BIRTH"
    assert window.plant_chapter == 1
    assert window.expected_close_from == 20
    assert window.expected_close_to == 30


def test_outline_chapter_defaults_empty_windows() -> None:
    chapter = OutlineChapter.model_validate({"no": 3, "beats": ["收束支线"]})
    assert chapter.foreshadow_windows == []


def test_outline_result_defaults_to_empty_chapters() -> None:
    assert OutlineResult().chapters == []


def test_foreshadow_window_optional_bounds_default_none() -> None:
    window = ForeshadowWindow.model_validate({"code": "FS-X"})
    assert window.plant_chapter is None
    assert window.expected_close_from is None
    assert window.expected_close_to is None


def test_foreshadow_window_requires_code() -> None:
    with pytest.raises(ValidationError):
        ForeshadowWindow.model_validate({"plant_chapter": 1})


def test_outline_chapter_requires_no() -> None:
    with pytest.raises(ValidationError):
        OutlineChapter.model_validate({"beats": ["x"]})


def test_outliner_spec_is_analyst_tier() -> None:
    assert outliner_spec.tier == "analyst"
    assert outliner_spec.name == "outliner"


def test_outliner_spec_declares_expected_reads() -> None:
    assert outliner_spec.reads == (
        "projects",
        "foreshadow",
        "characters",
        "world_entities",
    )


def test_outliner_spec_declares_outline_write() -> None:
    # 声明式 writes（经验收/T3.5 才真写库，不变量 #3）
    assert outliner_spec.writes == ("outline",)


def test_outliner_spec_output_schema_is_outline_result() -> None:
    assert outliner_spec.output_schema is OutlineResult


def test_outliner_spec_has_nonempty_system_prompt() -> None:
    assert outliner_spec.system_prompt.strip()


def test_outliner_spec_is_immutable() -> None:
    with pytest.raises(ValidationError):
        outliner_spec.tier = "writer"


def test_outliner_spec_is_agent_spec() -> None:
    assert isinstance(outliner_spec, AgentSpec)
