"""音色克隆 API 路由。"""

from __future__ import annotations

import logging
from typing import Optional

from app.auth import AuthenticatedUser, get_current_user
from app.dependencies import get_cosyvoice_service, get_voice_clone_profile_repository
from app.schemas.voice_clone import (
    CreateVoiceCloneRequest,
    ListVoiceCloneResponse,
    VoiceCloneProfileResponse,
    VoiceCloneStatusResponse,
)
from fastapi import APIRouter, Depends, HTTPException, Query, Response, status

from packages.adapters.sqlalchemy_impl.voice_clone_profile_repository import (
    SQLAlchemyVoiceCloneProfileRepository,
)
from packages.application.cosyvoice_service import CosyVoiceService
from packages.application.voice_clone.use_cases import (
    DeleteVoiceCloneUseCase,
    GetVoiceCloneStatusUseCase,
    GetVoiceCloneUseCase,
    ListVoiceClonesUseCase,
    VoiceCloneNotFoundError,
    VoiceCloneNotRetryableError,
)
from packages.application.voice_clone.workflow import (
    VoiceCloneWorkflowError,
    VoiceCloneWorkflowService,
)

logger = logging.getLogger(__name__)

router = APIRouter()


def _to_response(profile) -> VoiceCloneProfileResponse:
    return VoiceCloneProfileResponse(
        id=profile.id,
        user_id=profile.user_id,
        name=profile.name,
        description=profile.description,
        source_audio_url=profile.source_audio_url,
        voice_id=profile.voice_id,
        voice_model=profile.voice_model,
        language=profile.language,
        gender=profile.gender,
        status=profile.status,
        error_message=profile.error_message,
        retry_count=profile.retry_count,
        max_retries=profile.max_retries,
        metadata=profile.metadata,
        created_at=profile.created_at,
        updated_at=profile.updated_at,
    )


def _get_workflow_service(
    repository: SQLAlchemyVoiceCloneProfileRepository = Depends(get_voice_clone_profile_repository),
    cosyvoice_service: CosyVoiceService = Depends(get_cosyvoice_service),
) -> VoiceCloneWorkflowService:
    return VoiceCloneWorkflowService(repository=repository, cosyvoice_service=cosyvoice_service)


@router.post(
    "",
    response_model=VoiceCloneProfileResponse,
    status_code=status.HTTP_201_CREATED,
)
def create_voice_clone(
    request: CreateVoiceCloneRequest,
    authenticated_user: AuthenticatedUser = Depends(get_current_user),
    workflow: VoiceCloneWorkflowService = Depends(_get_workflow_service),
) -> VoiceCloneProfileResponse:
    """创建音色克隆任务。

    创建 VoiceCloneProfile → 提交 CosyVoice 克隆任务 → 触发 Celery 异步轮询。
    如果有 source_audio_url，状态会变为 processing；否则保持 pending。
    """
    user_id = authenticated_user.user.id
    profile = workflow.start_clone(
        user_id=user_id,
        name=request.name,
        description=request.description,
        source_audio_url=request.source_audio_url,
        voice_model=request.voice_model,
        language=request.language,
        gender=request.gender,
        max_retries=request.max_retries,
        metadata=request.metadata_,
    )

    # 如果 profile 处于 processing 且有 task_id，触发 Celery 异步轮询
    task_id = (profile.metadata or {}).get("cosyvoice_task_id", "")
    if profile.status == "processing" and task_id:
        try:
            from worker_app.tasks import process_voice_clone

            process_voice_clone.delay(profile.id)
            logger.info(f"Celery task dispatched for voice clone {profile.id}")
        except Exception as e:
            logger.error(f"Failed to dispatch Celery task: {e}")
            # P2-3: Celery 调度失败时标记 profile 为 failed，避免永久卡在 processing
            try:
                workflow.process_clone_failure(profile.id, f"Celery 任务调度失败: {e}")
            except Exception as inner_e:
                logger.error(f"Failed to mark profile as failed after dispatch error: {inner_e}")

    return _to_response(profile)


@router.get("", response_model=ListVoiceCloneResponse)
def list_voice_clones(
    status_filter: Optional[str] = Query(None, alias="status"),
    skip: int = Query(0, ge=0),
    limit: int = Query(50, ge=1, le=200),
    authenticated_user: AuthenticatedUser = Depends(get_current_user),
    repository: SQLAlchemyVoiceCloneProfileRepository = Depends(get_voice_clone_profile_repository),
) -> ListVoiceCloneResponse:
    """获取用户的音色克隆列表。"""
    user_id = authenticated_user.user.id
    use_case = ListVoiceClonesUseCase(repository)
    items, total = use_case.execute(user_id, status=status_filter, skip=skip, limit=limit)
    return ListVoiceCloneResponse(
        items=[_to_response(p) for p in items],
        total=total,
    )


@router.get("/{clone_id}", response_model=VoiceCloneProfileResponse)
def get_voice_clone(
    clone_id: str,
    authenticated_user: AuthenticatedUser = Depends(get_current_user),
    repository: SQLAlchemyVoiceCloneProfileRepository = Depends(get_voice_clone_profile_repository),
) -> VoiceCloneProfileResponse:
    """获取音色克隆详情。"""
    user_id = authenticated_user.user.id
    use_case = GetVoiceCloneUseCase(repository)
    try:
        profile = use_case.execute(clone_id, user_id)
    except VoiceCloneNotFoundError:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Voice clone not found")
    return _to_response(profile)


@router.get("/{clone_id}/status", response_model=VoiceCloneStatusResponse)
def get_voice_clone_status(
    clone_id: str,
    authenticated_user: AuthenticatedUser = Depends(get_current_user),
    repository: SQLAlchemyVoiceCloneProfileRepository = Depends(get_voice_clone_profile_repository),
) -> VoiceCloneStatusResponse:
    """查询音色克隆状态（用于前端轮询）。"""
    user_id = authenticated_user.user.id
    use_case = GetVoiceCloneStatusUseCase(repository)
    try:
        profile = use_case.execute(clone_id, user_id)
    except VoiceCloneNotFoundError:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Voice clone not found")
    return VoiceCloneStatusResponse(
        id=profile.id,
        status=profile.status,
        error_message=profile.error_message,
        voice_id=profile.voice_id,
        retry_count=profile.retry_count,
    )


@router.delete(
    "/{clone_id}",
    status_code=status.HTTP_204_NO_CONTENT,
    response_model=None,
)
def delete_voice_clone(
    clone_id: str,
    authenticated_user: AuthenticatedUser = Depends(get_current_user),
    repository: SQLAlchemyVoiceCloneProfileRepository = Depends(get_voice_clone_profile_repository),
) -> Response:
    """删除音色克隆档案。"""
    user_id = authenticated_user.user.id
    use_case = DeleteVoiceCloneUseCase(repository)
    deleted = use_case.execute(clone_id, user_id)
    if not deleted:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Voice clone not found")
    return Response(status_code=204)


@router.post("/{clone_id}/retry", response_model=VoiceCloneProfileResponse)
def retry_voice_clone(
    clone_id: str,
    authenticated_user: AuthenticatedUser = Depends(get_current_user),
    workflow: VoiceCloneWorkflowService = Depends(_get_workflow_service),
) -> VoiceCloneProfileResponse:
    """重试失败的音色克隆。

    仅当状态为 failed 时可重试，重试后重新提交 CosyVoice 克隆任务。
    """
    user_id = authenticated_user.user.id
    try:
        profile = workflow.retry_clone(clone_id, user_id)
    except VoiceCloneNotFoundError:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Voice clone not found")
    except VoiceCloneNotRetryableError:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="Voice clone is not retryable (only failed clones can be retried)",
        )

    # 如果 profile 处于 processing 且有 task_id，触发 Celery 异步轮询
    task_id = (profile.metadata or {}).get("cosyvoice_task_id", "")
    if profile.status == "processing" and task_id:
        try:
            from worker_app.tasks import process_voice_clone

            process_voice_clone.delay(profile.id)
            logger.info(f"Celery task dispatched for voice clone retry {profile.id}")
        except Exception as e:
            logger.error(f"Failed to dispatch Celery task: {e}")
            # P2-3: Celery 调度失败时标记 profile 为 failed，避免永久卡在 processing
            try:
                workflow.process_clone_failure(profile.id, f"Celery 任务调度失败: {e}")
            except Exception as inner_e:
                logger.error(f"Failed to mark profile as failed after dispatch error: {inner_e}")

    return _to_response(profile)
