from __future__ import annotations

from typing import Any

from pydantic import BaseModel, ConfigDict, Field, field_validator, model_validator

from app.schemas.common import NaiveDatetimeOptional

HOT_OPENING_PROJECT_STATUS_DESCRIPTIONS: dict[str, str] = {
    "pending": "已创建但未进入流程",
    "waiting_user": "等待用户手动触发下一步",
    "processing": "当前有步骤处理中",
    "completed": "总任务完成",
    "failed": "总任务失败",
    "cancelled": "总任务取消",
}

HOT_OPENING_STEP_STATUS_DESCRIPTIONS: dict[str, str] = {
    "pending": "子任务待处理",
    "waiting_user": "等待用户确认或触发",
    "processing": "子任务处理中",
    "completed": "子任务完成",
    "failed": "子任务失败",
    "cancelled": "子任务取消",
}

HOT_OPENING_STEP_DESCRIPTIONS: list[dict[str, Any]] = [
    {"step_index": 1, "step_code": "material_input", "name": "素材输入"},
    {"step_index": 2, "step_code": "image_prompt_optimize", "name": "图片 AI 提词"},
    {"step_index": 3, "step_code": "image_generate", "name": "图片生成"},
    {"step_index": 4, "step_code": "video_prompt_optimize", "name": "视频 AI 提词 JSON schema"},
    {"step_index": 5, "step_code": "video_generate", "name": "视频生成"},
]

HOT_OPENING_STEP_IO_SCHEMA_VERSION = "hot_opening_step_io_v1"

HOT_OPENING_STEP_IO_EXAMPLES: dict[str, dict[str, Any]] = {
    "material_input": {
        "input_json": {
            "schema_version": HOT_OPENING_STEP_IO_SCHEMA_VERSION,
            "step_code": "material_input",
            "source": {"source_step_id": None, "parent_step_id": None},
            "payload": {
                "material_video_url": "https://example.com/source.mp4",
                "material_image_url": "https://example.com/product.png",
                "source_project_name": "参考素材项目名称",
                "target_project_name": "新项目名称",
                "core_content_point": "50字以内核心内容点",
            },
            "context": {},
        },
        "output_json": {
            "schema_version": HOT_OPENING_STEP_IO_SCHEMA_VERSION,
            "step_code": "material_input",
            "status": "completed",
            "payload": {},
            "result": {"accepted": True, "message": "素材输入已提交", "next_step_code": "image_prompt_optimize"},
            "usage": {},
            "error": {},
        },
    },
    "image_prompt_optimize": {
        "input_json": {
            "schema_version": HOT_OPENING_STEP_IO_SCHEMA_VERSION,
            "step_code": "image_prompt_optimize",
            "source": {"source_step_id": "第1步素材输入ID", "parent_step_id": "第1步素材输入ID"},
            "payload": {"source_step_id": "第1步素材输入ID"},
            "context": {},
        },
        "output_json": {
            "schema_version": HOT_OPENING_STEP_IO_SCHEMA_VERSION,
            "step_code": "image_prompt_optimize",
            "status": "completed",
            "payload": {
                "optimized_prompt": "图片生成提示词",
                "prompt": "兼容字段，同 optimized_prompt",
                "original_prompt": "后端拼接的图片提词原始需求",
                "references": [{"type": "video|image", "url": "...", "name": "..."}],
            },
            "result": {},
            "usage": {
                "input_tokens": 0,
                "output_tokens": 0,
                "total_tokens": 0,
                "text_credits_cost": 0,
                "credit_biz_key": "module_generation_step:{step_id}:attempt:1:text_prompt:charge",
            },
            "error": {},
        },
    },
    "image_generate": {
        "input_json": {
            "schema_version": HOT_OPENING_STEP_IO_SCHEMA_VERSION,
            "step_code": "image_generate",
            "source": {"source_step_id": "第2步图片提词ID", "parent_step_id": "第2步图片提词ID"},
            "payload": {
                "engine_id": "图片引擎ID",
                "params": {"image_size": "2K", "image_proportion": "1:1", "image_px": "2048x2048"},
                "prompt": "图片生成提示词",
                "media_references": [{"type": "image", "url": "新产品图片", "name": "新产品图片"}],
            },
            "context": {},
        },
        "output_json": {
            "schema_version": HOT_OPENING_STEP_IO_SCHEMA_VERSION,
            "step_code": "image_generate",
            "status": "completed",
            "payload": {},
            "result": {"result_image_url": "/generate/images/xxx.png", "chat_task_id": "ChatGenerationTask ID"},
            "usage": {},
            "error": {},
        },
    },
    "video_prompt_optimize": {
        "input_json": {
            "schema_version": HOT_OPENING_STEP_IO_SCHEMA_VERSION,
            "step_code": "video_prompt_optimize",
            "source": {"source_step_id": "第3步图片生成ID", "parent_step_id": "第3步图片生成ID"},
            "payload": {
                "source_step_id": "第3步图片生成ID",
                "video_config": {"engine_id": "视频引擎ID", "duration": 8, "aspect_ratio": "9:16", "resolution": "1080p"},
                "target_platform": "抖音",
            },
            "context": {},
        },
        "output_json": {
            "schema_version": HOT_OPENING_STEP_IO_SCHEMA_VERSION,
            "step_code": "video_prompt_optimize",
            "status": "completed",
            "payload": {
                "prompt_schema": {"任务基础信息": {}, "最终提示词": {}},
                "final_prompt": "展示用最终视频提示词",
                "params_used_for_prompt": {"duration": 8, "aspect_ratio": "9:16", "resolution": "1080p"},
                "target_platform": "抖音",
            },
            "result": {},
            "usage": {
                "input_tokens": 0,
                "output_tokens": 0,
                "total_tokens": 0,
                "text_credits_cost": 0,
                "credit_biz_key": "module_generation_step:{step_id}:attempt:1:text_prompt:charge",
            },
            "error": {},
        },
    },
    "video_generate": {
        "input_json": {
            "schema_version": HOT_OPENING_STEP_IO_SCHEMA_VERSION,
            "step_code": "video_generate",
            "source": {"source_step_id": "第4步视频提词ID", "parent_step_id": "第4步视频提词ID"},
            "payload": {
                "engine_id": "视频引擎ID",
                "params": {"duration": 8, "aspect_ratio": "9:16", "resolution": "1080p"},
                "prompt_schema": {"任务基础信息": {}, "最终提示词": {}},
                "final_prompt": "展示用最终提示词",
                "media_references": [{"type": "image", "url": "第3步生成图片", "name": "新项目图片"}],
            },
            "context": {},
        },
        "output_json": {
            "schema_version": HOT_OPENING_STEP_IO_SCHEMA_VERSION,
            "step_code": "video_generate",
            "status": "completed",
            "payload": {},
            "result": {
                "result_video_url": "/generate/videos/xxx.mp4",
                "result_video_cover_url": "/generate/covers/xxx.jpg",
                "chat_task_id": "ChatGenerationTask ID",
            },
            "usage": {},
            "error": {},
        },
    },
}


class HotOpeningTaskCreate(BaseModel):
    """创建爆款开头复刻总任务项目请求体。"""

    model_config = ConfigDict(
        json_schema_extra={
            "example": {
                "material_video_url": "https://example.com/source.mp4",
                "material_image_url": "https://example.com/product.png",
                "source_project_name": "参考素材项目名称",
                "target_project_name": "新项目名称",
                "core_content_point": "突出产品能帮助用户认识附近新朋友",
                "idempotency_key": "frontend-submit-uuid-001",
            }
        }
    )

    material_video_url: str = Field(..., min_length=1, description="素材视频链接，参考素材，1份。由项目已有上传接口返回，本接口不负责上传，不做后端素材校验")
    material_image_url: str = Field(..., min_length=1, description="素材图片链接，新产品图片，1份。由项目已有上传接口返回，本接口不负责上传，不做后端素材校验")
    source_project_name: str = Field(..., min_length=1, max_length=20, description="视频素材内容项目名称")
    target_project_name: str = Field(..., min_length=1, max_length=20, description="生成项目名称")
    core_content_point: str = Field(..., min_length=1, max_length=50, description="生成的项目核心内容点，最多50字")
    idempotency_key: str | None = Field(None, max_length=64, description="创建总任务幂等键。只用于 module_generation_projects，不用于 ChatGenerationTask")

    @field_validator("material_video_url", "material_image_url", "source_project_name", "target_project_name", "core_content_point")
    @classmethod
    def _strip_required(cls, value: str) -> str:
        value = str(value or "").strip()
        if not value:
            raise ValueError("字段不能为空")
        return value


class HotOpeningMaterialUpdateRequest(BaseModel):
    """修改爆款开头复刻第1步素材输入请求体。"""

    model_config = ConfigDict(
        json_schema_extra={
            "example": {
                "material_video_url": "https://example.com/new-source.mp4",
                "material_image_url": "https://example.com/new-product.png",
                "source_project_name": "新的参考素材项目名称",
                "target_project_name": "新的生成项目名称",
                "core_content_point": "新的50字以内核心内容点",
            }
        }
    )

    material_video_url: str | None = Field(None, min_length=1, description="素材视频链接，未传则沿用旧值")
    material_image_url: str | None = Field(None, min_length=1, description="素材图片链接，未传则沿用旧值")
    source_project_name: str | None = Field(None, min_length=1, max_length=20, description="视频素材内容项目名称，未传则沿用旧值")
    target_project_name: str | None = Field(None, min_length=1, max_length=20, description="生成项目名称，未传则沿用旧值")
    core_content_point: str | None = Field(None, min_length=1, max_length=50, description="生成项目核心内容点，最多50字，未传则沿用旧值")

    @field_validator("material_video_url", "material_image_url", "source_project_name", "target_project_name", "core_content_point", mode="before")
    @classmethod
    def _strip_optional(cls, value: str | None) -> str | None:
        if value is None:
            return None
        value = str(value).strip()
        if not value:
            raise ValueError("字段不能为空字符串")
        return value

    @model_validator(mode="after")
    def _require_at_least_one(self) -> "HotOpeningMaterialUpdateRequest":
        if not any(getattr(self, field) is not None for field in ("material_video_url", "material_image_url", "source_project_name", "target_project_name", "core_content_point")):
            raise ValueError("至少需要传入一个需要修改的字段")
        return self


class HotOpeningStepUpdate(BaseModel):
    """修改爆款开头复刻子任务请求体。"""

    material_video_url: str | None = Field(None, description="修改第1步素材视频链接")
    material_image_url: str | None = Field(None, description="修改第1步素材图片链接")
    source_project_name: str | None = Field(None, max_length=20, description="修改第1步视频素材内容项目名称")
    target_project_name: str | None = Field(None, max_length=20, description="修改第1步生成项目名称")
    core_content_point: str | None = Field(None, max_length=50, description="修改第1步生成项目核心内容点，最多50字")
    prompt: str | None = Field(None, description="修改第2步图片提词或第4步视频最终提词")
    prompt_schema: dict[str, Any] | None = Field(None, description="修改第4步视频提词 JSON schema。只对视频提词步骤有意义")
    input_json: dict[str, Any] | None = Field(None, description="高级用法：合并修改当前步骤 input_json.payload")
    output_json: dict[str, Any] | None = Field(None, description="高级用法：合并修改当前步骤 output_json.payload")


class HotOpeningImagePromptUpdateRequest(BaseModel):
    """直接修改第2步图片 AI 优化提词请求体。

    本接口不调用 AI、不扣积分；保存后会软删除第3、4、5步当前有效任务。
    """

    model_config = ConfigDict(
        extra="ignore",
        json_schema_extra={"example": {"prompt": "用户手动修改后的图片生成提示词"}},
    )

    prompt: str = Field(..., min_length=1, description="用户手动修改后的图片生成提示词，不能为空")

    @field_validator("prompt", mode="before")
    @classmethod
    def _strip_prompt(cls, value: str) -> str:
        value = str(value or "").strip()
        if not value:
            raise ValueError("图片提示词不能为空")
        return value


class HotOpeningVideoPromptSchemaUpdateRequest(BaseModel):
    """修改第4步视频 AI 提词 JSON schema 请求体。

    前端提交的 prompt_schema 只作为 patch：服务端会锁定视频时长、比例、清晰度、帧率、推荐分辨率、
    动作/镜头/动态时间规划数组长度和时间段、输出规格限制、质量控制、合规控制、schema_version、schema_usage。
    最终提示词允许修改，但保存前会清洗视频参数。
    """

    model_config = ConfigDict(
        extra="ignore",
        json_schema_extra={
            "example": {
                "prompt_schema": {
                    "业务属性": {"产品名称": "脱单交友APP", "行动引导": "立即下载"},
                    "最终提示词": {"主提示词": "脱单交友APP推广短视频，突出认识附近新朋友和高效匹配"},
                }
            }
        },
    )

    prompt_schema: dict[str, Any] = Field(..., description="前端修改后的视频提词 JSON schema。后端只按白名单回填允许修改字段")

    @field_validator("prompt_schema")
    @classmethod
    def _validate_schema(cls, value: dict[str, Any]) -> dict[str, Any]:
        if not isinstance(value, dict) or not value:
            raise ValueError("prompt_schema 必须是非空 JSON 对象")
        return value


class HotOpeningGenerateImagePromptRequest(BaseModel):
    """手动生成第2步图片 AI 提词请求体。当前无需请求参数。"""

    model_config = ConfigDict(extra="ignore")


class HotOpeningGenerateImageRequest(BaseModel):
    """根据图片提词生成新项目图片请求体。

    ChatGenerationTask.idempotency_key 由后端自动生成，接口不再接收前端幂等键。
    """

    model_config = ConfigDict(
        extra="ignore",
        json_schema_extra={"example": {"engine_id": "image_engine_xxx", "image_size": "2K", "image_proportion": "1:1", "image_px": "2048x2048"}},
    )

    engine_id: str | None = Field(None, description="图片生成引擎ID。为空则使用当前启用且优先级最高的图片引擎")
    image_size: str | None = Field(None, description="图片分辨率档位，例如 1K、2K。具体可选值来自图片引擎配置接口；为空使用引擎默认值")
    image_proportion: str | None = Field(None, description="图片比例，例如 1:1、16:9、9:16。具体可选值来自图片引擎配置接口；为空使用默认值")
    image_px: str | None = Field(None, description="图片像素尺寸，例如 1024x1024、2048x2048。具体可选值来自图片引擎配置接口；为空时按引擎支持尺寸自动匹配")


class HotOpeningGenerateVideoPromptRequest(BaseModel):
    """手动生成第4步视频 AI 提词请求体。

    视频时长、比例、分辨率集中在本步骤确定；第5步生成视频只选择视频引擎。
    """

    model_config = ConfigDict(
        extra="ignore",
        json_schema_extra={"example": {"engine_id": "video_engine_xxx", "duration": 8, "aspect_ratio": "9:16", "resolution": "1080p", "target_platform": "抖音"}},
    )

    engine_id: str | None = Field(None, description="视频引擎ID。用于读取该引擎支持的视频时长、比例、分辨率配置；为空使用最高优先级启用引擎")
    duration: int | None = Field(None, ge=1, description="希望用于视频提词规划的视频时长，单位秒。具体可选值来自视频引擎 supported_durations；为空时优先使用 HOT_OPENING_DEFAULT_VIDEO_DURATION")
    aspect_ratio: str | None = Field(None, description="希望用于视频提词规划的视频比例，例如 9:16、16:9、1:1。具体可选值来自视频引擎 supported_ratios；为空时优先使用 HOT_OPENING_DEFAULT_VIDEO_RATIO")
    resolution: str | None = Field(None, description="希望用于视频提词规划的视频分辨率，例如 480p、720p、1080p。具体可选值来自视频引擎 supported_resolutions；为空时优先使用 HOT_OPENING_DEFAULT_VIDEO_RESOLUTION")
    target_platform: str | None = Field(None, max_length=64, description="目标平台，例如抖音/快手/小红书。为空时使用 HOT_OPENING_DEFAULT_TARGET_PLATFORM")


class HotOpeningGenerateVideoRequest(BaseModel):
    """根据视频提词生成最终视频请求体。

    只选择视频生成引擎。duration / aspect_ratio / resolution 从第4步视频提词优化结果读取。
    ChatGenerationTask.original_prompt / optimized_prompt 都写入第4步生成的 prompt_schema JSON 字符串。
    """

    model_config = ConfigDict(extra="ignore", json_schema_extra={"example": {"engine_id": "video_engine_xxx"}})

    engine_id: str | None = Field(None, description="视频生成引擎ID。为空优先使用第4步视频提词时选择的 engine_id，再为空使用最高优先级启用视频引擎")


class HotOpeningStepOut(BaseModel):
    id: str = Field(..., description="子任务ID")
    project_id: str = Field(..., description="总任务项目ID，即 module_generation_projects.id")
    module: str = Field(..., description="模块标识，例如 hot_opening_replicate")
    step_index: int = Field(..., description="步骤序号：1素材输入、2图片提词、3图片生成、4视频提词、5视频生成")
    step_code: str = Field(..., description="步骤编码：material_input/image_prompt_optimize/image_generate/video_prompt_optimize/video_generate")
    status: str = Field(..., description="步骤状态：pending=待处理，waiting_user=等待用户操作，processing=处理中，completed=完成，failed=失败，cancelled=取消")
    version: int = Field(..., description="步骤版本号。重新生成或修改上游步骤后 version+1")
    is_current: bool = Field(..., description="是否当前有效步骤。旧步骤会软删除且 is_current=false")
    parent_step_id: str | None = Field(None, description="上一个步骤ID")
    source_step_id: str | None = Field(None, description="当前步骤基于哪个上游步骤生成")
    chat_task_id: str | None = Field(None, description="关联的 ChatGenerationTask ID。第3步图片生成、第5步视频生成有值")
    input: dict[str, Any] | None = Field(None, description=f"步骤输入 JSON，统一 schema_version={HOT_OPENING_STEP_IO_SCHEMA_VERSION}")
    output: dict[str, Any] | None = Field(None, description=f"步骤输出 JSON，统一 schema_version={HOT_OPENING_STEP_IO_SCHEMA_VERSION}")
    error_message: str | None = Field(None, description="步骤错误信息")
    created_at: NaiveDatetimeOptional = Field(None, description="创建时间")
    updated_at: NaiveDatetimeOptional = Field(None, description="更新时间")
    completed_at: NaiveDatetimeOptional = Field(None, description="完成时间")


class HotOpeningMaterialOut(BaseModel):
    material_step_id: str | None = Field(None, description="第1步素材输入子任务ID")
    material_video_url: str | None = Field(None, description="素材视频链接")
    material_image_url: str | None = Field(None, description="素材图片链接")
    source_project_name: str | None = Field(None, description="视频素材内容项目名称")
    target_project_name: str | None = Field(None, description="生成项目名称")
    core_content_point: str | None = Field(None, description="生成项目核心内容点")


class HotOpeningImageGenerationOut(BaseModel):
    prompt_step_id: str | None = Field(None, description="第2步图片 AI 提词子任务ID")
    generate_step_id: str | None = Field(None, description="第3步图片生成子任务ID")
    prompt: str | None = Field(None, description="图片优化提词")
    engine_id: str | None = Field(None, description="图片生成引擎ID")
    engine_name: str | None = Field(None, description="图片生成引擎名称")
    params: dict[str, Any] | None = Field(None, description="图片生成参数")
    chat_task_id: str | None = Field(None, description="图片生成 ChatGenerationTask ID")
    status: str | None = Field(None, description="图片生成状态")
    result_image_url: str | None = Field(None, description="新项目图片 URL")
    error_message: str | None = Field(None, description="图片生成错误信息")


class HotOpeningVideoGenerationOut(BaseModel):
    prompt_step_id: str | None = Field(None, description="第4步视频 AI 提词子任务ID")
    generate_step_id: str | None = Field(None, description="第5步视频生成子任务ID")
    prompt_schema: dict[str, Any] | None = Field(None, description="视频提词 JSON schema。第5步 ChatGenerationTask 原始提词会使用该 JSON 字符串")
    final_prompt: str | None = Field(None, description="视频最终提词，仅用于前端展示")
    prompt_params: dict[str, Any] | None = Field(None, description="第4步生成视频提词时使用的视频配置，例如 duration、aspect_ratio、resolution")
    schema_config_snapshot: dict[str, Any] | None = Field(None, description="第4步生成视频提词时备份的 VIDEO_SCHEMA 配置快照。修改时按快照判断可编辑字段")
    schema_config_source: str | None = Field(None, description="VIDEO_SCHEMA 配置来源：database=后台配置，default=CLIENT_SCHEMA_V1 默认配置")
    schema_config_version: str | None = Field(None, description="VIDEO_SCHEMA 配置版本")
    schema_config_is_fallback: bool = Field(False, description="是否为接口运行时兼容历史数据临时补齐的默认 VIDEO_SCHEMA 配置。true 表示数据库原始第4步没有 schema_config_snapshot")
    engine_id: str | None = Field(None, description="视频生成引擎ID")
    engine_name: str | None = Field(None, description="视频生成引擎名称")
    params: dict[str, Any] | None = Field(None, description="视频生成实际参数。第5步只传 engine_id，其它参数继承第4步")
    chat_task_id: str | None = Field(None, description="视频生成 ChatGenerationTask ID")
    status: str | None = Field(None, description="视频生成状态")
    result_video_url: str | None = Field(None, description="最终视频 URL")
    result_video_cover_url: str | None = Field(None, description="最终视频封面 URL")
    error_message: str | None = Field(None, description="视频生成错误信息")


class HotOpeningTaskDetailOut(BaseModel):
    id: str = Field(..., description="总任务项目ID。这个ID就是前端项目ID")
    project_id: str = Field(..., description="兼容前端命名，等同于 id")
    user_id: str | None = Field(None, description="所属用户ID；管理员后台排查使用")
    user_name: str | None = Field(None, description="所属用户名；管理员后台排查使用")
    module: str = Field(..., description="模块标识，爆款开头复刻固定为 hot_opening_replicate")
    title: str | None = Field(None, description="项目标题，默认取生成项目名称")
    status: str = Field(..., description="总任务状态：pending=已创建，waiting_user=等待用户操作，processing=处理中，completed=完成，failed=失败，cancelled=取消")
    current_step_code: str | None = Field(None, description="当前所处步骤编码")
    final_image_url: str | None = Field(None, description="最终新项目图片 URL")
    final_video_url: str | None = Field(None, description="最终视频 URL")
    final_video_cover_url: str | None = Field(None, description="最终视频封面 URL")
    error_message: str | None = Field(None, description="总任务错误信息")
    material: HotOpeningMaterialOut = Field(default_factory=HotOpeningMaterialOut, description="素材和项目描述信息")
    image_generation: HotOpeningImageGenerationOut = Field(default_factory=HotOpeningImageGenerationOut, description="图片提词、图片引擎参数和图片结果")
    video_generation: HotOpeningVideoGenerationOut = Field(default_factory=HotOpeningVideoGenerationOut, description="视频提词、视频引擎参数和视频结果")
    steps: list[HotOpeningStepOut] = Field(default_factory=list, description="当前有效子任务列表")
    created_at: NaiveDatetimeOptional = Field(None, description="创建时间")
    updated_at: NaiveDatetimeOptional = Field(None, description="更新时间")
    completed_at: NaiveDatetimeOptional = Field(None, description="完成时间")


class HotOpeningTaskListItemOut(BaseModel):
    id: str = Field(..., description="总任务项目ID。这个ID就是前端项目ID")
    project_id: str = Field(..., description="兼容前端命名，等同于 id")
    user_id: str | None = Field(None, description="所属用户ID；管理员后台排查使用")
    user_name: str | None = Field(None, description="所属用户名；管理员后台排查使用")
    module: str = Field(..., description="模块标识")
    title: str | None = Field(None, description="项目标题")
    status: str = Field(..., description="总任务状态：pending=已创建，waiting_user=等待用户操作，processing=处理中，completed=完成，failed=失败，cancelled=取消")
    current_step_code: str | None = Field(None, description="当前步骤")
    source_project_name: str | None = Field(None, description="视频素材内容项目名称，来源于第1步素材输入")
    target_project_name: str | None = Field(None, description="生成项目名称，来源于第1步素材输入")
    core_content_point: str | None = Field(None, description="核心内容点，来源于第1步素材输入")
    final_image_url: str | None = Field(None, description="最终图片 URL")
    final_video_url: str | None = Field(None, description="最终视频 URL")
    final_video_cover_url: str | None = Field(None, description="最终视频封面 URL")
    error_message: str | None = Field(None, description="错误信息")
    created_at: NaiveDatetimeOptional = Field(None, description="创建时间")
    updated_at: NaiveDatetimeOptional = Field(None, description="更新时间")
    completed_at: NaiveDatetimeOptional = Field(None, description="完成时间")


class HotOpeningTaskListOut(BaseModel):
    total: int = Field(..., description="总数量")
    items: list[HotOpeningTaskListItemOut] = Field(default_factory=list, description="列表数据")


class HotOpeningActionOut(BaseModel):
    message: str = Field(..., description="操作结果提示")
    project_id: str = Field(..., description="总任务项目ID")
    step_id: str | None = Field(None, description="本次创建或修改的子任务ID")
    next_step_id: str | None = Field(None, description="兼容字段：当前接口不自动生成下下个任务，一般为空")
    detail: HotOpeningTaskDetailOut | None = Field(None, description="操作后的总任务详情")


class HotOpeningDeleteOut(BaseModel):
    message: str = Field(..., description="删除结果提示")
    project_id: str = Field(..., description="被软删除的总任务项目ID")
    deleted: bool = Field(..., description="是否已软删除")


class HotOpeningSpecOut(BaseModel):
    project_statuses: dict[str, str] = Field(default_factory=lambda: HOT_OPENING_PROJECT_STATUS_DESCRIPTIONS, description="总任务状态说明")
    step_statuses: dict[str, str] = Field(default_factory=lambda: HOT_OPENING_STEP_STATUS_DESCRIPTIONS, description="子任务状态说明")
    steps: list[dict[str, Any]] = Field(default_factory=lambda: HOT_OPENING_STEP_DESCRIPTIONS, description="5个固定步骤说明")
    step_io_schema_version: str = Field(default=HOT_OPENING_STEP_IO_SCHEMA_VERSION, description="步骤 input_json/output_json 结构版本")
    step_io_examples: dict[str, dict[str, Any]] = Field(default_factory=lambda: HOT_OPENING_STEP_IO_EXAMPLES, description="每个步骤 input_json/output_json 示例")
