"""
Projects API - Assets Module

包含资产管理相关的 API 路由。
"""

import logging
import uuid
from typing import Optional, List

from fastapi import APIRouter, Depends, Query

from pydantic import TypeAdapter

from src.models.schemas import (
    ResponseModel,
    Asset, CreateAssetRequest, UpdateAssetRequest,
    CharacterAsset, SceneAsset, PropAsset, OtherAsset,
    GenerationRecord,
)
from src.auth.dependencies import get_current_user
from src.auth.models import UserAuth
from src.services.project_service import project_manager
from src.services.asset_deduplication_service import deduplicate_project_assets as deduplicate_assets_service
from src.utils.errors import ErrorCode, AppException

logger = logging.getLogger(__name__)

router = APIRouter(tags=["projects-assets"])


def _check_project_access(project_id: str, user_id: str):
    """检查用户是否有权限访问项目"""
    project = project_manager.get_project(project_id, user_id=user_id)
    return project


# --- Asset Management ---

@router.post("/projects/{project_id}/assets", response_model=ResponseModel)
async def create_asset(
    project_id: str,
    request: dict,
    current_user: UserAuth = Depends(get_current_user)
):
    """
    Create a new asset.
    Accepts a dictionary, validates based on 'type' field.
    """
    try:
        # Check project access
        if not _check_project_access(project_id, current_user.id):
            raise AppException(
                message="Project not found or access denied",
                code=ErrorCode.NOT_FOUND,
                status_code=404
            )

        asset_type = request.get('type', 'other')
        if 'id' not in request:
            request['id'] = str(uuid.uuid4())

        asset = None
        if asset_type == 'character':
            asset = TypeAdapter(CharacterAsset).validate_python(request)
        elif asset_type == 'scene':
            asset = TypeAdapter(SceneAsset).validate_python(request)
        elif asset_type == 'prop':
             asset = TypeAdapter(PropAsset).validate_python(request)
        else:
             asset = TypeAdapter(OtherAsset).validate_python(request)

        updated_project = project_manager.add_asset(project_id, asset)
        if not updated_project:
             raise AppException(
                message="Project not found",
                code=ErrorCode.NOT_FOUND,
                status_code=404
            )

        return ResponseModel(data=updated_project)
    except Exception as e:
        raise AppException(
                message=str(e),
                code=ErrorCode.INTERNAL_ERROR,
                status_code=500
            )

@router.put("/projects/{project_id}/assets/{asset_id}", response_model=ResponseModel)
async def update_asset(project_id: str, asset_id: str, request: UpdateAssetRequest):
    """Update an asset"""
    try:
        project = project_manager.get_project(project_id)
        if not project:
            raise AppException(
                message="Project not found",
                code=ErrorCode.NOT_FOUND,
                status_code=404
            )

        existing_asset = next((a for a in project.assets if a.id == asset_id), None)
        if not existing_asset:
             raise AppException(
                message="Asset not found",
                code=ErrorCode.NOT_FOUND,
                status_code=404
            )

        # Merge updates
        update_data = request.model_dump(exclude_unset=True)
        updated_asset = existing_asset.model_copy(update=update_data)

        result = project_manager.update_asset(project_id, asset_id, updated_asset)
        return ResponseModel(data=result)
    except Exception as e:
        raise AppException(
                message=str(e),
                code=ErrorCode.INTERNAL_ERROR,
                status_code=500
            )

@router.delete("/projects/{project_id}/assets/{asset_id}", response_model=ResponseModel)
async def delete_asset(project_id: str, asset_id: str):
    """Delete an asset"""
    try:
        result = project_manager.delete_asset(project_id, asset_id)
        if not result:
             raise AppException(
                message="Project not found",
                code=ErrorCode.NOT_FOUND,
                status_code=404
            )
        return ResponseModel(data=result)
    except Exception as e:
        raise AppException(
                message=str(e),
                code=ErrorCode.INTERNAL_ERROR,
                status_code=500
            )


@router.post("/projects/{project_id}/assets/deduplicate", response_model=ResponseModel)
async def deduplicate_project_assets(
    project_id: str,
    current_user: UserAuth = Depends(get_current_user)
):
    """
    Deduplicate all assets in the project using LLM; update storyboard references.
    """
    try:
        updated_project = await deduplicate_assets_service(project_id, current_user.id)
        return ResponseModel(data=updated_project)
    except ValueError as e:
        if "not found" in str(e).lower():
            raise AppException(
                message=str(e),
                code=ErrorCode.NOT_FOUND,
                status_code=404
            )
        raise AppException(
                message=str(e),
                code=ErrorCode.INTERNAL_ERROR,
                status_code=500
            )
    except Exception as e:
        logger.error("Asset deduplication failed: %s", e, exc_info=True)
        raise AppException(
                message=str(e),
                code=ErrorCode.INTERNAL_ERROR,
                status_code=500
            )

# --- Asset Management ---
@router.get("/projects/{project_id}/assets", response_model=ResponseModel)
async def list_project_assets(
    project_id: str,
    asset_type: Optional[str] = Query(None, description="Filter by asset type"),
    search_query: Optional[str] = Query(None, description="Search by name or description"),
    limit: int = 50,
    offset: int = 0
):
    """List assets for a project with optional filtering and pagination"""
    try:
        result = project_manager.list_assets(project_id, asset_type, search_query, limit, offset)
        if result is None:
             raise AppException(
                message="Project not found",
                code=ErrorCode.NOT_FOUND,
                status_code=404
            )
        return ResponseModel(data=result)
    except Exception as e:
        raise AppException(
                message=str(e),
                code=ErrorCode.INTERNAL_ERROR,
                status_code=500
            )

@router.post("/projects/{project_id}/assets", response_model=ResponseModel)
async def create_asset(project_id: str, request: CreateAssetRequest):
    """Create a new asset"""
    try:
        asset_data = request.model_dump(by_alias=True)
        asset_data['id'] = str(uuid.uuid4())

        # Validate and convert to Asset Union
        new_asset = TypeAdapter(Asset).validate_python(asset_data)

        updated_project = project_manager.add_asset(project_id, new_asset)
        if not updated_project:
            raise AppException(
                message="Project not found",
                code=ErrorCode.NOT_FOUND,
                status_code=404
            )
        return ResponseModel(data=updated_project)
    except Exception as e:
        raise AppException(
                message=str(e),
                code=ErrorCode.INTERNAL_ERROR,
                status_code=500
            )

@router.post("/projects/{project_id}/assets/batch", response_model=ResponseModel)
async def batch_create_assets(project_id: str, request: List[CreateAssetRequest]):
    """Batch create assets"""
    try:
        new_assets = []
        for asset_req in request:
            asset_data = asset_req.model_dump(by_alias=True)
            asset_data['id'] = str(uuid.uuid4())
            # Validate and convert to Asset Union
            new_asset = TypeAdapter(Asset).validate_python(asset_data)
            new_assets.append(new_asset)

        updated_project = project_manager.batch_add_assets(project_id, new_assets)
        if not updated_project:
            raise AppException(
                message="Project not found",
                code=ErrorCode.NOT_FOUND,
                status_code=404
            )
        return ResponseModel(data=updated_project)
    except Exception as e:
        raise AppException(
                message=str(e),
                code=ErrorCode.INTERNAL_ERROR,
                status_code=500
            )

@router.put("/projects/{project_id}/assets/{asset_id}", response_model=ResponseModel)
async def update_asset(project_id: str, asset_id: str, request: UpdateAssetRequest):
    """Update an asset"""
    try:
        project = project_manager.get_project(project_id)
        if not project:
             raise AppException(
                message="Project not found",
                code=ErrorCode.NOT_FOUND,
                status_code=404
            )

        existing_asset = next((a for a in project.assets if a.id == asset_id), None)
        if not existing_asset:
             raise AppException(
                message="Asset not found",
                code=ErrorCode.NOT_FOUND,
                status_code=404
            )

        update_data = request.model_dump(exclude_unset=True)

        # Manually validate generations if present to ensure they are model instances not dicts
        if 'generations' in update_data and update_data['generations']:
            update_data['generations'] = TypeAdapter(List[GenerationRecord]).validate_python(update_data['generations'])

        updated_asset = existing_asset.model_copy(update=update_data)

        updated_project = project_manager.update_asset(project_id, asset_id, updated_asset)
        return ResponseModel(data=updated_project)
    except Exception as e:
        raise AppException(
                message=str(e),
                code=ErrorCode.INTERNAL_ERROR,
                status_code=500
            )

@router.delete("/projects/{project_id}/assets/{asset_id}", response_model=ResponseModel)
async def delete_asset(project_id: str, asset_id: str):
    """Delete an asset"""
    try:
        updated_project = project_manager.delete_asset(project_id, asset_id)
        if not updated_project:
            raise AppException(
                message="Project not found",
                code=ErrorCode.NOT_FOUND,
                status_code=404
            )
        return ResponseModel(data=updated_project)
    except Exception as e:
        raise AppException(
                message=str(e),
                code=ErrorCode.INTERNAL_ERROR,
                status_code=500
            )
