"""T6 创作工具箱声明式描述符类型单测（Wave-0 契约）。

仅校验描述符**类型**本身（无 TOOLBOX 注册表 seeding——那是 Wave A）：
- `ContextStrategy`：注入策略字面量（4 基础 + 续写/原文输入 2 竞品策略）；
- `InputField`：声明式表单字段（label/type/required/default/help）；
- `IngestSpec`：可选入库目标（table 必须在 KNOWN_TABLES，None=纯预览）；
- `GeneratorTool`：生成器声明（legacy 带 legacy_route 且 spec 可为 None；新工具带 spec+schema）。

全部 frozen（不可变，全局 immutability 约定）——构造后改字段须抛错。
"""

from __future__ import annotations

import dataclasses

import pytest
from pydantic import BaseModel, ValidationError
from ww_agents import AgentSpec
from ww_shared import AppError, ErrorCode
from ww_skills import (
    ContextStrategy,
    GeneratorTool,
    IngestSpec,
    InputField,
)


class _FakeOut(BaseModel):
    """构造测试用的 output_schema 占位（Pydantic 模型类）。"""

    value: str


def _spec() -> AgentSpec:
    return AgentSpec(
        name="brainstorm",
        tier="light",
        system_prompt="x",
        output_schema=_FakeOut,
        reads=("projects",),
        writes=(),
    )


# ---- ContextStrategy ----


def test_context_strategy_values() -> None:
    # Arrange / Act
    from typing import get_args

    values = set(get_args(ContextStrategy))

    # Assert：4 基础策略 + 竞品快赢 2 策略（续写读前文 / 原文输入）。
    assert values == {
        "brief_only",
        "with_project",
        "with_world",
        "with_outline_chapter",
        "with_prior_chapter",
        "text_input",
    }


# ---- InputField ----


def test_input_field_required_fields_and_defaults() -> None:
    # Arrange / Act
    field = InputField(name="brief", label="一句话需求", type="textarea")

    # Assert: required 默认 True，其余可空
    assert field.name == "brief"
    assert field.label == "一句话需求"
    assert field.type == "textarea"
    assert field.required is True
    assert field.default is None
    assert field.help is None


def test_input_field_accepts_optional_metadata() -> None:
    field = InputField(
        name="count",
        label="数量",
        type="number",
        required=False,
        default="3",
        help="一次生成几条",
    )
    assert field.required is False
    assert field.default == "3"
    assert field.help == "一次生成几条"


def test_input_field_is_frozen() -> None:
    field = InputField(name="brief", label="需求", type="text")
    with pytest.raises(ValidationError):
        field.label = "改名"


# ---- IngestSpec ----


def test_ingest_spec_accepts_known_table() -> None:
    spec = IngestSpec(table="world_entities")
    assert spec.table == "world_entities"


def test_ingest_spec_rejects_unknown_table() -> None:
    # 越权/不存在的入库表 → VALIDATION（不可信声明的第一道闸）
    with pytest.raises(AppError) as exc:
        IngestSpec(table="users")
    assert exc.value.code is ErrorCode.VALIDATION


def test_ingest_spec_is_frozen() -> None:
    spec = IngestSpec(table="outline")
    with pytest.raises(ValidationError):
        spec.table = "characters"


# ---- GeneratorTool ----


def test_generator_tool_new_tool_with_spec_and_schema() -> None:
    # Arrange / Act：新工具带 spec + output_schema，无 legacy_route
    tool = GeneratorTool(
        key="brainstorm",
        title="脑洞生成器",
        subtitle="突破想象，脑洞大开",
        spec=_spec(),
        output_schema=_FakeOut,
        context_strategy="brief_only",
        input_fields=[InputField(name="brief", label="需求", type="textarea", required=False)],
    )

    # Assert
    assert tool.key == "brainstorm"
    assert tool.spec is not None
    assert tool.output_schema is _FakeOut
    assert tool.context_strategy == "brief_only"
    assert tool.ingest is None
    assert tool.legacy_route is None
    assert tool.genre is None
    assert len(tool.input_fields) == 1


def test_generator_tool_legacy_tool_without_spec() -> None:
    # legacy 工具：spec=None + 指向现有页面/端点
    tool = GeneratorTool(
        key="worldbuilding",
        title="世界观生成器",
        subtitle="构建自洽世界",
        spec=None,
        output_schema=None,
        context_strategy="with_project",
        input_fields=[],
        legacy_route="/projects/{id}/world",
    )
    assert tool.spec is None
    assert tool.output_schema is None
    assert tool.legacy_route == "/projects/{id}/world"


def test_generator_tool_with_ingest_and_genre() -> None:
    tool = GeneratorTool(
        key="glossary",
        title="词条生成器",
        subtitle="批量造词条",
        spec=_spec(),
        output_schema=_FakeOut,
        context_strategy="with_world",
        input_fields=[],
        ingest=IngestSpec(table="world_entities"),
        genre="xuanhuan",
    )
    assert tool.ingest is not None
    assert tool.ingest.table == "world_entities"
    assert tool.genre == "xuanhuan"


def test_generator_tool_is_frozen() -> None:
    tool = GeneratorTool(
        key="brainstorm",
        title="脑洞生成器",
        subtitle="x",
        spec=_spec(),
        output_schema=_FakeOut,
        context_strategy="brief_only",
        input_fields=[],
    )
    with pytest.raises(ValidationError):
        tool.title = "改名"


def test_generator_tool_input_fields_default_empty() -> None:
    tool = GeneratorTool(
        key="x",
        title="t",
        subtitle="s",
        spec=None,
        output_schema=None,
        context_strategy="brief_only",
        input_fields=[],
        legacy_route="/x",
    )
    # input_fields 是显式入参；frozen 后整体不可重绑
    assert tool.input_fields == []
    assert not dataclasses.is_dataclass(tool)  # 用 Pydantic，非 dataclass
