"""T2.1 续审契约测试：ContinuityReview schema + continuity_spec 声明（C6）。

契约测试——构造符合 schema 的 mock 响应，校验 type 枚举约束、conflicts 列表、
四审只读（writes==[]）。不联网、无 DB。
"""

from __future__ import annotations

import pytest
from pydantic import ValidationError
from ww_agents import AgentSpec, Conflict, ContinuityReview, continuity_spec


def test_continuity_review_parses_mock_response() -> None:
    # Arrange：模拟网关 instructor 校验后的结构化产出
    mock = {
        "conflicts": [
            {
                "type": "性格漂移",
                "where": "第 3 段，主角忽然示弱",
                "refs": ["第 1 章人物卡：性格刚烈"],
                "suggestion": "改为强硬回应，或补内心转折铺垫",
            },
            {
                "type": "时间线倒错",
                "where": "开篇提到三日后",
                "refs": ["第 5 章 digest：事件发生在次日"],
                "suggestion": "统一为次日",
            },
        ]
    }

    # Act
    review = ContinuityReview.model_validate(mock)

    # Assert
    assert len(review.conflicts) == 2
    assert review.conflicts[0].type == "性格漂移"
    assert review.conflicts[1].refs == ["第 5 章 digest：事件发生在次日"]


def test_continuity_review_allows_empty_conflicts() -> None:
    review = ContinuityReview.model_validate({"conflicts": []})
    assert review.conflicts == []


def test_continuity_review_defaults_to_empty_conflicts() -> None:
    review = ContinuityReview()
    assert review.conflicts == []


def test_conflict_type_rejects_unknown_value() -> None:
    with pytest.raises(ValidationError):
        Conflict.model_validate({"type": "排版错误", "where": "x", "refs": [], "suggestion": "y"})


def test_conflict_refs_default_empty() -> None:
    conflict = Conflict.model_validate(
        {"type": "设定违例", "where": "战斗场景", "suggestion": "删去违例能力"}
    )
    assert conflict.refs == []


def test_conflict_patch_fields_default_none() -> None:
    # 一键采纳补丁字段（original/replacement）可缺：不可定位的冲突两者留空。
    conflict = Conflict.model_validate(
        {"type": "设定违例", "where": "战斗场景", "suggestion": "删去违例能力"}
    )
    assert conflict.original is None
    assert conflict.replacement is None


def test_conflict_carries_patch_pair_when_localizable() -> None:
    # 可局部修复：original 逐字原文最小句段 + replacement 改后文本，供前端一键 find-replace。
    conflict = Conflict.model_validate(
        {
            "type": "设定违例",
            "where": "第1段",
            "refs": [],
            "suggestion": "迈巴赫改为奔驰",
            "original": "迈巴赫扬长而去的尾灯",
            "replacement": "奔驰扬长而去的尾灯",
        }
    )
    assert conflict.original == "迈巴赫扬长而去的尾灯"
    assert conflict.replacement == "奔驰扬长而去的尾灯"


def test_continuity_spec_is_analyst_tier() -> None:
    assert continuity_spec.tier == "analyst"
    assert continuity_spec.name == "continuity"


def test_continuity_spec_is_read_only() -> None:
    # 不变量 #3：四审只读
    assert continuity_spec.writes == ()


def test_continuity_spec_declares_expected_reads() -> None:
    assert continuity_spec.reads == ("chapter_digests", "characters", "world_entities")


def test_continuity_spec_output_schema_is_continuity_review() -> None:
    assert continuity_spec.output_schema is ContinuityReview


def test_continuity_spec_has_nonempty_system_prompt() -> None:
    assert continuity_spec.system_prompt.strip()


def test_agent_spec_is_immutable() -> None:
    # frozen：加载后不得改动
    with pytest.raises(ValidationError):
        continuity_spec.tier = "writer"


def test_agent_spec_writer_allows_none_output_schema() -> None:
    writer = AgentSpec(name="writer", tier="writer", system_prompt="写")
    assert writer.output_schema is None
    assert writer.scope == "builtin"
