"""Voice library CRUD routes — Phase 3 增强版.

支持预置音色和克隆音色的统一列表。
"""

from __future__ import annotations

from typing import Literal, Optional

from app.auth import AuthenticatedUser, get_current_user
from app.dependencies import get_db_session, get_user_repository
from app.schemas.voice import (
    PresetVoiceItemResponse,
    PresetVoiceListResponse,
    UnifiedVoiceItemResponse,
    UnifiedVoiceListResponse,
)
from app.schemas.voice_library import (
    CreateVoiceLibraryRequest,
    ListVoiceLibraryResponse,
    UpdateVoiceLibraryRequest,
    VoiceLibraryItemResponse,
)
from fastapi import APIRouter, Depends, HTTPException, Query, Response, status
from sqlalchemy.orm import Session

from packages.adapters.sqlalchemy_impl.voice_clone_profile_repository import SQLAlchemyVoiceCloneProfileRepository
from packages.adapters.sqlalchemy_impl.voice_library_repository import SQLAlchemyVoiceLibraryRepository
from packages.application.voice_library.commands import CreateVoiceLibraryCommand, UpdateVoiceLibraryCommand
from packages.application.voice_library.use_cases import (
    CreateVoiceLibraryUseCase,
    DeleteVoiceLibraryUseCase,
    GetVoiceLibraryUseCase,
    ListVoiceLibraryUseCase,
    NotFoundError,
    QuotaExceededError,
    UpdateVoiceLibraryUseCase,
)
from packages.domain.preset_voices import PRESET_VOICES
from packages.ports.user_repository import UserRepository

router = APIRouter()


def _get_voice_repository(session: Session = Depends(get_db_session)) -> SQLAlchemyVoiceLibraryRepository:
    return SQLAlchemyVoiceLibraryRepository(session)


def _get_clone_profile_repository(session: Session = Depends(get_db_session)) -> SQLAlchemyVoiceCloneProfileRepository:
    return SQLAlchemyVoiceCloneProfileRepository(session)


def _to_response(item) -> VoiceLibraryItemResponse:
    return VoiceLibraryItemResponse(
        id=item.id,
        user_id=item.user_id,
        name=item.name,
        text=item.text,
        voice_provider=item.voice_provider,
        voice_id=item.voice_id,
        voice_name=item.voice_name,
        audio_url=item.audio_url,
        duration=item.duration,
        file_size=item.file_size,
        status=item.status,
        project_id=item.project_id,
        tags=item.tags,
        created_at=item.created_at,
        updated_at=item.updated_at,
    )


def _to_unified_response(item, profile_id_map: dict | None = None) -> UnifiedVoiceItemResponse:
    """将数据库音色转换为统一响应格式。

    Args:
        item: VoiceLibraryItem
        profile_id_map: voice_id → profile_id 映射，用于填充 voice_clone_profile_id
    """
    profile_id = None
    if profile_id_map and item.voice_id:
        profile_id = profile_id_map.get(item.voice_id)
    return UnifiedVoiceItemResponse(
        id=item.id,
        type="clone",
        name=item.name,
        description=item.text,
        gender="unknown",
        language="zh-CN",
        voice_id=item.voice_id,
        voice_provider=item.voice_provider or "cosyvoice",
        audio_url=item.audio_url,
        duration=item.duration,
        file_size=item.file_size,
        status=item.status,
        tags=item.tags,
        user_id=item.user_id,
        project_id=item.project_id,
        voice_clone_profile_id=profile_id,
        created_at=item.created_at,
        updated_at=item.updated_at,
    )


def _preset_to_unified_response(preset) -> UnifiedVoiceItemResponse:
    """将预置音色转换为统一响应格式。"""
    return UnifiedVoiceItemResponse(
        id=preset.voice_id,
        type="preset",
        name=preset.name,
        description=preset.description,
        gender=preset.gender,
        language=preset.language,
        voice_id=preset.voice_id,
        voice_provider="cosyvoice",
        preview_url=preset.preview_url,
        tags=preset.tags or [],
    )


def _get_user_plan(user_id: str, user_repository: UserRepository) -> str:
    user = user_repository.find_by_id(user_id)
    if user is None:
        return "free"
    return getattr(user, "subscription_plan", "free") or "free"


# ==================== 统一配音列表（预置 + 克隆）====================


@router.get("", response_model=UnifiedVoiceListResponse)
def list_voices_unified(
    type: Optional[Literal["preset", "clone"]] = Query(
        None,
        description="音色类型过滤：preset=仅预置，clone=仅克隆，不传=全部",
    ),
    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),
    voice_repository: SQLAlchemyVoiceLibraryRepository = Depends(_get_voice_repository),
    clone_profile_repository: SQLAlchemyVoiceCloneProfileRepository = Depends(_get_clone_profile_repository),
) -> UnifiedVoiceListResponse:
    """获取配音列表（预置音色 + 用户克隆音色）。

    - 不传 type：返回预置音色 + 用户克隆音色，预置音色在前
    - type=preset：仅返回预置音色
    - type=clone：仅返回用户克隆音色
    """
    user_id = authenticated_user.user.id
    items: list[UnifiedVoiceItemResponse] = []
    preset_count = 0
    clone_count = 0

    has_preset = type is None or type == "preset"
    has_clone = type is None or type == "clone"

    # 获取预置音色
    if has_preset:
        preset_items = [_preset_to_unified_response(p) for p in PRESET_VOICES]
        preset_count = len(preset_items)

    # 获取克隆音色
    if has_clone:
        use_case = ListVoiceLibraryUseCase(voice_repository)
        clone_items_raw, clone_count = use_case.execute(user_id, status=status_filter, skip=skip, limit=limit)
        # 批量查询 voice_id → profile_id 映射，填充 voice_clone_profile_id
        voice_ids = [i.voice_id for i in clone_items_raw if i.voice_id]
        profile_id_map = clone_profile_repository.find_profile_ids_by_voice_ids(voice_ids) if voice_ids else {}
        clone_items = [_to_unified_response(i, profile_id_map) for i in clone_items_raw]

    # 组装结果
    if type == "preset":
        items = preset_items[skip : skip + limit]
        total = preset_count
    elif type == "clone":
        items = clone_items
        total = clone_count
    else:
        # 全量模式：预置在前，克隆补位
        all_items = preset_items + clone_items
        total = preset_count + clone_count
        items = all_items[skip : skip + limit]

    return UnifiedVoiceListResponse(
        items=items,
        total=total,
        preset_count=preset_count if has_preset else 0,
        clone_count=clone_count if has_clone else 0,
    )


# ==================== 预置音色专用端点 ====================


@router.get("/presets", response_model=PresetVoiceListResponse)
def list_preset_voices() -> PresetVoiceListResponse:
    """获取预置音色列表。

    不需要认证，返回所有系统预置的 CosyVoice 音色。
    """
    items = [
        PresetVoiceItemResponse(
            voice_id=p.voice_id,
            name=p.name,
            description=p.description,
            gender=p.gender,
            language=p.language,
            preview_url=p.preview_url,
            tags=p.tags or [],
        )
        for p in PRESET_VOICES
    ]
    return PresetVoiceListResponse(items=items, total=len(items))


# ==================== 原有 CRUD 端点（保持向后兼容）====================


@router.get("/legacy", response_model=ListVoiceLibraryResponse)
def list_voices_legacy(
    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),
    voice_repository: SQLAlchemyVoiceLibraryRepository = Depends(_get_voice_repository),
) -> ListVoiceLibraryResponse:
    """原有配音列表接口（仅返回用户克隆音色）。

    保留用于向后兼容，新客户端请使用 GET /api/v1/voices。
    """
    user_id = authenticated_user.user.id
    use_case = ListVoiceLibraryUseCase(voice_repository)
    items, total = use_case.execute(user_id, status=status_filter, skip=skip, limit=limit)
    return ListVoiceLibraryResponse(
        items=[_to_response(i) for i in items],
        total=total,
    )


@router.get("/{voice_id}", response_model=VoiceLibraryItemResponse)
def get_voice(
    voice_id: str,
    authenticated_user: AuthenticatedUser = Depends(get_current_user),
    voice_repository: SQLAlchemyVoiceLibraryRepository = Depends(_get_voice_repository),
) -> VoiceLibraryItemResponse:
    user_id = authenticated_user.user.id
    use_case = GetVoiceLibraryUseCase(voice_repository)
    item = use_case.execute(voice_id, user_id)
    if item is None:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Voice not found")
    return _to_response(item)


@router.post("", response_model=VoiceLibraryItemResponse, status_code=status.HTTP_201_CREATED)
def create_voice(
    request: CreateVoiceLibraryRequest,
    authenticated_user: AuthenticatedUser = Depends(get_current_user),
    voice_repository: SQLAlchemyVoiceLibraryRepository = Depends(_get_voice_repository),
    user_repository: UserRepository = Depends(get_user_repository),
) -> VoiceLibraryItemResponse:
    user_id = authenticated_user.user.id
    plan_name = _get_user_plan(user_id, user_repository)
    command = CreateVoiceLibraryCommand(
        user_id=user_id,
        name=request.name,
        text=request.text,
        voice_provider=request.voice_provider,
        voice_id=request.voice_id,
        voice_name=request.voice_name,
        audio_url=request.audio_url,
        duration=request.duration,
        file_size=request.file_size,
        status=request.status,
        project_id=request.project_id,
        tags=request.tags,
    )
    use_case = CreateVoiceLibraryUseCase(voice_repository)
    try:
        item = use_case.execute(command, plan_name=plan_name)
    except QuotaExceededError as exc:
        raise HTTPException(
            status_code=status.HTTP_429_TOO_MANY_REQUESTS,
            detail=f"配音库配额已满（{exc.used}/{exc.limit}），请升级套餐",
        )
    return _to_response(item)


@router.put("/{voice_id}", response_model=VoiceLibraryItemResponse)
def update_voice(
    voice_id: str,
    request: UpdateVoiceLibraryRequest,
    authenticated_user: AuthenticatedUser = Depends(get_current_user),
    voice_repository: SQLAlchemyVoiceLibraryRepository = Depends(_get_voice_repository),
) -> VoiceLibraryItemResponse:
    user_id = authenticated_user.user.id
    command = UpdateVoiceLibraryCommand(
        id=voice_id,
        user_id=user_id,
        name=request.name,
        text=request.text,
        voice_provider=request.voice_provider,
        voice_id=request.voice_id,
        voice_name=request.voice_name,
        audio_url=request.audio_url,
        duration=request.duration,
        file_size=request.file_size,
        status=request.status,
        tags=request.tags,
    )
    use_case = UpdateVoiceLibraryUseCase(voice_repository)
    try:
        item = use_case.execute(command)
    except NotFoundError:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Voice not found")
    return _to_response(item)


@router.delete("/{voice_id}", status_code=status.HTTP_204_NO_CONTENT, response_model=None)
def delete_voice(
    voice_id: str,
    authenticated_user: AuthenticatedUser = Depends(get_current_user),
    voice_repository: SQLAlchemyVoiceLibraryRepository = Depends(_get_voice_repository),
) -> Response:
    user_id = authenticated_user.user.id
    use_case = DeleteVoiceLibraryUseCase(voice_repository)
    deleted = use_case.execute(voice_id, user_id)
    if not deleted:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Voice not found")
    return Response(status_code=204)
