"""
Storyboard Service

Provides storyboard management functionality including:
- Storyboard generation from novel text
- CRUD operations for storyboards
- Integration with project management
"""

import logging
import uuid
from typing import List, Dict, Any, Optional
from datetime import datetime

from src.models.schemas import Storyboard, ProjectData
from src.services.script.pipeline import ScriptAnalysisPipeline
from src.services.project_service import project_manager

logger = logging.getLogger(__name__)


class StoryboardService:
    """
    Service for managing storyboards in projects.
    
    Storyboards represent individual shots/frames in a video production,
    containing visual descriptions, camera movements, and other cinematic details.
    """
    
    def __init__(
        self,
        model_name: Optional[str] = None,
        provider: Optional[str] = None,
        base_url: Optional[str] = None,
        api_key: Optional[str] = None
    ):
        """
        Initialize the storyboard service.
        
        Args:
            model_name: LLM model name for storyboard generation
            provider: LLM provider (e.g., "dashscope", "openai")
            base_url: Custom API base URL
            api_key: API key for the LLM
        """
        self.model_name = model_name
        self.provider = provider
        self.base_url = base_url
        self.api_key = api_key
        self._pipeline = None
    
    @property
    def pipeline(self) -> ScriptAnalysisPipeline:
        """Lazy initialization of the script analysis pipeline."""
        if self._pipeline is None:
            self._pipeline = ScriptAnalysisPipeline(
                model_name=self.model_name,
                provider=self.provider,
                base_url=self.base_url,
                api_key=self.api_key
            )
        return self._pipeline
    
    def set_api_key(self, api_key: str):
        """Set API key for the pipeline."""
        self.api_key = api_key
        if self._pipeline:
            self._pipeline.set_api_key(api_key)
    
    async def generate_storyboards(
        self,
        novel_text: str,
        summary: str = "",
        language: str = "Chinese",
        known_characters: Optional[List[Dict]] = None,
        known_scenes: Optional[List[Dict]] = None,
        known_props: Optional[List[Dict]] = None
    ) -> List[Dict[str, Any]]:
        """
        Generate storyboards from novel text using AI.
        
        Args:
            novel_text: The novel/chapter text to convert to storyboards
            summary: Summary of the novel for context
            language: Output language (default: Chinese)
            known_characters: List of known character assets
            known_scenes: List of known scene assets
            known_props: List of known prop assets
            
        Returns:
            List of storyboard dictionaries
        """
        logger.info(f"Generating storyboards for text of length {len(novel_text)}")
        
        try:
            storyboards = await self.pipeline.run_storyboard_generation(
                text=novel_text,
                summary=summary,
                language=language,
                known_characters=known_characters or [],
                known_scenes=known_scenes or [],
                known_props=known_props or []
            )
            
            logger.info(f"Generated {len(storyboards)} storyboards")
            return storyboards
            
        except Exception as e:
            logger.error(f"Failed to generate storyboards: {e}", exc_info=True)
            raise
    
    def get_storyboard(self, project_id: str, storyboard_id: str) -> Optional[Storyboard]:
        """
        Get a specific storyboard from a project.
        
        Args:
            project_id: The project ID
            storyboard_id: The storyboard ID
            
        Returns:
            Storyboard object or None if not found
        """
        try:
            project = project_manager.get_project(project_id)
            if not project:
                logger.warning(f"Project {project_id} not found")
                return None
            
            for storyboard in project.storyboards:
                if storyboard.id == storyboard_id:
                    return storyboard
            
            logger.warning(f"Storyboard {storyboard_id} not found in project {project_id}")
            return None
            
        except Exception as e:
            logger.error(f"Failed to get storyboard: {e}", exc_info=True)
            return None
    
    def list_storyboards(self, project_id: str) -> List[Storyboard]:
        """
        List all storyboards in a project.
        
        Args:
            project_id: The project ID
            
        Returns:
            List of Storyboard objects
        """
        try:
            project = project_manager.get_project(project_id)
            if not project:
                logger.warning(f"Project {project_id} not found")
                return []
            
            return project.storyboards
            
        except Exception as e:
            logger.error(f"Failed to list storyboards: {e}", exc_info=True)
            return []
    
    def add_storyboard(
        self,
        project_id: str,
        episode_id: Optional[str] = None,
        shot: str = "",
        desc: str = "",
        duration: str = "3s",
        type: str = "image",
        order: Optional[int] = None,
        **kwargs
    ) -> Optional[Storyboard]:
        """
        Add a new storyboard to a project.
        
        Args:
            project_id: The project ID
            episode_id: Optional episode ID
            shot: Shot title/description
            desc: Detailed description
            duration: Shot duration (e.g., "3s", "5s")
            type: Shot type (image/video)
            order: Order in the sequence
            **kwargs: Additional storyboard fields
            
        Returns:
            Created Storyboard object or None on failure
        """
        try:
            project = project_manager.get_project(project_id)
            if not project:
                logger.warning(f"Project {project_id} not found")
                return None
            
            # Generate ID and order
            storyboard_id = str(uuid.uuid4())
            if order is None:
                order = len(project.storyboards) + 1
            
            storyboard = Storyboard(
                id=storyboard_id,
                episode_id=episode_id or "",
                order=order,
                shot=shot,
                desc=desc,
                duration=duration,
                type=type,
                **kwargs
            )
            
            project.storyboards.append(storyboard)
            project.updated_at = datetime.now()
            project_manager.save_project(project)
            
            logger.info(f"Added storyboard {storyboard_id} to project {project_id}")
            return storyboard
            
        except Exception as e:
            logger.error(f"Failed to add storyboard: {e}", exc_info=True)
            return None
    
    def update_storyboard(
        self,
        project_id: str,
        storyboard_id: str,
        **updates
    ) -> Optional[Storyboard]:
        """
        Update a storyboard in a project.
        
        Args:
            project_id: The project ID
            storyboard_id: The storyboard ID
            **updates: Fields to update
            
        Returns:
            Updated Storyboard object or None on failure
        """
        try:
            project = project_manager.get_project(project_id)
            if not project:
                logger.warning(f"Project {project_id} not found")
                return None
            
            for i, storyboard in enumerate(project.storyboards):
                if storyboard.id == storyboard_id:
                    # Create updated storyboard
                    updated_data = storyboard.model_dump()
                    updated_data.update(updates)
                    updated_storyboard = Storyboard(**updated_data)
                    
                    project.storyboards[i] = updated_storyboard
                    project.updated_at = datetime.now()
                    project_manager.save_project(project)
                    
                    logger.info(f"Updated storyboard {storyboard_id}")
                    return updated_storyboard
            
            logger.warning(f"Storyboard {storyboard_id} not found")
            return None
            
        except Exception as e:
            logger.error(f"Failed to update storyboard: {e}", exc_info=True)
            return None
    
    def delete_storyboard(self, project_id: str, storyboard_id: str) -> bool:
        """
        Delete a storyboard from a project.
        
        Args:
            project_id: The project ID
            storyboard_id: The storyboard ID
            
        Returns:
            True if deleted, False otherwise
        """
        try:
            project = project_manager.get_project(project_id)
            if not project:
                logger.warning(f"Project {project_id} not found")
                return False
            
            original_count = len(project.storyboards)
            project.storyboards = [
                sb for sb in project.storyboards if sb.id != storyboard_id
            ]
            
            if len(project.storyboards) < original_count:
                project.updated_at = datetime.now()
                project_manager.save_project(project)
                logger.info(f"Deleted storyboard {storyboard_id}")
                return True
            
            logger.warning(f"Storyboard {storyboard_id} not found")
            return False
            
        except Exception as e:
            logger.error(f"Failed to delete storyboard: {e}", exc_info=True)
            return False
    
    def reorder_storyboards(
        self,
        project_id: str,
        storyboard_ids: List[str]
    ) -> bool:
        """
        Reorder storyboards in a project.
        
        Args:
            project_id: The project ID
            storyboard_ids: List of storyboard IDs in desired order
            
        Returns:
            True if successful, False otherwise
        """
        try:
            project = project_manager.get_project(project_id)
            if not project:
                logger.warning(f"Project {project_id} not found")
                return False
            
            # Create a map of id -> storyboard
            storyboard_map = {sb.id: sb for sb in project.storyboards}
            
            # Reorder based on provided IDs
            new_storyboards = []
            for order, sb_id in enumerate(storyboard_ids, 1):
                if sb_id in storyboard_map:
                    storyboard = storyboard_map[sb_id]
                    storyboard.order = order
                    new_storyboards.append(storyboard)
            
            # Add any storyboards not in the reorder list at the end
            remaining = [
                sb for sb in project.storyboards 
                if sb.id not in storyboard_ids
            ]
            for i, sb in enumerate(remaining, len(new_storyboards) + 1):
                sb.order = i
                new_storyboards.append(sb)
            
            project.storyboards = new_storyboards
            project.updated_at = datetime.now()
            project_manager.save_project(project)
            
            logger.info(f"Reordered {len(storyboard_ids)} storyboards")
            return True
            
        except Exception as e:
            logger.error(f"Failed to reorder storyboards: {e}", exc_info=True)
            return False
    
    async def generate_and_save_storyboards(
        self,
        project_id: str,
        novel_text: str,
        summary: str = "",
        language: str = "Chinese",
        known_characters: Optional[List[Dict]] = None,
        known_scenes: Optional[List[Dict]] = None,
        known_props: Optional[List[Dict]] = None,
        episode_id: Optional[str] = None
    ) -> List[Storyboard]:
        """
        Generate storyboards and save them to a project.
        
        Args:
            project_id: The project ID
            novel_text: The novel text to convert
            summary: Novel summary for context
            language: Output language
            known_characters: Known character assets
            known_scenes: Known scene assets
            known_props: Known prop assets
            episode_id: Optional episode ID to associate
            
        Returns:
            List of created Storyboard objects
        """
        try:
            project = project_manager.get_project(project_id)
            if not project:
                raise ValueError(f"Project {project_id} not found")
            
            # Generate storyboards
            generated = await self.generate_storyboards(
                novel_text=novel_text,
                summary=summary,
                language=language,
                known_characters=known_characters,
                known_scenes=known_scenes,
                known_props=known_props
            )
            
            # Convert to Storyboard objects and add to project
            created_storyboards = []
            base_order = len(project.storyboards)
            
            for i, sb_data in enumerate(generated, 1):
                storyboard_id = str(uuid.uuid4())
                
                storyboard = Storyboard(
                    id=storyboard_id,
                    episode_id=episode_id or "",
                    order=base_order + i,
                    shot=sb_data.get("shot_title", f"Shot {i}"),
                    desc=sb_data.get("visual_description", ""),
                    duration=sb_data.get("duration", "3s"),
                    type="image",
                    scene_id=sb_data.get("location"),
                    character_ids=sb_data.get("character_list", []),
                    prop_ids=sb_data.get("prop_list", []),
                    voiceover=sb_data.get("dialogue"),
                    camera_movement=sb_data.get("camera_movement"),
                    transition=sb_data.get("transition"),
                    audio_desc=sb_data.get("audio_description"),
                    camera_angle=sb_data.get("camera_angle"),
                    location=sb_data.get("location"),
                    time=sb_data.get("time_of_day")
                )
                
                project.storyboards.append(storyboard)
                created_storyboards.append(storyboard)
            
            project.updated_at = datetime.now()
            project_manager.save_project(project)
            
            logger.info(f"Generated and saved {len(created_storyboards)} storyboards to project {project_id}")
            return created_storyboards
            
        except Exception as e:
            logger.error(f"Failed to generate and save storyboards: {e}", exc_info=True)
            raise


# Singleton instance
storyboard_service = StoryboardService()