"""立项方案生成器（project-plan）契约测试：schema + spec（灵感⑤）。

契约测试——构造符合 schema 的 mock 响应，校验字段默认值（解析韧性）、tier、只读权限。
project-plan：`{title_candidates:[str], setting, narrative_structure, story_core,
ending_design, tone}`（分析档，reads=()，writes=() 纯预览）。不联网、无 DB。
"""

from __future__ import annotations

import pytest
from pydantic import ValidationError
from ww_agents import (
    SPECS,
    AgentSpec,
    ProjectPlanResult,
    project_plan_spec,
)

# ---- ProjectPlanResult schema ----


def test_project_plan_parses_mock_response() -> None:
    # Arrange：模拟网关 instructor 校验后的结构化立项方案产出
    mock = {
        "title_candidates": ["逐光而行", "剑试九霄", "凡骨录"],
        "setting": "上古仙门倾轧的洞天世界",
        "narrative_structure": "黄金三章立钩 → 拜师入门 → 宗门大比爆发",
        "story_core": "废柴少年觉醒禁忌血脉，在仙门倾轧中逆势封神，代价是逐渐失去人性",
        "ending_design": "主角登顶却选择归隐，核心冲突以自我救赎收束（正剧）",
        "tone": "热血 + 悲怆",
    }

    # Act
    result = ProjectPlanResult.model_validate(mock)

    # Assert
    assert result.title_candidates == ["逐光而行", "剑试九霄", "凡骨录"]
    assert result.setting == "上古仙门倾轧的洞天世界"
    assert result.narrative_structure.startswith("黄金三章")
    assert "逆势封神" in result.story_core
    assert result.ending_design.endswith("（正剧）")
    assert result.tone == "热血 + 悲怆"


def test_project_plan_all_fields_default_for_parse_resilience() -> None:
    # 全字段给默认值守解析韧性：LLM 漏产任一字段不致整卡解析失败（仿 StyleDriftReview 降级）。
    result = ProjectPlanResult()
    assert result.title_candidates == []
    assert result.setting == ""
    assert result.narrative_structure == ""
    assert result.story_core == ""
    assert result.ending_design == ""
    assert result.tone == ""


def test_project_plan_partial_response_degrades_gracefully() -> None:
    # 只给书名候选，其余缺失 → 缺字段降级为空串，不报错。
    result = ProjectPlanResult.model_validate({"title_candidates": ["书名甲"]})
    assert result.title_candidates == ["书名甲"]
    assert result.story_core == ""


# ---- project_plan_spec 声明 ----


def test_project_plan_spec_is_analyst_tier() -> None:
    # 不变量 #2：立项方案构思用分析档，只声明 tier、不写 model
    assert project_plan_spec.tier == "analyst"
    assert project_plan_spec.name == "project-plan"


def test_project_plan_spec_is_read_only_preview() -> None:
    # 立项前 project 未建：reads=()（无库可读）；纯预览 writes=()（不变量 #3）
    assert project_plan_spec.reads == ()
    assert project_plan_spec.writes == ()


def test_project_plan_spec_output_schema() -> None:
    assert project_plan_spec.output_schema is ProjectPlanResult


def test_project_plan_spec_registered_in_specs() -> None:
    # 注册表按 name 命中同一实例（不变量：SPECS[name] is *_spec）
    assert SPECS["project-plan"] is project_plan_spec


def test_project_plan_spec_is_immutable() -> None:
    assert isinstance(project_plan_spec, AgentSpec)
    with pytest.raises(ValidationError):
        project_plan_spec.tier = "writer"


def test_project_plan_spec_prompt_documents_seed_discipline() -> None:
    # prompt 必须强调忠于种子（题材 + logline），不另起炉灶
    prompt = project_plan_spec.system_prompt
    assert "logline" in prompt
    assert "种子" in prompt
