"""创作工具箱通用端点的请求/响应 schema（C3 扩 T6 / 通用生成器框架）。

snake_case；前端经 OpenAPI 生成 TS 类型消费。改字段 → 前端必须 `pnpm gen:api`。

- `GET /skills/toolbox` → `ToolboxListResponse`（声明式描述符视图，含 legacy_route 供前端路由）。
- `POST .../skills/{tool_key}/generate` → `ToolGeneratePreviewResponse`（结构化预览，不入库）。
- `POST .../skills/{tool_key}/ingest` → `ToolIngestResponse`（过 continuity gate + 白名单后入库）。

预览/入库共用既有 schema：world_entities 入库走 `WorldEntityCardView`（贴 worldbuilder 形），
outline 入库走 `OutlineSceneIngestView`（贴 fine-outline 场景行）。
"""

from __future__ import annotations

from typing import Any

from pydantic import BaseModel, Field

from ww_api.schemas.generation import IngestConflictView, WorldEntityCardView

# 一次性生成数量上限沿用 generation 的保守值（避免巨量调用）；章号下限。
MIN_CHAPTER_NO = 1


# ---- GET /skills/toolbox（描述符列表）----


class ToolInputFieldView(BaseModel):
    """单个声明式表单字段视图（贴 ww_skills.InputField）。"""

    name: str
    label: str
    type: str = Field(description="控件类型：text / textarea / number / select 等")
    required: bool = True
    default: str | None = None
    help: str | None = None


class ToolDescriptorView(BaseModel):
    """单个生成器卡片描述符视图（前端据此渲染卡片栅格 + 输入表单）。"""

    key: str = Field(description="路由用稳定标识（brainstorm / book-title ...）")
    title: str
    subtitle: str
    genre: str | None = None
    is_legacy: bool = Field(description="legacy 工具（spec=None）→ 前端走 legacy_route 跳现有页面")
    ingestable: bool = Field(description="是否可入库（声明了 ingest 目标）")
    input_fields: list[ToolInputFieldView] = Field(default_factory=list)
    legacy_route: str | None = Field(
        default=None, description="legacy 工具指向的现有页面路径（含 {id} 占位）"
    )


class ToolboxListResponse(BaseModel):
    """GET /skills/toolbox：创作工具箱全量描述符（legacy + 新工具）。"""

    tools: list[ToolDescriptorView] = Field(default_factory=list)


# ---- POST .../skills/{tool_key}/generate（结构化预览）----


class ToolGenerateRequest(BaseModel):
    """通用生成请求（覆盖全部工具的可选入参；按工具 input_fields 取用，snake_case）。"""

    brief: str = Field(default="", description="一句话需求/方向（可空，空则按题材发散）")
    text: str | None = Field(
        default=None,
        description="原文输入（扩写/降AI 的原文、拆书的样本；text_input 策略工具用）",
    )
    chapter_no: int | None = Field(
        default=None,
        ge=MIN_CHAPTER_NO,
        description="按章展开/续写类工具的章号（开篇/细纲/续写）",
    )
    count: int | None = Field(default=None, ge=1, le=12, description="生成数量（部分工具可用）")
    kind: str | None = Field(default=None, description="命名对象类别等（部分工具可用）")


class ToolGeneratePreviewResponse(BaseModel):
    """通用生成预览：结构化产物（不持久化；仅记 ledger）。

    `output_kind` = 该工具产物的 schema 名（如 IdeaListResult），供前端按类型选预览渲染器；
    `preview` = 解析后的结构化产物（按各工具 output_schema 的形，键名见 ww_agents.schemas）。
    """

    tool_key: str
    output_kind: str = Field(description="产物 schema 名（前端按此选预览渲染器）")
    preview: dict[str, Any] = Field(
        default_factory=dict,
        description="结构化产物（各工具 output_schema 的 model_dump；不入库）",
    )


# ---- POST .../skills/{tool_key}/ingest（入库；过 continuity gate + 白名单）----


class OutlineSceneIngestView(BaseModel):
    """细纲入库的单章场景行（贴 ww_agents.Scene；落 outline 表）。"""

    idx: int
    beat: str
    purpose: str = ""
    conflict: str = ""
    hook: str = ""


class TeardownIngestView(BaseModel):
    """拆书入库产物（贴 ww_agents.BookTeardownResult；落 rules 表）。F1。"""

    themes: list[str] = Field(default_factory=list, description="核心主题/立意清单")
    archetypes: list[str] = Field(default_factory=list, description="人物原型/角色模板清单")
    structure: str = Field(default="", description="叙事结构概述")
    hooks: list[str] = Field(default_factory=list, description="抓人钩子/爽点套路清单")


class ToolIngestRequest(BaseModel):
    """通用入库请求：待持久化的产物 + 冲突确认。

    仅声明了 `ingest` 的工具可用。按入库表取用对应字段：
    - world_entities（金手指/词条）→ `world_entities`（贴 WorldEntityCardView：type/name/rules）；
    - outline（细纲）→ `chapter_no` + `scenes`（贴 OutlineSceneIngestView）；
    - rules（拆书）→ `teardown`（贴 TeardownIngestView：themes/archetypes/structure/hooks）。
    `acknowledge_conflicts`：作者已查看并接受 continuity 预检冲突时置 true 放行（仿 accept gate）。
    """

    world_entities: list[WorldEntityCardView] = Field(
        default_factory=list, description="world_entities 入库项（金手指/词条）"
    )
    chapter_no: int | None = Field(
        default=None, ge=MIN_CHAPTER_NO, description="outline 入库的目标章号（细纲）"
    )
    scenes: list[OutlineSceneIngestView] = Field(
        default_factory=list, description="outline 入库的场景行（细纲）"
    )
    teardown: TeardownIngestView | None = Field(
        default=None, description="rules 入库的拆书结论（F1；拍平为可读规则条目）"
    )
    acknowledge_conflicts: bool = Field(
        default=False, description="作者已知悉并接受 continuity 冲突 → 放行入库"
    )


class ToolIngestResponse(BaseModel):
    """通用入库结果（201）：写入项标识 + 被白名单丢弃的越权表（审计）。"""

    table: str = Field(description="入库目标表")
    created: list[str] = Field(
        default_factory=list, description="写入项的标识（实体名 / 场景索引），按入参顺序"
    )
    rejected_tables: list[str] = Field(
        default_factory=list, description="被权限白名单丢弃的越权写表名（审计；正常为空）"
    )


class ToolIngestConflictDetails(BaseModel):
    """409 冲突详情（贴 generation 的 IngestConflictView）。"""

    conflicts: list[IngestConflictView] = Field(default_factory=list)
    conflict_count: int = 0
