from __future__ import annotations

from types import SimpleNamespace

from fastapi import APIRouter, Body, Depends, HTTPException, Path, Query
from sqlalchemy import inspect as sa_inspect
from sqlalchemy.ext.asyncio import AsyncSession

from app.dependencies import get_current_user, get_db
from app.models.user import User
from app.enums.shot_replicate import ModuleCodeEnum, ShotReplicateStepCodeEnum
from app.schemas.shot_replicate import (
    ShotReplicateActionOut,
    ShotReplicateDeleteOut,
    ShotReplicateGenerateImagePromptRequest,
    ShotReplicateGenerateImageRequest,
    ShotReplicateGenerateVideoPromptRequest,
    ShotReplicateGenerateVideoRequest,
    ShotReplicateImagePromptUpdateRequest,
    ShotReplicateMaterialUpdateRequest,
    ShotReplicateSpecOut,
    ShotReplicateTaskDetailOut,
    ShotReplicateVideoPromptSchemaUpdateRequest,
    ShotSegmentDetailOut,
    ShotSegmentListOut,
    ShotSegmentReplicationCreateRequest,
    ShotSplitByAIOut,
    ShotSplitByAIRequest,
    ShotSplitCustomOut,
    ShotSplitCustomRequest,
    ShotTaskSetCreate,
    ShotTaskSetDetailOut,
    ShotTaskSetListOut,
)
from app.services.shot_replicate_flow_service import (
    _get_project_for_user,
    create_shot_replicate_project_from_segment,
    delete_shot_replicate_project,
    generate_image_from_prompt,
    generate_video_from_prompt,
    mark_shot_replicate_step_dispatch_failed,
    project_to_detail_out,
    submit_image_prompt_optimize,
    submit_video_prompt_optimize,
    update_shot_replicate_image_prompt,
    update_shot_replicate_material_input,
    update_shot_replicate_video_prompt_schema,
)
from app.services.shot_replicate_taskset_service import (
    create_custom_segment,
    create_segments_by_ai,
    create_task_set,
    get_segment_for_user,
    list_segments,
    list_task_sets,
    segment_detail,
    task_set_detail,
)
from app.services.module_generation_log_service import log_module_error, log_module_event_file
from app.services.module_async_recovery_service import (
    TASK_SHOT_IMAGE_PROMPT,
    TASK_SHOT_VIDEO_PROMPT,
    register_module_step_task,
    register_shot_segment_analysis_task,
    register_shot_split_task,
    register_shot_task_set_analysis_task,
)
from app.tasks.celery_app import celery_app

MODULE = ModuleCodeEnum.SHOT_REPLICATE.value



def _safe_user_id(user: object | None) -> str | None:
    """从 ORM 对象中安全取用户ID，避免 rollback/commit 后访问过期属性触发 MissingGreenlet。"""
    if user is None:
        return None
    try:
        value = getattr(user, "__dict__", {}).get("id")
        if value is not None:
            return str(value)
    except Exception:
        pass
    try:
        identity = sa_inspect(user).identity
        if identity:
            return str(identity[0])
    except Exception:
        pass
    return None


def _safe_user_is_admin(user: object | None) -> bool:
    """安全判断管理员身份；如果对象属性已过期，保守按普通用户处理。"""
    if user is None:
        return False
    try:
        data = getattr(user, "__dict__", {})
        if "is_admin" in data:
            return bool(data.get("is_admin"))
    except Exception:
        pass
    return False


def _user_context(user: object | None) -> SimpleNamespace:
    return SimpleNamespace(id=_safe_user_id(user), is_admin=_safe_user_is_admin(user))

router = APIRouter(prefix="/shot-replications", tags=["shot-replications"])




def _log_api_error(
    *,
    event_type: str,
    current_user: User | None = None,
    project_id: str | None = None,
    step_id: str | None = None,
    message: str | None = None,
    exc: BaseException | None = None,
    detail: dict | None = None,
) -> None:
    log_module_error(
        module=MODULE,
        event_type=event_type,
        project_id=project_id,
        step_id=step_id,
        user_id=_safe_user_id(current_user),
        message=message,
        detail=detail,
        exc=exc,
    )


def _log_api_exception_from_locals(exc: BaseException, local_values: dict, message: str) -> None:
    current_user = local_values.get("current_user")
    project_id = local_values.get("project_id_value") or local_values.get("project_id") or local_values.get("task_set_id") or local_values.get("task_set_id_value")
    step_id = local_values.get("step_id_value") or local_values.get("step_id") or local_values.get("segment_id") or local_values.get("segment_id_value")
    req = local_values.get("req")
    detail = {"api": local_values.get("__name__"), "request": req.model_dump() if hasattr(req, "model_dump") else str(req) if req is not None else None}
    _log_api_error(
        event_type="API_REQUEST_FAILED",
        current_user=current_user if isinstance(current_user, User) else None,
        project_id=str(project_id) if project_id else None,
        step_id=str(step_id) if step_id else None,
        message=message,
        detail=detail,
        exc=exc,
    )



def _ensure_celery_enabled(*, current_user: User | None = None, project_id: str | None = None, step_id: str | None = None) -> None:
    if celery_app is not None:
        return
    message = "Celery未启用：请配置 REDIS_URL 或 CELERY_BROKER_URL 后启动 worker"
    _log_api_error(
        event_type="CELERY_DISABLED",
        current_user=current_user,
        project_id=project_id,
        step_id=step_id,
        message=message,
        detail={"api": "shot_replicate"},
    )
    raise HTTPException(status_code=503, detail=message)

async def _reload_project_detail(db: AsyncSession, current_user: User, project_id: str) -> ShotReplicateTaskDetailOut:
    project = await _get_project_for_user(
        db,
        project_id=project_id,
        user=_user_context(current_user),
        for_update=False,
        populate_existing=True,
    )
    return await project_to_detail_out(db, project)


async def _mark_dispatch_failed_and_raise(
    db: AsyncSession,
    *,
    current_user: User,
    project_id: str,
    step_id: str | None,
    message: str,
) -> None:
    if step_id:
        try:
            await mark_shot_replicate_step_dispatch_failed(
                db,
                current_user=_user_context(current_user),
                project_id=project_id,
                step_id=step_id,
                error_message=message,
            )
            await db.commit()
        except Exception as exc:
            await db.rollback()
            _log_api_error(
                event_type="CELERY_DISPATCH_MARK_FAILED",
                current_user=current_user,
                project_id=project_id,
                step_id=step_id,
                message="Celery 投递失败后标记步骤失败也失败",
                detail={"dispatch_error": message},
                exc=exc,
            )
    log_module_error(
        module=MODULE,
        event_type="CELERY_DISPATCH_FAILED",
        project_id=project_id,
        step_id=step_id,
        user_id=_safe_user_id(current_user),
        message=message,
        detail={"reason": "celery_dispatch_failed"},
        error=message,
    )
    raise HTTPException(status_code=503, detail=message)


@router.get(
    "/spec",
    response_model=ShotReplicateSpecOut,
    summary="查询拆镜复刻模块状态枚举和步骤 JSON 结构说明",
)
async def get_spec():
    return ShotReplicateSpecOut()


@router.post(
    "/task-sets",
    response_model=ShotTaskSetDetailOut,
    summary="创建拆镜总任务集并异步分析原视频",
)
async def create_shot_task_set(
    req: ShotTaskSetCreate = Body(...),
    current_user: User = Depends(get_current_user),
    db: AsyncSession = Depends(get_db),
):
    _ensure_celery_enabled(current_user=current_user, project_id=locals().get("project_id") or locals().get("task_set_id"))
    try:
        task_set = await create_task_set(db, current_user=current_user, req=req)
        task_set_id = task_set.id
        await db.commit()
    except HTTPException:
        await db.rollback()
        raise
    except Exception as exc:
        await db.rollback()
        _log_api_exception_from_locals(exc, locals(), f"创建拆镜总任务集失败: {exc}")
        raise HTTPException(status_code=500, detail=f"创建拆镜总任务集失败: {exc}")

    try:
        from app.tasks.shot_replicate_tasks import analyze_original_video

        await register_shot_task_set_analysis_task(task_set_id)
        analyze_original_video.apply_async(args=[task_set_id], queue="gen_chatapi_create", countdown=0)
    except Exception as exc:
        _log_api_error(
            event_type="CELERY_DISPATCH_FAILED",
            current_user=current_user,
            project_id=task_set_id,
            message=f"拆镜分析任务投递失败: {exc}",
            detail={"task_set_id": task_set_id, "task": "analyze_original_video"},
            exc=exc,
        )
        raise HTTPException(status_code=503, detail=f"拆镜分析任务投递失败: {exc}")

    return await task_set_detail(db, current_user=_user_context(current_user), task_set_id=task_set_id)


@router.get(
    "/task-sets",
    response_model=ShotTaskSetListOut,
    summary="查询拆镜总任务集列表",
)
async def list_shot_task_sets(
    status: str | None = Query(None, description="总任务状态，见 ShotTaskSetStatusEnum"),
    analysis_status: str | None = Query(None, description="分析状态，见 ShotAnalysisStatusEnum"),
    split_status: str | None = Query(None, description="拆镜状态，见 ShotSplitStatusEnum"),
    keyword: str | None = Query(None, description="标题/内容关键词"),
    page: int = Query(1, ge=1),
    page_size: int = Query(20, ge=1, le=100),
    current_user: User = Depends(get_current_user),
    db: AsyncSession = Depends(get_db),
):
    return await list_task_sets(
        db,
        current_user=current_user,
        status=status,
        analysis_status=analysis_status,
        split_status=split_status,
        keyword=keyword,
        page=page,
        page_size=page_size,
    )


@router.get(
    "/task-sets/{task_set_id}",
    response_model=ShotTaskSetDetailOut,
    summary="获取拆镜总任务集详情",
)
async def get_shot_task_set(
    task_set_id: str = Path(...),
    current_user: User = Depends(get_current_user),
    db: AsyncSession = Depends(get_db),
):
    return await task_set_detail(db, current_user=_user_context(current_user), task_set_id=task_set_id)


@router.post(
    "/task-sets/{task_set_id}/split-by-ai",
    response_model=ShotSplitByAIOut,
    summary="按 AI 建议方案异步拆镜",
)
async def split_by_ai(
    task_set_id: str = Path(...),
    req: ShotSplitByAIRequest = Body(default_factory=ShotSplitByAIRequest),
    current_user: User = Depends(get_current_user),
    db: AsyncSession = Depends(get_db),
):
    _ensure_celery_enabled(current_user=current_user, project_id=locals().get("project_id") or locals().get("task_set_id"))
    try:
        out = await create_segments_by_ai(db, current_user=current_user, task_set_id=task_set_id, req=req)
        segment_ids = [item.id for item in out.segments]
        await db.commit()
    except HTTPException:
        await db.rollback()
        raise
    except Exception as exc:
        await db.rollback()
        _log_api_exception_from_locals(exc, locals(), f"按 AI 建议拆镜失败: {exc}")
        raise HTTPException(status_code=500, detail=f"按 AI 建议拆镜失败: {exc}")

    from app.tasks.shot_replicate_tasks import split_one_segment

    for segment_id in segment_ids:
        await register_shot_split_task(segment_id, task_set_id=task_set_id)
        split_one_segment.apply_async(args=[segment_id], queue="gen_result_download", countdown=0)
    return out


@router.post(
    "/task-sets/{task_set_id}/split-custom",
    response_model=ShotSplitCustomOut,
    summary="按用户自定义开始/结束秒异步拆单条片段",
)
async def split_custom(
    task_set_id: str = Path(...),
    req: ShotSplitCustomRequest = Body(...),
    current_user: User = Depends(get_current_user),
    db: AsyncSession = Depends(get_db),
):
    _ensure_celery_enabled(current_user=current_user, project_id=locals().get("project_id") or locals().get("task_set_id"))
    try:
        out = await create_custom_segment(db, current_user=current_user, task_set_id=task_set_id, req=req)
        segment_id = out.segment.id
        await db.commit()
    except HTTPException:
        await db.rollback()
        raise
    except Exception as exc:
        await db.rollback()
        _log_api_exception_from_locals(exc, locals(), f"自定义拆镜失败: {exc}")
        raise HTTPException(status_code=500, detail=f"自定义拆镜失败: {exc}")

    from app.tasks.shot_replicate_tasks import split_one_segment

    await register_shot_split_task(segment_id, task_set_id=task_set_id)
    split_one_segment.apply_async(args=[segment_id], queue="gen_result_download", countdown=0)
    return out


@router.get(
    "/task-sets/{task_set_id}/segments",
    response_model=ShotSegmentListOut,
    summary="查询拆镜片段列表",
)
async def list_task_set_segments(
    task_set_id: str = Path(...),
    source_mode: str | None = Query(None, description="ai_suggestion/custom"),
    split_status: str | None = Query(None, description="拆镜状态"),
    analysis_status: str | None = Query(None, description="片段分析状态"),
    replicate_status: str | None = Query(None, description="复刻状态"),
    page: int = Query(1, ge=1),
    page_size: int = Query(20, ge=1, le=100),
    current_user: User = Depends(get_current_user),
    db: AsyncSession = Depends(get_db),
):
    return await list_segments(
        db,
        current_user=current_user,
        task_set_id=task_set_id,
        source_mode=source_mode,
        split_status=split_status,
        analysis_status=analysis_status,
        replicate_status=replicate_status,
        page=page,
        page_size=page_size,
    )


@router.get(
    "/segments/{segment_id}",
    response_model=ShotSegmentDetailOut,
    summary="获取拆镜片段详情",
)
async def get_segment(
    segment_id: str = Path(...),
    current_user: User = Depends(get_current_user),
    db: AsyncSession = Depends(get_db),
):
    return await segment_detail(db, current_user=current_user, segment_id=segment_id)


@router.post(
    "/segments/{segment_id}/replication-projects",
    response_model=ShotReplicateActionOut,
    summary="将拆镜片段创建为拆镜复刻项目",
)
async def create_replication_project_from_segment(
    segment_id: str = Path(...),
    req: ShotSegmentReplicationCreateRequest = Body(...),
    current_user: User = Depends(get_current_user),
    db: AsyncSession = Depends(get_db),
):
    try:
        segment = await get_segment_for_user(db, segment_id=segment_id, user=current_user, for_update=True)
        project = await create_shot_replicate_project_from_segment(db, current_user=current_user, segment=segment, req=req)
        project_id = project.id
        await db.commit()
    except HTTPException:
        await db.rollback()
        raise
    except Exception as exc:
        await db.rollback()
        _log_api_exception_from_locals(exc, locals(), f"创建拆镜复刻项目失败: {exc}")
        raise HTTPException(status_code=500, detail=f"创建拆镜复刻项目失败: {exc}")

    return ShotReplicateActionOut(
        message="已从拆镜片段创建复刻项目，素材视频已锁定",
        project_id=project_id,
        step_id=None,
        detail=await _reload_project_detail(db, current_user, project_id),
    )


@router.get(
    "/projects/{project_id}",
    response_model=ShotReplicateTaskDetailOut,
    summary="获取拆镜复刻项目详情",
)
async def get_project(
    project_id: str = Path(...),
    current_user: User = Depends(get_current_user),
    db: AsyncSession = Depends(get_db),
):
    return await _reload_project_detail(db, current_user, project_id)


@router.put(
    "/projects/{project_id}/material",
    response_model=ShotReplicateActionOut,
    summary="修改拆镜复刻素材信息，素材视频不允许修改",
)
async def update_material(
    project_id: str = Path(...),
    req: ShotReplicateMaterialUpdateRequest = Body(...),
    current_user: User = Depends(get_current_user),
    db: AsyncSession = Depends(get_db),
):
    try:
        project_id_value, step_id_value = await update_shot_replicate_material_input(db, current_user=current_user, project_id=project_id, req=req)
        await db.commit()
    except HTTPException:
        await db.rollback()
        raise
    except Exception as exc:
        await db.rollback()
        _log_api_exception_from_locals(exc, locals(), f"修改素材输入失败: {exc}")
        raise HTTPException(status_code=500, detail=f"修改素材输入失败: {exc}")
    return ShotReplicateActionOut(message="素材输入已修改，素材视频保持锁定", project_id=project_id_value, step_id=step_id_value, detail=await _reload_project_detail(db, current_user, project_id_value))


@router.put(
    "/projects/{project_id}/steps/{step_id}/image-prompt",
    response_model=ShotReplicateActionOut,
    summary="直接修改图片 AI 优化提词",
)
async def update_image_prompt(
    project_id: str = Path(...),
    step_id: str = Path(...),
    req: ShotReplicateImagePromptUpdateRequest = Body(...),
    current_user: User = Depends(get_current_user),
    db: AsyncSession = Depends(get_db),
):
    try:
        project, step = await update_shot_replicate_image_prompt(db, current_user=current_user, project_id=project_id, step_id=step_id, req=req)
        project_id_value, step_id_value = project.id, step.id
        await db.commit()
    except HTTPException:
        await db.rollback()
        raise
    except Exception as exc:
        await db.rollback()
        _log_api_exception_from_locals(exc, locals(), f"修改图片 AI 提词失败: {exc}")
        raise HTTPException(status_code=500, detail=f"修改图片 AI 提词失败: {exc}")
    return ShotReplicateActionOut(message="图片 AI 提词已修改，后续步骤已软删除", project_id=project_id_value, step_id=step_id_value, detail=await _reload_project_detail(db, current_user, project_id_value))


@router.put(
    "/projects/{project_id}/steps/{step_id}/video-prompt-schema",
    response_model=ShotReplicateActionOut,
    summary="修改视频 AI 提词 JSON schema",
)
async def update_video_prompt_schema(
    project_id: str = Path(...),
    step_id: str = Path(...),
    req: ShotReplicateVideoPromptSchemaUpdateRequest = Body(...),
    current_user: User = Depends(get_current_user),
    db: AsyncSession = Depends(get_db),
):
    try:
        project, step = await update_shot_replicate_video_prompt_schema(db, current_user=current_user, project_id=project_id, step_id=step_id, req=req)
        project_id_value, step_id_value = project.id, step.id
        await db.commit()
    except HTTPException:
        await db.rollback()
        raise
    except Exception as exc:
        await db.rollback()
        _log_api_exception_from_locals(exc, locals(), f"修改视频 AI 提词失败: {exc}")
        raise HTTPException(status_code=500, detail=f"修改视频 AI 提词失败: {exc}")
    return ShotReplicateActionOut(message="视频 AI 提词 schema 已修改，第5步视频生成已软删除", project_id=project_id_value, step_id=step_id_value, detail=await _reload_project_detail(db, current_user, project_id_value))


@router.post(
    "/projects/{project_id}/generate-image-prompt",
    response_model=ShotReplicateActionOut,
    summary="生成图片 AI 提词",
)
async def generate_image_prompt(
    project_id: str = Path(...),
    req: ShotReplicateGenerateImagePromptRequest = Body(default_factory=ShotReplicateGenerateImagePromptRequest),
    current_user: User = Depends(get_current_user),
    db: AsyncSession = Depends(get_db),
):
    _ensure_celery_enabled(current_user=current_user, project_id=locals().get("project_id") or locals().get("task_set_id"))
    try:
        project, step = await submit_image_prompt_optimize(db, current_user=current_user, project_id=project_id, req=req)
        project_id_value, step_id_value = project.id, step.id
        await db.commit()
    except HTTPException:
        await db.rollback()
        raise
    except Exception as exc:
        await db.rollback()
        _log_api_exception_from_locals(exc, locals(), f"提交图片 AI 提词失败: {exc}")
        raise HTTPException(status_code=500, detail=f"提交图片 AI 提词失败: {exc}")

    try:
        from app.tasks.shot_replicate_flow_tasks import start_image_prompt_optimize

        await register_module_step_task(
            module=MODULE,
            project_id=project_id_value,
            step_id=step_id_value,
            step_code=ShotReplicateStepCodeEnum.IMAGE_PROMPT_OPTIMIZE.value,
            task_name=TASK_SHOT_IMAGE_PROMPT,
        )
        start_image_prompt_optimize.apply_async(args=[project_id_value, step_id_value], queue="gen_chatapi_create", countdown=0)
    except Exception as exc:
        await _mark_dispatch_failed_and_raise(db, current_user=current_user, project_id=project_id_value, step_id=step_id_value, message=f"图片 AI 提词任务投递失败: {exc}")

    return ShotReplicateActionOut(message="图片 AI 提词任务已提交", project_id=project_id_value, step_id=step_id_value, detail=await _reload_project_detail(db, current_user, project_id_value))


@router.post(
    "/projects/{project_id}/generate-image",
    response_model=ShotReplicateActionOut,
    summary="根据图片 AI 提词生成图片",
)
async def generate_image(
    project_id: str = Path(...),
    req: ShotReplicateGenerateImageRequest = Body(...),
    current_user: User = Depends(get_current_user),
    db: AsyncSession = Depends(get_db),
):
    _ensure_celery_enabled(current_user=current_user, project_id=locals().get("project_id") or locals().get("task_set_id"))
    try:
        project, step, chat_task = await generate_image_from_prompt(db, current_user=current_user, project_id=project_id, req=req)
        project_id_value, step_id_value, chat_task_id_value = project.id, step.id, chat_task.id
        await db.commit()
    except HTTPException:
        await db.rollback()
        raise
    except Exception as exc:
        await db.rollback()
        _log_api_exception_from_locals(exc, locals(), f"提交图片生成失败: {exc}")
        raise HTTPException(status_code=500, detail=f"提交图片生成失败: {exc}")

    try:
        from app.tasks.generation_create_tasks import chatapi_create_generation_task

        chatapi_create_generation_task.apply_async(args=[chat_task_id_value], queue="gen_chatapi_create", countdown=0)
    except Exception as exc:
        await _mark_dispatch_failed_and_raise(db, current_user=current_user, project_id=project_id_value, step_id=step_id_value, message=f"图片生成任务投递失败: {exc}")

    return ShotReplicateActionOut(message="图片生成任务已提交", project_id=project_id_value, step_id=step_id_value, detail=await _reload_project_detail(db, current_user, project_id_value))


@router.post(
    "/projects/{project_id}/generate-video-prompt",
    response_model=ShotReplicateActionOut,
    summary="生成视频 AI 提词 JSON schema",
)
async def generate_video_prompt(
    project_id: str = Path(...),
    req: ShotReplicateGenerateVideoPromptRequest = Body(...),
    current_user: User = Depends(get_current_user),
    db: AsyncSession = Depends(get_db),
):
    _ensure_celery_enabled(current_user=current_user, project_id=locals().get("project_id") or locals().get("task_set_id"))
    try:
        project, step = await submit_video_prompt_optimize(db, current_user=current_user, project_id=project_id, req=req)
        project_id_value, step_id_value = project.id, step.id
        await db.commit()
    except HTTPException:
        await db.rollback()
        raise
    except Exception as exc:
        await db.rollback()
        _log_api_exception_from_locals(exc, locals(), f"提交视频 AI 提词失败: {exc}")
        raise HTTPException(status_code=500, detail=f"提交视频 AI 提词失败: {exc}")

    try:
        from app.tasks.shot_replicate_flow_tasks import start_video_prompt_optimize

        await register_module_step_task(
            module=MODULE,
            project_id=project_id_value,
            step_id=step_id_value,
            step_code=ShotReplicateStepCodeEnum.VIDEO_PROMPT_OPTIMIZE.value,
            task_name=TASK_SHOT_VIDEO_PROMPT,
        )
        start_video_prompt_optimize.apply_async(args=[project_id_value, step_id_value], queue="gen_chatapi_create", countdown=0)
    except Exception as exc:
        await _mark_dispatch_failed_and_raise(db, current_user=current_user, project_id=project_id_value, step_id=step_id_value, message=f"视频 AI 提词任务投递失败: {exc}")

    return ShotReplicateActionOut(message="视频 AI 提词任务已提交", project_id=project_id_value, step_id=step_id_value, detail=await _reload_project_detail(db, current_user, project_id_value))


@router.post(
    "/projects/{project_id}/generate-video",
    response_model=ShotReplicateActionOut,
    summary="根据视频 AI 提词生成视频",
)
async def generate_video(
    project_id: str = Path(...),
    req: ShotReplicateGenerateVideoRequest = Body(...),
    current_user: User = Depends(get_current_user),
    db: AsyncSession = Depends(get_db),
):
    _ensure_celery_enabled(current_user=current_user, project_id=locals().get("project_id") or locals().get("task_set_id"))
    try:
        project, step, chat_task = await generate_video_from_prompt(db, current_user=current_user, project_id=project_id, req=req)
        project_id_value, step_id_value, chat_task_id_value = project.id, step.id, chat_task.id
        await db.commit()
    except HTTPException:
        await db.rollback()
        raise
    except Exception as exc:
        await db.rollback()
        _log_api_exception_from_locals(exc, locals(), f"提交视频生成失败: {exc}")
        raise HTTPException(status_code=500, detail=f"提交视频生成失败: {exc}")

    try:
        from app.tasks.generation_create_tasks import chatapi_create_generation_task

        chatapi_create_generation_task.apply_async(args=[chat_task_id_value], queue="gen_chatapi_create", countdown=0)
    except Exception as exc:
        await _mark_dispatch_failed_and_raise(db, current_user=current_user, project_id=project_id_value, step_id=step_id_value, message=f"视频生成任务投递失败: {exc}")

    return ShotReplicateActionOut(message="视频生成任务已提交", project_id=project_id_value, step_id=step_id_value, detail=await _reload_project_detail(db, current_user, project_id_value))


@router.delete(
    "/projects/{project_id}",
    response_model=ShotReplicateDeleteOut,
    summary="软删除拆镜复刻项目",
)
async def delete_project(
    project_id: str = Path(...),
    current_user: User = Depends(get_current_user),
    db: AsyncSession = Depends(get_db),
):
    try:
        out = await delete_shot_replicate_project(db, current_user=current_user, project_id=project_id)
        await db.commit()
        return out
    except HTTPException:
        await db.rollback()
        raise
    except Exception as exc:
        await db.rollback()
        _log_api_exception_from_locals(exc, locals(), f"删除拆镜复刻项目失败: {exc}")
        raise HTTPException(status_code=500, detail=f"删除拆镜复刻项目失败: {exc}")
