"""从小说文本初始化项目的业务逻辑（后台管道）。"""
import logging
import uuid
from typing import Callable, Optional

from src.models.schemas import (
    CharacterAsset,
    SceneAsset,
    PropAsset,
    Storyboard,
)
from src.services.project_service import project_manager
from src.services.script import script_service
from src.services.project_helpers import get_cinematic_defaults

logger = logging.getLogger(__name__)

ProgressCallback = Callable[[str, int, str, Optional[dict]], None]


async def run_initialization_pipeline(
    project_id: str,
    novel_text: str,
    style: str,
    progress_callback: ProgressCallback,
    user_id: Optional[str] = None,
) -> None:
    """
    在后台执行完整初始化：分析文本 -> 资产/章节/分镜 -> 更新项目。
    progress_callback(step, percentage, message, details).

    Args:
        user_id: User ID for retrieving user's API key (纯用户密钥模式)
    """
    try:
        logger.info("Starting background initialization for project %s", project_id)

        progress_callback("starting", 5, "正在启动初始化流程...", None)
        progress_callback("analyzing", 10, "AI 正在分析文本...", None)

        result = await script_service.run_full_initialization(
            novel_text=novel_text,
            project_id=project_id,
            style_id=style,
            progress_callback=progress_callback,
            user_id=user_id,
        )

        progress_callback("processing_assets", 70, "正在处理资产数据...", None)

        all_assets = []
        for char in result["assets"].get("characters", []):
            all_assets.append(
                CharacterAsset(
                    id=char.get("id") or str(uuid.uuid4()),
                    name=char.get("name", "Unknown"),
                    desc=char.get("desc", ""),
                    tags=char.get("tags", []),
                    type="character",
                    role=char.get("role"),
                    age=char.get("age"),
                    appearance=char.get("appearance"),
                    image_prompt=char.get("image_prompt"),
                )
            )
        for scene in result["assets"].get("scenes", []):
            all_assets.append(
                SceneAsset(
                    id=scene.get("id") or str(uuid.uuid4()),
                    name=scene.get("name", "Unknown"),
                    desc=scene.get("desc", ""),
                    tags=scene.get("tags", []),
                    type="scene",
                    location=scene.get("location"),
                    time_of_day=scene.get("time_of_day"),
                    atmosphere=scene.get("atmosphere"),
                    image_prompt=scene.get("image_prompt"),
                )
            )
        for prop in result["assets"].get("props", []):
            all_assets.append(
                PropAsset(
                    id=prop.get("id") or str(uuid.uuid4()),
                    name=prop.get("name", "Unknown"),
                    desc=prop.get("desc", ""),
                    tags=prop.get("tags", []),
                    type="prop",
                    usage=prop.get("usage"),
                    image_prompt=prop.get("image_prompt"),
                )
            )

        chapter_list = []
        for ch in result.get("chapters", []):
            ch_id = ch.get("id") or str(uuid.uuid4())
            ch["id"] = ch_id
            chapter_list.append({
                "id": ch_id,
                "title": ch.get("title"),
                "order": ch.get("order"),
                "content": ch.get("content"),
                "summary": ch.get("summary"),
                "status": "draft",
            })

        char_map = {a.name: a.id for a in all_assets if a.type == "character"}
        prop_map = {a.name: a.id for a in all_assets if a.type == "prop"}
        scene_map = {}
        for a in all_assets:
            if a.type == "scene":
                scene_map[a.name] = a.id
                if a.location:
                    scene_map[a.location] = a.id

        cinematic_defaults = get_cinematic_defaults()
        all_storyboards = []
        for ch in result.get("chapters", []):
            ch_id = ch["id"]
            storyboards_data = ch.get("storyboards", [])
            if not storyboards_data:
                continue
            for i, sb_data in enumerate(storyboards_data):
                try:
                    char_ids = []
                    for name in sb_data.get("character_list", []):
                        for c_name, c_id in char_map.items():
                            if c_name in name or name in c_name:
                                char_ids.append(c_id)
                                break
                    prop_ids = []
                    for name in sb_data.get("prop_list", []):
                        for p_name, p_id in prop_map.items():
                            if p_name in name or name in p_name:
                                prop_ids.append(p_id)
                                break
                    scene_id = None
                    loc = sb_data.get("location")
                    if loc:
                        if loc in scene_map:
                            scene_id = scene_map[loc]
                        else:
                            for s_key, s_id in scene_map.items():
                                if s_key in loc or loc in s_key:
                                    scene_id = s_id
                                    break
                    new_sb = Storyboard(
                        id=sb_data.get("id") or str(uuid.uuid4()),
                        episodeId=ch_id,
                        order=sb_data.get("order") or (i + 1),
                        shot=sb_data.get("shot_title") or sb_data.get("shot", f"Shot {i+1}"),
                        desc=sb_data.get("visual_description") or sb_data.get("desc", ""),
                        duration=sb_data.get("duration", "3s"),
                        type=sb_data.get("shot_type") or sb_data.get("type", "medium"),
                        sceneId=scene_id,
                        characterIds=list(set(char_ids)),
                        propIds=list(set(prop_ids)),
                        voiceover=sb_data.get("dialogue") or sb_data.get("voiceover"),
                        audioDesc=sb_data.get("audio_description") or sb_data.get("audio_desc"),
                        cameraMovement=sb_data.get("camera_movement"),
                        cameraAngle=sb_data.get("camera_angle") or cinematic_defaults["camera_angle"],
                        lens=sb_data.get("lens") or cinematic_defaults["lens"],
                        focus=sb_data.get("focus") or cinematic_defaults["focus"],
                        lighting=sb_data.get("lighting") or cinematic_defaults["lighting"],
                        colorStyle=sb_data.get("color_style") or cinematic_defaults["color_style"],
                        transition=sb_data.get("transition"),
                        location=sb_data.get("location"),
                        time=sb_data.get("time_of_day"),
                        originalText=sb_data.get("original_text"),
                        mergeImagePrompt=sb_data.get("merge_image_prompt"),
                        videoPrompt=sb_data.get("video_prompt"),
                        imageUrls=sb_data.get("image_urls"),
                        videoUrls=sb_data.get("video_urls"),
                    )
                    all_storyboards.append(new_sb)
                except Exception as e:
                    logger.warning("Failed to prepare storyboard for project %s: %s", project_id, e)

        progress_callback("finalizing", 95, "正在保存项目数据...", None)
        project_manager.update_project(
            project_id,
            {
                "description": result.get("description"),
                "chapters": chapter_list,
                "style_id": result.get("style_id"),
                "assets": all_assets,
                "storyboards": all_storyboards,
                "status": "active",
                "progress": None,
            },
        )
        logger.info("Background initialization completed for project %s", project_id)
    except Exception as e:
        logger.error(
            "Background initialization failed for project %s: %s",
            project_id,
            e,
            exc_info=True,
        )
        project_manager.update_project(
            project_id,
            {
                "status": "failed",
                "description": f"Initialization Failed: {str(e)}",
                "error": {"message": str(e), "type": type(e).__name__},
                "progress": None,
            },
        )
        raise
