import random
import uuid
from typing import Any

from app.auth import AuthenticatedUser, get_current_user
from app.core.celery_app import celery_app
from app.dependencies import (
    get_asset_library_repository,
    get_asset_repository,
    get_generated_video_repository,
    get_generation_task_repository,
    get_project_repository,
)
from app.schemas.generated_video import (
    GeneratedVideoResponse,
    ListGeneratedVideosResponse,
)
from app.schemas.generation_task import (
    BatchGenerationTaskResponse,
    ConfirmGenerationRequest,
    CreateGenerationTaskRequest,
    GenerationTaskResponse,
    ListGenerationTasksResponse,
)
from fastapi import APIRouter, Depends, HTTPException

from packages.application import (
    CreateGenerationTaskCommand,
    CreateGenerationTaskUseCase,
    GetGenerationTaskUseCase,
    ListGeneratedVideosByTaskUseCase,
)

router = APIRouter()


def _check_project_access(project_id: str, user_id: str, project_repository) -> None:
    """检查用户是否有项目访问权限"""
    project = project_repository.find_by_id(project_id)
    if project is None:
        raise HTTPException(status_code=404, detail=f"Project {project_id} not found")
    if not project.can_access(user_id):
        raise HTTPException(status_code=403, detail="Access denied to project")


def _to_generation_task_response(task) -> GenerationTaskResponse:
    return GenerationTaskResponse(
        id=task.id,
        project_id=task.project_id,
        asset_library_id=task.asset_library_id,
        strategy_id=task.strategy_id,
        voice_library_id=task.voice_library_id,
        template_id=task.template_id,
        asset_ids=task.asset_ids,
        title_ids=task.title_ids,
        voice_ids=task.voice_ids,
        source_edit_plan_id=task.source_edit_plan_id or "",
        asset_select_mode=getattr(task, "asset_select_mode", ""),
        batch_id=getattr(task, "batch_id", ""),
        is_preview=getattr(task, "is_preview", True),
        source_task_id=getattr(task, "source_task_id", ""),
        output_width=getattr(task, "output_width", 1280),
        output_height=getattr(task, "output_height", 720),
        cover_url=getattr(task, "cover_url", ""),
        custom_title=getattr(task, "custom_title", ""),
        status=task.status,
        progress=task.progress,
        result_count=task.result_count,
        error_message=task.error_message,
    )


def _to_generated_video_response(item) -> GeneratedVideoResponse:
    return GeneratedVideoResponse(
        id=item.id,
        project_id=item.project_id,
        generation_task_id=item.generation_task_id,
        name=item.name,
        file_url=item.file_url,
        file_size=item.file_size,
        duration=item.duration,
        thumbnail_url=item.thumbnail_url,
        width=item.width,
        height=item.height,
        fps=item.fps,
    )


def _ensure_library_has_ready_video_assets(assets) -> None:
    ready_video_assets = [
        asset for asset in assets if asset.status.value == "ready" and asset.mime_type.startswith("video")
    ]
    if not ready_video_assets:
        raise HTTPException(
            status_code=422,
            detail="当前素材库没有 ready 状态的视频素材，请先上传并等待导入完成后再生成。",
        )


def _select_assets_from_library(
    assets: list,
    mode: str,
    count: int,
) -> list[str]:
    """根据选取模式从素材库中选取 ready 状态的视频素材 ID。

    Args:
        assets: 素材库中所有素材（Asset 实体列表）
        mode: 选取模式 — all=全部, random=随机, smart=按质量评分
        count: 选取数量，0 表示全部（仅 random/smart 模式有效）

    Returns:
        选中的素材 ID 列表
    """
    ready_video_assets = [a for a in assets if a.status.value == "ready" and a.mime_type.startswith("video")]

    if not ready_video_assets:
        return []

    if mode == "random":
        selected = (
            ready_video_assets if count <= 0 else random.sample(ready_video_assets, min(count, len(ready_video_assets)))
        )
        return [a.id for a in selected]

    if mode == "smart":
        # 按质量分降序排列（质量分高的优先），质量分相同时按时长降序
        sorted_assets = sorted(
            ready_video_assets,
            key=lambda a: (
                a.quality_score if a.quality_score is not None else 0.0,
                a.duration if a.duration is not None else 0.0,
            ),
            reverse=True,
        )
        selected = sorted_assets if count <= 0 else sorted_assets[:count]
        return [a.id for a in selected]

    # 默认 all 模式：返回全部 ready 视频素材
    return [a.id for a in ready_video_assets]


def _resolve_project_and_library(
    request: CreateGenerationTaskRequest,
    project_repository: Any,
    asset_library_repository: Any,
    asset_repository: Any,
    authenticated_user: AuthenticatedUser,
) -> tuple[str, str]:
    """解析 project_id 和 asset_library_id。

    支持两种模式：
    - 显式传入（向后兼容）
    - 从 asset_ids 反查 asset_library（模板模式）
    返回 (project_id, asset_library_id)。
    """
    project_id = request.project_id.strip()
    asset_library_id = request.asset_library_id.strip()

    # 模板模式：project_id 未提供时，从 asset_ids 反查所属 project
    if not project_id and request.asset_ids:
        first_asset_id = request.asset_ids[0]
        asset = asset_repository.find_by_id(first_asset_id)
        if asset is not None:
            project_id = asset.project_id
            if not asset_library_id:
                asset_library_id = asset.library_id

    # 向后兼容校验：project_id 已提供时验证权限
    if project_id:
        project = project_repository.find_by_id(project_id)
        if project is None:
            raise HTTPException(status_code=404, detail=f"Project {project_id} not found")
        if not project.can_access(authenticated_user.user.id):
            raise HTTPException(status_code=403, detail="Access denied to project")

    return project_id, asset_library_id


@router.post("/tasks", response_model=BatchGenerationTaskResponse)
def create_generation_task(
    request: CreateGenerationTaskRequest,
    authenticated_user: AuthenticatedUser = Depends(get_current_user),
    generation_task_repository: Any = Depends(get_generation_task_repository),
    project_repository: Any = Depends(get_project_repository),
    asset_library_repository: Any = Depends(get_asset_library_repository),
    asset_repository: Any = Depends(get_asset_repository),
) -> BatchGenerationTaskResponse:
    project_id, asset_library_id = _resolve_project_and_library(
        request, project_repository, asset_library_repository, asset_repository, authenticated_user
    )

    # asset_library 存在性校验（仅在提供了 asset_library_id 时）
    resolved_asset_ids: list[str] = list(request.asset_ids)
    if asset_library_id:
        library = asset_library_repository.get(asset_library_id)
        if library is None or (project_id and library.project_id != project_id):
            raise HTTPException(status_code=404, detail=f"AssetLibrary {asset_library_id} not found")

        assets = asset_repository.find_by_library(asset_library_id)
        _ensure_library_has_ready_video_assets(assets)

        # 素材库自动匹配：当未显式指定 asset_ids 时，按模式自动选取
        if not resolved_asset_ids:
            resolved_asset_ids = _select_assets_from_library(
                assets,
                mode=request.asset_select_mode,
                count=request.asset_select_count,
            )

    use_case = CreateGenerationTaskUseCase(generation_task_repository)
    count = request.count
    created_tasks = []
    # 同批次任务共享 batch_id，用于视频查重时批次内比对
    batch_id = uuid.uuid4().hex if count > 1 else ""

    for _ in range(count):
        task = use_case.execute(
            CreateGenerationTaskCommand(
                project_id=project_id,
                asset_library_id=asset_library_id,
                strategy_id=request.strategy_id,
                voice_library_id=request.voice_library_id,
                template_id=request.template_id,
                asset_ids=resolved_asset_ids,
                title_ids=request.title_ids,
                voice_ids=request.voice_ids,
                created_by_user_id=authenticated_user.user.id,
                source_edit_plan_id=request.source_edit_plan_id,
                asset_select_mode=request.asset_select_mode,
                batch_id=batch_id,
                is_preview=request.is_preview,
                source_task_id=request.source_task_id,
                output_width=request.output_width,
                output_height=request.output_height,
                cover_url=request.cover_url,
                custom_title=request.custom_title,
            )
        )
        celery_app.send_task("worker.generate_video", args=[task.id])
        created_tasks.append(task)

    items = [_to_generation_task_response(t) for t in created_tasks]
    return BatchGenerationTaskResponse(items=items, total=len(items))


@router.post("/tasks/{task_id}/confirm", response_model=BatchGenerationTaskResponse)
def confirm_generation(
    task_id: str,
    request: ConfirmGenerationRequest,
    authenticated_user: AuthenticatedUser = Depends(get_current_user),
    generation_task_repository: Any = Depends(get_generation_task_repository),
    project_repository: Any = Depends(get_project_repository),
) -> BatchGenerationTaskResponse:
    """确认生成 — 基于预览任务创建正式生成任务。

    查找预览任务，复制其配置，创建新的正式生成任务（is_preview=False），
    使用高分辨率，复用 worker.generate_video 渲染路径。
    """
    # 1. 查找源预览任务
    source_task = generation_task_repository.get(task_id)
    if source_task is None:
        raise HTTPException(status_code=404, detail=f"Preview task {task_id} not found")

    # 2. 权限检查
    if source_task.created_by_user_id and source_task.created_by_user_id != authenticated_user.user.id:
        raise HTTPException(status_code=403, detail="Access denied to this task")
    if source_task.project_id:
        _check_project_access(source_task.project_id, authenticated_user.user.id, project_repository)

    # 3. 创建正式生成任务，复制预览任务的配置
    use_case = CreateGenerationTaskUseCase(generation_task_repository)
    new_task = use_case.execute(
        CreateGenerationTaskCommand(
            project_id=source_task.project_id,
            asset_library_id=source_task.asset_library_id,
            strategy_id=source_task.strategy_id,
            voice_library_id=source_task.voice_library_id,
            template_id=source_task.template_id,
            asset_ids=source_task.asset_ids,
            title_ids=source_task.title_ids,
            voice_ids=source_task.voice_ids,
            created_by_user_id=authenticated_user.user.id,
            source_edit_plan_id=source_task.source_edit_plan_id or "",
            asset_select_mode=source_task.asset_select_mode,
            is_preview=False,
            source_task_id=task_id,
            output_width=request.output_width,
            output_height=request.output_height,
            cover_url=request.cover_url,
            custom_title=request.custom_title,
        )
    )

    # 4. 调度 worker.generate_video（同一条渲染路径）
    celery_app.send_task("worker.generate_video", args=[new_task.id])

    return BatchGenerationTaskResponse(
        items=[_to_generation_task_response(new_task)],
        total=1,
    )


@router.get("/tasks", response_model=ListGenerationTasksResponse)
def list_generation_tasks(
    authenticated_user: AuthenticatedUser = Depends(get_current_user),
    generation_task_repository: Any = Depends(get_generation_task_repository),
) -> ListGenerationTasksResponse:
    """用户级生成任务列表（跨 project）。"""
    tasks = generation_task_repository.list_by_user(authenticated_user.user.id)
    items = [_to_generation_task_response(task) for task in tasks]
    return ListGenerationTasksResponse(items=items)


@router.get("/tasks/{task_id}", response_model=GenerationTaskResponse)
def get_generation_task(
    task_id: str,
    authenticated_user: AuthenticatedUser = Depends(get_current_user),
    generation_task_repository: Any = Depends(get_generation_task_repository),
    project_repository: Any = Depends(get_project_repository),
) -> GenerationTaskResponse:
    use_case = GetGenerationTaskUseCase(generation_task_repository)
    task = use_case.execute(task_id)
    if task is None:
        raise HTTPException(status_code=404, detail=f"GenerationTask {task_id} not found")
    if task.project_id:
        _check_project_access(task.project_id, authenticated_user.user.id, project_repository)
    return _to_generation_task_response(task)


@router.get("/tasks/{task_id}/results", response_model=ListGeneratedVideosResponse)
def list_generation_results(
    task_id: str,
    authenticated_user: AuthenticatedUser = Depends(get_current_user),
    generation_task_repository: Any = Depends(get_generation_task_repository),
    generated_video_repository: Any = Depends(get_generated_video_repository),
    project_repository: Any = Depends(get_project_repository),
) -> ListGeneratedVideosResponse:
    task = generation_task_repository.get(task_id)
    if task is None:
        raise HTTPException(status_code=404, detail=f"GenerationTask {task_id} not found")
    if task.project_id:
        _check_project_access(task.project_id, authenticated_user.user.id, project_repository)
    use_case = ListGeneratedVideosByTaskUseCase(generated_video_repository)
    items = use_case.execute(task_id)
    return ListGeneratedVideosResponse(items=[_to_generated_video_response(item) for item in items])


@router.post("/tasks/{task_id}/retry", response_model=GenerationTaskResponse)
def retry_generation_task(
    task_id: str,
    authenticated_user: AuthenticatedUser = Depends(get_current_user),
    generation_task_repository: Any = Depends(get_generation_task_repository),
) -> GenerationTaskResponse:
    """简化重试：通过 task_id 直接重试失败任务。"""
    task = generation_task_repository.get(task_id)
    if task is None:
        raise HTTPException(status_code=404, detail="Generation task not found")
    if task.created_by_user_id and task.created_by_user_id != authenticated_user.user.id:
        raise HTTPException(status_code=403, detail="Access denied to this task")
    status_val = task.status.value if hasattr(task.status, "value") else str(task.status)
    if status_val != "failed":
        raise HTTPException(status_code=409, detail="Only failed tasks can be retried")

    use_case = CreateGenerationTaskUseCase(generation_task_repository)
    retried = use_case.execute(
        CreateGenerationTaskCommand(
            project_id=task.project_id,
            asset_library_id=task.asset_library_id,
            strategy_id=task.strategy_id,
            voice_library_id=task.voice_library_id,
            template_id=task.template_id,
            asset_ids=task.asset_ids,
            title_ids=task.title_ids,
            voice_ids=task.voice_ids,
            created_by_user_id=authenticated_user.user.id,
            source_edit_plan_id=task.source_edit_plan_id or "",
            asset_select_mode=getattr(task, "asset_select_mode", ""),
            is_preview=getattr(task, "is_preview", True),
            source_task_id=getattr(task, "source_task_id", ""),
            output_width=getattr(task, "output_width", 1280),
            output_height=getattr(task, "output_height", 720),
            cover_url=getattr(task, "cover_url", ""),
            custom_title=getattr(task, "custom_title", ""),
        )
    )
    celery_app.send_task("worker.generate_video", args=[retried.id])
    return _to_generation_task_response(retried)
