"""
Script Analysis Endpoints - 脚本分析模块

包含所有与 LLM 脚本分析相关的端点:
- /script/analyze - 分析小说文本
- /prompt/optimize - 优化提示词
- /style/recommend - 推荐风格
- /script/summary - 小说摘要
- /script/characters - 角色提取
- /script/scenes - 场景提取
- /script/props - 道具提取
- /script/storyboards - 分镜拆分
- /script/split - 章节拆分
""" 
import logging
import uuid

from fastapi import APIRouter, BackgroundTasks, Depends

from src.auth.dependencies import get_current_user
from src.auth.models import UserAuth
from src.models.schemas import (
    ResponseModel,
    ScriptProcessRequest, ScriptResponse,
    PromptOptimizationRequest, PromptOptimizationResponse,
    StyleRecommendationRequest, StyleRecommendationResponse,
    NovelSummaryRequest, NovelSummaryResponse,
    CharacterExtractionRequest, CharacterExtractionResponse,
    SceneExtractionRequest, SceneExtractionResponse,
    PropExtractionRequest, PropExtractionResponse,
    StoryboardSplitRequest, StoryboardSplitResponse,
    ChapterSplitRequest, ChapterSplitResponse,
    CharacterAsset, SceneAsset, PropAsset
)
from src.services.script import script_service
from src.services.project_service import project_manager

from .helpers import get_available_styles_from_config
from src.utils.errors import ErrorCode, AppException

router = APIRouter()
logger = logging.getLogger(__name__)


@router.post("/script/analyze", response_model=ResponseModel)
async def analyze_script(
    request: ScriptProcessRequest,
    background_tasks: BackgroundTasks,
    current_user: UserAuth = Depends(get_current_user)
):
    """Analyze novel text and generate script"""
    try:
        # Handle skip_storyboard priority (explicit param vs extra_params)
        skip_sb = request.skip_storyboard
        extra = request.extra_params or {}
        if 'skip_storyboard' in extra:
            skip_sb = extra.pop('skip_storyboard')

        response_data = await script_service.analyze_novel(
            novel_text=request.novel_text,
            project_id=request.project_id,
            model_name=request.model,
            max_input_tokens=request.max_input_tokens,
            skip_storyboard=skip_sb,
            user_id=current_user.id,
            **extra
        )

        # Automatically save extracted assets to the project
        if request.project_id:
            # 获取 existing project assets for deduplication
            existing_assets_map = {}
            try:
                current_project = project_manager.get_project(request.project_id)
                if current_project and current_project.assets:
                    for asset in current_project.assets:
                        existing_assets_map[(asset.type, asset.name)] = asset
            except Exception as e:
                logger.warning(f"Failed to fetch project for deduplication: {e}")

            # 辅助函数 to create and add asset
            def save_asset(item: dict, asset_type: str):
                name = item.get('name')
                if not name:
                    return

                # Check if exists
                existing_asset = existing_assets_map.get((asset_type, name))
                
                if existing_asset:
                    logger.info(f"Asset {name} ({asset_type}) already exists, updating.")
                    asset_id = existing_asset.id
                else:
                    asset_id = str(uuid.uuid4())
                
                # 创建 specific asset models
                try:
                    asset = None
                    if asset_type == 'character':
                        asset = CharacterAsset(
                            id=asset_id,
                            name=item.get('name'),
                            desc=item.get('desc', ''),
                            tags=item.get('tags', []),
                            age=item.get('age'),
                            role=item.get('role'),
                            appearance=item.get('appearance'),
                            image_prompt=item.get('image_prompt')
                        )
                    elif asset_type == 'scene':
                        asset = SceneAsset(
                            id=asset_id,
                            name=item.get('name'),
                            desc=item.get('desc', ''),
                            tags=item.get('tags', []),
                            location=item.get('location'),
                            time_of_day=item.get('time_of_day'),
                            atmosphere=item.get('atmosphere'),
                            image_prompt=item.get('image_prompt')
                        )
                    elif asset_type == 'prop':
                        asset = PropAsset(
                            id=asset_id,
                            name=item.get('name'),
                            desc=item.get('desc', ''),
                            tags=item.get('tags', []),
                            usage=item.get('usage'),
                            image_prompt=item.get('image_prompt')
                        )
                    else:
                        return
                    
                    if existing_asset:
                        project_manager.update_asset(request.project_id, asset_id, asset)
                    else:
                        project_manager.add_asset(request.project_id, asset)
                        
                except Exception as e:
                    logger.error(f"Failed to save asset {item.get('name')}: {e}")

            # Save characters
            for char in (response_data.characters or []):
                save_asset(char.model_dump(), 'character')
            
            # Save scenes
            for scene in (response_data.scenes or []):
                save_asset(scene.model_dump(), 'scene')
                
            # Save props
            for prop in (response_data.props or []):
                save_asset(prop.model_dump(), 'prop')

            # Save summary if available
            if response_data.summary:
                try:
                    project_manager.update_project(request.project_id, {"description": response_data.summary})
                    logger.info(f"Updated project description with generated summary.")
                except Exception as e:
                    logger.error(f"Failed to update project description: {e}")
        
        return ResponseModel(data=response_data)
        
    except Exception as e:
        logger.error(f"Script analysis failed: {str(e)}")
        raise AppException(
                message=str(e),
                code=ErrorCode.INTERNAL_ERROR,
                status_code=500
            )


@router.post("/prompt/optimize", response_model=ResponseModel)
async def optimize_prompt_endpoint(
    request: PromptOptimizationRequest,
    current_user: UserAuth = Depends(get_current_user)
):
    """Optimize prompt for image or video generation"""
    try:
        optimized_prompt = await script_service.optimize_prompt(
            prompt=request.prompt,
            target_type=request.target_type,
            template=request.template,
            model_name=request.model,
            provider=request.provider,
            language=request.language or "Chinese",
            user_id=current_user.id,
            **(request.extra_params or {})
        )
        return ResponseModel(data=PromptOptimizationResponse(
            original_prompt=request.prompt,
            optimized_prompt=optimized_prompt,
            target_type=request.target_type
        ))
    except Exception as e:
        logger.error(f"Prompt optimization failed: {str(e)}")
        raise AppException(
                message=str(e),
                code=ErrorCode.INTERNAL_ERROR,
                status_code=500
            )


@router.post("/style/recommend", response_model=ResponseModel)
async def recommend_style(
    request: StyleRecommendationRequest,
    current_user: UserAuth = Depends(get_current_user)
):
    """Recommend an art style based on novel text.
    
    Backend automatically fetches available styles with full descriptions from config.
    """
    try:
        # Automatically fetch styles with full information from backend config
        styles_full = await get_available_styles_from_config()
        logger.info(f"Auto-fetched {len(styles_full)} styles from backend config")
        
        # 格式化 style information for AI: include name and description
        available_styles = [
            f"{style['name']} - {style.get('desc', '')}"
            for style in styles_full
            if style.get('name')
        ]
        
        logger.info(f"Formatted {len(available_styles)} styles with descriptions for AI")
        
        result = await script_service.recommend_style(
            novel_text=request.novel_text,
            available_styles=available_styles,
            model_name=request.model,
            provider=request.provider,
            user_id=current_user.id,
            **(request.extra_params or {})
        )
        return ResponseModel(data=StyleRecommendationResponse(**result))
    except Exception as e:
        logger.error(f"Style recommendation failed: {str(e)}")
        raise AppException(
                message=str(e),
                code=ErrorCode.INTERNAL_ERROR,
                status_code=500
            )


@router.post("/script/summary", response_model=ResponseModel)
async def summarize_novel_endpoint(
    request: NovelSummaryRequest,
    current_user: UserAuth = Depends(get_current_user)
):
    """ 总和marize novel text into a concise overview (without characters)"""
    try:
        result = await script_service.summarize_novel(
            novel_text=request.novel_text,
            language=request.language,
            model_name=request.model,
            provider=request.provider,
            global_summary=request.global_summary,
            user_id=current_user.id,
            **(request.extra_params or {})
        )
        return ResponseModel(data=NovelSummaryResponse(**result))
    except Exception as e:
        logger.error(f"Novel summary failed: {str(e)}")
        raise AppException(
                message=str(e),
                code=ErrorCode.INTERNAL_ERROR,
                status_code=500
            )


@router.post("/script/characters", response_model=ResponseModel)
async def extract_characters_endpoint(
    request: CharacterExtractionRequest,
    current_user: UserAuth = Depends(get_current_user)
):
    """ 提取 characters from novel text"""
    try:
        result = await script_service.extract_characters(
            novel_text=request.novel_text,
            language=request.language,
            model_name=request.model,
            provider=request.provider,
            global_summary=request.global_summary,
            known_characters=request.known_characters,
            user_id=current_user.id,
            **(request.extra_params or {})
        )
        return ResponseModel(data=CharacterExtractionResponse(**result))
    except Exception as e:
        logger.error(f"Character extraction failed: {str(e)}")
        raise AppException(
                message=str(e),
                code=ErrorCode.INTERNAL_ERROR,
                status_code=500
            )


@router.post("/script/scenes", response_model=ResponseModel)
async def extract_scenes_endpoint(
    request: SceneExtractionRequest,
    current_user: UserAuth = Depends(get_current_user)
):
    """ 提取 scenes from novel text"""
    try:
        result = await script_service.extract_scenes(
            novel_text=request.novel_text,
            language=request.language,
            model_name=request.model,
            global_summary=request.global_summary,
            known_scenes=request.known_scenes,
            user_id=current_user.id,
            **(request.extra_params or {})
        )
        return ResponseModel(data=SceneExtractionResponse(**result))
    except Exception as e:
        logger.error(f"Scene extraction failed: {str(e)}")
        raise AppException(
                message=str(e),
                code=ErrorCode.INTERNAL_ERROR,
                status_code=500
            )


@router.post("/script/props", response_model=ResponseModel)
async def extract_props_endpoint(
    request: PropExtractionRequest,
    current_user: UserAuth = Depends(get_current_user)
):
    """ 提取 props from novel text"""
    try:
        result = await script_service.extract_props(
            novel_text=request.novel_text,
            language=request.language,
            model_name=request.model,
            global_summary=request.global_summary,
            known_props=request.known_props,
            user_id=current_user.id,
            **(request.extra_params or {})
        )
        return ResponseModel(data=PropExtractionResponse(**result))
    except Exception as e:
        logger.error(f"Prop extraction failed: {str(e)}")
        raise AppException(
                message=str(e),
                code=ErrorCode.INTERNAL_ERROR,
                status_code=500
            )


@router.post("/script/storyboards", response_model=ResponseModel)
async def split_storyboards_endpoint(
    request: StoryboardSplitRequest,
    current_user: UserAuth = Depends(get_current_user)
):
    """Split novel text into storyboards (shots)"""
    try:
        result = await script_service.split_storyboards(
            novel_text=request.novel_text,
            project_id=request.project_id,
            model_name=request.model,
            provider=request.provider,
            language=request.language,
            known_characters=request.known_characters,
            known_scenes=request.known_scenes,
            known_props=request.known_props,
            user_id=current_user.id,
            **(request.extra_params or {})
        )
        return ResponseModel(data=StoryboardSplitResponse(**result))
    except Exception as e:
        logger.error(f"Storyboard splitting failed: {str(e)}")
        raise AppException(
                message=str(e),
                code=ErrorCode.INTERNAL_ERROR,
                status_code=500
            )


@router.post("/script/split", response_model=ResponseModel)
async def split_chapters_endpoint(
    request: ChapterSplitRequest,
    current_user: UserAuth = Depends(get_current_user)
):
    """Split novel text into chapters using regex or agent"""
    try:
        # Check if agent splitting is requested via extra_params
        extra = request.extra_params or {}
        use_agent = extra.get("use_agent", False)

        chapters = await script_service.split_chapters(
            novel_text=request.novel_text,
            regex_pattern=request.regex_pattern,
            use_agent=use_agent,
            model_name=request.model,
            language=extra.get("language", "Chinese"),
            user_id=current_user.id
        )
        return ResponseModel(data=ChapterSplitResponse(
            chapters=chapters,
            total_chapters=len(chapters)
        ))
    except Exception as e:
        logger.error(f"Chapter split failed: {str(e)}")
        raise AppException(
                message=str(e),
                code=ErrorCode.INTERNAL_ERROR,
                status_code=500
            )
