import logging
from typing import Any, Optional

from app.auth import AuthenticatedUser, get_current_user
from app.core.storage import get_storage_service
from app.dependencies import (
    get_asset_library_repository,
    get_asset_repository,
    get_project_repository,
    get_tag_repository,
)
from app.schemas.asset import (
    AssetResponse,
    BatchDeleteRequest,
    BatchDeleteResponse,
    CreateAssetRequest,
    ListAssetsResponse,
    UpdateAssetRequest,
    UpdateAssetReviewRequest,
)
from app.schemas.tag import TagAssetsRequest
from fastapi import APIRouter, Depends, HTTPException, Query

from packages.application import (
    CreateAssetCommand,
    CreateAssetUseCase,
)
from packages.domain import AssetStatus, ClassificationStatus

logger = logging.getLogger(__name__)

router = APIRouter()


def _to_asset_response(item, storage_service=None) -> AssetResponse:
    # 生成签名文件 URL（用于视频播放 / 文件下载）
    file_url = None
    if item.storage_key:
        try:
            svc = storage_service or get_storage_service()
            file_url = svc.get_download_url(item.storage_key)
        except Exception:
            logger.warning("生成签名URL失败: storage_key=%s", item.storage_key, exc_info=True)
            file_url = None

    # 缩略图：优先用已有 thumbnail_url，否则对视频素材复用文件签名 URL
    thumbnail_url = item.thumbnail_url
    if not thumbnail_url and item.mime_type and item.mime_type.startswith("video") and file_url:
        thumbnail_url = file_url

    return AssetResponse(
        id=item.id,
        project_id=item.project_id,
        library_id=item.library_id,
        name=item.name,
        storage_key=item.storage_key,
        mime_type=item.mime_type,
        metadata=item.metadata,
        file_size=item.file_size,
        file_url=file_url,
        thumbnail_url=thumbnail_url,
        duration=item.duration,
        width=item.width,
        height=item.height,
        fps=item.fps,
        codec=item.codec,
        status=item.status.value,
        classification_status=item.classification_status.value,
        quality_score=item.quality_score,
        uploaded_by_user_id=item.uploaded_by_user_id,
        tag_ids=getattr(item, "tag_ids", []),
    )


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")


@router.get("", response_model=ListAssetsResponse)
def list_assets(
    library_id: Optional[str] = Query(None),
    project_id: Optional[str] = Query(None),
    kind: Optional[str] = Query(None, pattern="^(video|voice|image)$"),
    keyword: Optional[str] = Query(None, description="按名称模糊匹配"),
    gender: Optional[str] = Query(None, description="按 metadata.gender 筛选"),
    style: Optional[str] = Query(None, description="按 metadata.style 筛选"),
    tag_ids: Optional[str] = Query(None, description="按标签 ID 筛选（逗号分隔，取交集）"),
    skip: int = Query(0, ge=0),
    limit: int = Query(100, ge=1, le=500),
    authenticated_user: AuthenticatedUser = Depends(get_current_user),
    asset_repository: Any = Depends(get_asset_repository),
    asset_library_repository: Any = Depends(get_asset_library_repository),
    project_repository: Any = Depends(get_project_repository),
) -> ListAssetsResponse:
    user_id = authenticated_user.user.id

    # kind → file_type 映射（voice 对应 audio）
    kind_to_file_type = {"video": "video", "voice": "audio", "image": "image"}

    # 解析 tag_ids 参数（逗号分隔）
    filter_tag_ids: list[str] | None = None
    if tag_ids:
        filter_tag_ids = [t.strip() for t in tag_ids.split(",") if t.strip()]
        if not filter_tag_ids:
            filter_tag_ids = None

    # 需要内存过滤的标志（keyword/gender/style/tag_ids 无法在 DB 层过滤）
    needs_memory_filter = bool(keyword or gender or style or filter_tag_ids)

    def _apply_memory_filters(items):
        """应用 keyword / gender / style / tag_ids 内存过滤。"""
        result = items
        if keyword:
            kw = keyword.lower()
            result = [i for i in result if kw in (i.name or "").lower()]
        if gender:
            result = [i for i in result if (i.metadata or {}).get("gender") == gender]
        if style:
            result = [i for i in result if (i.metadata or {}).get("style") == style]
        if filter_tag_ids:
            tag_set = set(filter_tag_ids)
            result = [i for i in result if tag_set.issubset(set(getattr(i, "tag_ids", [])))]
        return result

    # ── 优化路径：无内存过滤时，使用 DB 级分页 ──
    if not needs_memory_filter:
        ft = kind_to_file_type.get(kind) if kind else None

        # 模式1：指定 library_id
        if library_id:
            library = asset_library_repository.get(library_id)
            if library is None:
                raise HTTPException(status_code=404, detail=f"AssetLibrary {library_id} not found")
            _check_project_access(library.project_id, user_id, project_repository)
            if ft:
                items = asset_repository.find_by_library_and_file_type(library_id, ft, skip=skip, limit=limit)
                total = asset_repository.count_by_project(library.project_id) if not kind else len(items)
            else:
                items = asset_repository.find_by_library(library_id, skip=skip, limit=limit)
                total = asset_repository.count_by_project(library.project_id)
            return ListAssetsResponse(
                items=[_to_asset_response(item) for item in items],
                total=total,
                skip=skip,
                limit=limit,
            )

        # 模式2：指定 project_id
        if project_id:
            _check_project_access(project_id, user_id, project_repository)
            if ft:
                # 无直接方法，加载后按 file_type 过滤（仍比全量加载好）
                all_items = asset_repository.find_by_project(project_id)
                items = [i for i in all_items if i.mime_type and i.mime_type.startswith(ft)]
                total = len(items)
                paged = items[skip : skip + limit]
            else:
                items = asset_repository.find_by_project(project_id, skip=skip, limit=limit)
                total = asset_repository.count_by_project(project_id)
                paged = items
            return ListAssetsResponse(
                items=[_to_asset_response(item) for item in paged],
                total=total,
                skip=skip,
                limit=limit,
            )

        # 模式3：跨项目（无 library_id/project_id）
        try:
            projects = project_repository.find_accessible_projects(user_id)
        except Exception:
            logger.exception("查询用户可访问项目失败: user_id=%s", user_id)
            return ListAssetsResponse(items=[], total=0, skip=skip, limit=limit)

        project_ids = [p.id for p in projects]
        if not project_ids:
            return ListAssetsResponse(items=[], total=0, skip=skip, limit=limit)

        total = asset_repository.count_by_project_ids(project_ids)
        # 跨项目分页：逐项目累积直到凑够一页
        paged_items: list = []
        offset = skip
        remaining = limit
        for pid in project_ids:
            proj_total = asset_repository.count_by_project(pid)
            if offset >= proj_total:
                offset -= proj_total
                continue
            proj_items = asset_repository.find_by_project(pid, skip=offset, limit=remaining)
            paged_items.extend(proj_items)
            remaining -= len(proj_items)
            offset = 0
            if remaining <= 0:
                break

        return ListAssetsResponse(
            items=[_to_asset_response(item) for item in paged_items],
            total=total,
            skip=skip,
            limit=limit,
        )

    # ── 内存过滤路径：有 keyword/gender/style 时，加载全量后内存过滤 ──
    if library_id:
        library = asset_library_repository.get(library_id)
        if library is None:
            raise HTTPException(status_code=404, detail=f"AssetLibrary {library_id} not found")
        _check_project_access(library.project_id, user_id, project_repository)
        if kind:
            all_items = asset_repository.find_by_library_and_file_type(library_id, kind_to_file_type[kind])
        else:
            all_items = asset_repository.find_by_library(library_id)
    elif project_id:
        _check_project_access(project_id, user_id, project_repository)
        all_items = asset_repository.find_by_project(project_id)
    else:
        try:
            projects = project_repository.find_accessible_projects(user_id)
        except Exception:
            logger.exception("查询用户可访问项目失败: user_id=%s", user_id)
            return ListAssetsResponse(items=[], total=0, skip=skip, limit=limit)
        all_items = []
        for proj in projects:
            all_items.extend(asset_repository.find_by_project(proj.id))

    # 应用 kind 过滤（如果有）+ keyword/gender/style
    if kind:
        ft = kind_to_file_type.get(kind)
        all_items = [i for i in all_items if i.mime_type and i.mime_type.startswith(ft or "")]
    filtered = _apply_memory_filters(all_items)
    total = len(filtered)
    paged = filtered[skip : skip + limit]
    return ListAssetsResponse(
        items=[_to_asset_response(item) for item in paged],
        total=total,
        skip=skip,
        limit=limit,
    )


def _apply_asset_review_status(item, review_status: str):
    item.metadata = {
        **item.metadata,
        "review_status": review_status,
    }
    return item


@router.patch("/{asset_id}/review", response_model=AssetResponse)
def update_asset_review_status(
    asset_id: str,
    request: UpdateAssetReviewRequest,
    authenticated_user: AuthenticatedUser = Depends(get_current_user),
    asset_repository: Any = Depends(get_asset_repository),
    project_repository: Any = Depends(get_project_repository),
) -> AssetResponse:
    item = asset_repository.get(asset_id)
    if item is None:
        raise HTTPException(status_code=404, detail=f"Asset {asset_id} not found")
    _check_project_access(item.project_id, authenticated_user.user.id, project_repository)
    _apply_asset_review_status(item, request.review_status)
    updated = asset_repository.update(item)
    return _to_asset_response(updated)


@router.post("/batch-delete", response_model=BatchDeleteResponse)
def batch_delete_assets(
    request: BatchDeleteRequest,
    authenticated_user: AuthenticatedUser = Depends(get_current_user),
    asset_repository: Any = Depends(get_asset_repository),
    project_repository: Any = Depends(get_project_repository),
) -> BatchDeleteResponse:
    """批量删除素材（配音素材等），需逐项校验项目权限。"""
    user_id = authenticated_user.user.id
    deleted_ids: list[str] = []
    failed_ids: list[str] = []

    for asset_id in request.ids:
        item = asset_repository.find_by_id(asset_id)
        if item is None:
            failed_ids.append(asset_id)
            continue
        try:
            _check_project_access(item.project_id, user_id, project_repository)
            deleted_ids.append(asset_id)
        except HTTPException:
            failed_ids.append(asset_id)

    if deleted_ids:
        asset_repository.batch_delete(deleted_ids)

    return BatchDeleteResponse(deleted_count=len(deleted_ids), failed_ids=failed_ids)


@router.get("/{asset_id}", response_model=AssetResponse)
def get_asset(
    asset_id: str,
    authenticated_user: AuthenticatedUser = Depends(get_current_user),
    asset_repository: Any = Depends(get_asset_repository),
    project_repository: Any = Depends(get_project_repository),
) -> AssetResponse:
    item = asset_repository.find_by_id(asset_id)
    if item is None:
        raise HTTPException(status_code=404, detail=f"Asset {asset_id} not found")
    _check_project_access(item.project_id, authenticated_user.user.id, project_repository)
    return _to_asset_response(item)


@router.put("/{asset_id}", response_model=AssetResponse)
def update_asset(
    asset_id: str,
    request: UpdateAssetRequest,
    authenticated_user: AuthenticatedUser = Depends(get_current_user),
    asset_repository: Any = Depends(get_asset_repository),
    project_repository: Any = Depends(get_project_repository),
) -> AssetResponse:
    item = asset_repository.find_by_id(asset_id)
    if item is None:
        raise HTTPException(status_code=404, detail=f"Asset {asset_id} not found")
    _check_project_access(item.project_id, authenticated_user.user.id, project_repository)

    # 合并可修改字段
    if request.name is not None:
        item.name = request.name
    if request.metadata is not None:
        item.metadata = {**item.metadata, **request.metadata}
    if request.tags is not None:
        item.metadata = {**item.metadata, "tags": request.tags}

    updated = asset_repository.update(item)
    return _to_asset_response(updated)


@router.delete("/{asset_id}", status_code=204)
def delete_asset(
    asset_id: str,
    authenticated_user: AuthenticatedUser = Depends(get_current_user),
    asset_repository: Any = Depends(get_asset_repository),
    project_repository: Any = Depends(get_project_repository),
) -> None:
    item = asset_repository.find_by_id(asset_id)
    if item is None:
        raise HTTPException(status_code=404, detail=f"Asset {asset_id} not found")
    _check_project_access(item.project_id, authenticated_user.user.id, project_repository)
    asset_repository.delete(asset_id)


@router.post("/{asset_id}/tags", response_model=AssetResponse)
def tag_asset(
    asset_id: str,
    request: TagAssetsRequest,
    authenticated_user: AuthenticatedUser = Depends(get_current_user),
    asset_repository: Any = Depends(get_asset_repository),
    project_repository: Any = Depends(get_project_repository),
    tag_repository: Any = Depends(get_tag_repository),
) -> AssetResponse:
    """给素材打标签。"""
    item = asset_repository.find_by_id(asset_id)
    if item is None:
        raise HTTPException(status_code=404, detail=f"Asset {asset_id} not found")
    _check_project_access(item.project_id, authenticated_user.user.id, project_repository)
    for tag_id in request.tag_ids:
        tag = tag_repository.get(tag_id)
        if tag is None:
            raise HTTPException(status_code=404, detail=f"Tag {tag_id} not found")
        if tag.user_id != authenticated_user.user.id:
            raise HTTPException(status_code=403, detail=f"无权使用标签 {tag_id}")
        item.add_tag(tag_id)
    updated = asset_repository.update(item)
    return _to_asset_response(updated)


@router.delete("/{asset_id}/tags/{tag_id}", status_code=204)
def untag_asset(
    asset_id: str,
    tag_id: str,
    authenticated_user: AuthenticatedUser = Depends(get_current_user),
    asset_repository: Any = Depends(get_asset_repository),
    project_repository: Any = Depends(get_project_repository),
) -> None:
    """取消素材的标签。"""
    item = asset_repository.find_by_id(asset_id)
    if item is None:
        raise HTTPException(status_code=404, detail=f"Asset {asset_id} not found")
    _check_project_access(item.project_id, authenticated_user.user.id, project_repository)
    item.remove_tag(tag_id)
    asset_repository.update(item)


@router.post("", response_model=AssetResponse)
def create_asset(
    request: CreateAssetRequest,
    authenticated_user: AuthenticatedUser = Depends(get_current_user),
    asset_repository: Any = Depends(get_asset_repository),
    asset_library_repository: Any = Depends(get_asset_library_repository),
    project_repository: Any = Depends(get_project_repository),
) -> AssetResponse:
    project = project_repository.find_by_id(request.project_id)
    if project is None:
        raise HTTPException(status_code=404, detail=f"Project {request.project_id} not found")
    if not project.can_access(authenticated_user.user.id):
        raise HTTPException(status_code=403, detail="Access denied to project")

    library = asset_library_repository.get(request.library_id)
    if library is None or library.project_id != request.project_id:
        raise HTTPException(status_code=404, detail=f"AssetLibrary {request.library_id} not found")

    use_case = CreateAssetUseCase(asset_repository)
    item = use_case.execute(
        CreateAssetCommand(
            project_id=request.project_id,
            library_id=request.library_id,
            name=request.name,
            storage_key=request.storage_key,
            mime_type=request.mime_type,
            metadata=request.metadata,
            file_size=request.file_size,
            thumbnail_url=request.thumbnail_url,
            duration=request.duration,
            width=request.width,
            height=request.height,
            fps=request.fps,
            codec=request.codec,
            status=AssetStatus(request.status),
            classification_status=ClassificationStatus(request.classification_status),
            quality_score=request.quality_score,
            uploaded_by_user_id=authenticated_user.user.id,
        )
    )
    return _to_asset_response(item)
