"""Content creation workflow orchestrator."""
import httpx
import asyncio
from typing import Dict, Any, Optional
from pathlib import Path
from datetime import datetime
import sys

sys.path.insert(0, str(Path(__file__).parent.parent.parent))

from shared.config import settings
from shared.logging_config import setup_logging
from shared.database import SessionLocal
from shared.models import Project

logger = setup_logging("orchestration.workflow")


class ContentWorkflow:
    """Orchestrates the complete content creation workflow."""
    
    def __init__(self):
        """Initialize workflow orchestrator."""
        self.audio_service_url = settings.audio_service_url
        self.video_service_url = settings.video_service_url
        self.timeout = 30.0
    
    async def create_content(
        self,
        project_id: str,
        prompt: str,
        audio_settings: Optional[Dict[str, Any]] = None,
        video_settings: Optional[Dict[str, Any]] = None
    ) -> Dict[str, Any]:
        """Orchestrate complete content creation.
        
        Args:
            project_id: Project ID
            prompt: User's text prompt
            audio_settings: Audio generation settings
            video_settings: Video generation settings
            
        Returns:
            Workflow status and job IDs
        """
        logger.info(f"[{project_id}] Starting content creation workflow")
        logger.info(f"[{project_id}] Prompt: {prompt[:100]}...")
        
        # Update project status
        self._update_project_status(project_id, "processing", started_at=datetime.utcnow())
        
        try:
            # Step 1: Generate audio (if requested)
            audio_job_id = None
            audio_file = None
            
            if audio_settings:
                logger.info(f"[{project_id}] Step 1: Generating audio...")
                audio_result = await self._generate_audio(project_id, prompt, audio_settings)
                audio_job_id = audio_result.get("job_id")
                
                # Wait for audio completion
                audio_file = await self._wait_for_audio(audio_job_id)
                logger.info(f"[{project_id}] Audio generation completed: {audio_file}")
            
            # Step 2: Generate video (if requested)
            video_job_id = None
            video_file = None
            
            if video_settings:
                logger.info(f"[{project_id}] Step 2: Generating video...")
                
                if audio_file:
                    # Generate video with audio sync
                    video_result = await self._generate_video_with_audio(
                        project_id, prompt, audio_file, video_settings
                    )
                else:
                    # Generate video only
                    video_result = await self._generate_video(
                        project_id, prompt, video_settings
                    )
                
                video_job_id = video_result.get("job_id")
                
                # Wait for video completion
                video_file = await self._wait_for_video(video_job_id)
                logger.info(f"[{project_id}] Video generation completed: {video_file}")
            
            # Step 3: Update project with results
            self._update_project_results(
                project_id,
                audio_file=audio_file,
                video_file=video_file,
                final_file=video_file or audio_file
            )
            
            # Mark as completed
            self._update_project_status(
                project_id,
                "completed",
                completed_at=datetime.utcnow()
            )
            
            logger.info(f"[{project_id}] Workflow completed successfully")
            
            return {
                "status": "completed",
                "audio_job_id": audio_job_id,
                "video_job_id": video_job_id,
                "audio_file": audio_file,
                "video_file": video_file
            }
            
        except Exception as e:
            logger.error(f"[{project_id}] Workflow failed: {e}")
            self._update_project_status(
                project_id,
                "failed",
                error_message=str(e)
            )
            raise
    
    async def _generate_audio(
        self,
        project_id: str,
        prompt: str,
        settings: Dict[str, Any]
    ) -> Dict[str, Any]:
        """Call audio service to generate music.
        
        Args:
            project_id: Project ID
            prompt: Text prompt
            settings: Audio settings
            
        Returns:
            Audio job information
        """
        async with httpx.AsyncClient(timeout=self.timeout) as client:
            response = await client.post(
                f"{self.audio_service_url}/generate/music",
                json={
                    "prompt": prompt,
                    "duration": settings.get("duration", 30),
                    "style": settings.get("style"),
                    "temperature": settings.get("temperature", 1.0)
                }
            )
            response.raise_for_status()
            return response.json()
    
    async def _generate_video(
        self,
        project_id: str,
        prompt: str,
        settings: Dict[str, Any]
    ) -> Dict[str, Any]:
        """Call video service to generate video.
        
        Args:
            project_id: Project ID
            prompt: Text prompt
            settings: Video settings
            
        Returns:
            Video job information
        """
        async with httpx.AsyncClient(timeout=self.timeout) as client:
            response = await client.post(
                f"{self.video_service_url}/generate",
                json={
                    "prompt": prompt,
                    "duration": settings.get("duration", 30),
                    "resolution": settings.get("resolution", "1080p"),
                    "fps": settings.get("fps", 30),
                    "style": settings.get("style"),
                    "provider": settings.get("provider", "mock")
                }
            )
            response.raise_for_status()
            return response.json()
    
    async def _generate_video_with_audio(
        self,
        project_id: str,
        prompt: str,
        audio_file: str,
        settings: Dict[str, Any]
    ) -> Dict[str, Any]:
        """Call video service to generate video with audio sync.
        
        Args:
            project_id: Project ID
            prompt: Text prompt
            audio_file: Path to audio file
            settings: Video settings
            
        Returns:
            Video job information
        """
        async with httpx.AsyncClient(timeout=self.timeout) as client:
            response = await client.post(
                f"{self.video_service_url}/generate-with-audio",
                json={
                    "prompt": prompt,
                    "audio_file": audio_file,
                    "resolution": settings.get("resolution", "1080p"),
                    "fps": settings.get("fps", 30),
                    "style": settings.get("style"),
                    "provider": settings.get("provider", "mock")
                }
            )
            response.raise_for_status()
            return response.json()
    
    async def _wait_for_audio(self, job_id: str, max_wait: int = 300) -> str:
        """Wait for audio generation to complete.
        
        Args:
            job_id: Audio job ID
            max_wait: Maximum wait time in seconds
            
        Returns:
            Path to generated audio file
        """
        async with httpx.AsyncClient(timeout=self.timeout) as client:
            start_time = asyncio.get_event_loop().time()
            
            while True:
                # Check if we've exceeded max wait time
                if asyncio.get_event_loop().time() - start_time > max_wait:
                    raise TimeoutError(f"Audio generation exceeded {max_wait}s timeout")
                
                # Poll status
                response = await client.get(f"{self.audio_service_url}/jobs/{job_id}")
                response.raise_for_status()
                status = response.json()
                
                if status["status"] == "completed":
                    return status["result"]["audio_file"]
                elif status["status"] == "failed":
                    raise Exception(f"Audio generation failed: {status.get('error')}")
                
                # Wait before next poll
                await asyncio.sleep(2)
    
    async def _wait_for_video(self, job_id: str, max_wait: int = 600) -> str:
        """Wait for video generation to complete.
        
        Args:
            job_id: Video job ID
            max_wait: Maximum wait time in seconds
            
        Returns:
            Path to generated video file
        """
        async with httpx.AsyncClient(timeout=self.timeout) as client:
            start_time = asyncio.get_event_loop().time()
            
            while True:
                if asyncio.get_event_loop().time() - start_time > max_wait:
                    raise TimeoutError(f"Video generation exceeded {max_wait}s timeout")
                
                response = await client.get(
                    f"{self.video_service_url}/jobs/{job_id}",
                    params={"provider": "mock"}
                )
                response.raise_for_status()
                status = response.json()
                
                if status["status"] == "completed":
                    return status["video_file"]
                elif status["status"] == "failed":
                    raise Exception(f"Video generation failed: {status.get('error')}")
                
                await asyncio.sleep(2)
    
    def _update_project_status(
        self,
        project_id: str,
        status: str,
        started_at: Optional[datetime] = None,
        completed_at: Optional[datetime] = None,
        error_message: Optional[str] = None
    ):
        """Update project status in database.
        
        Args:
            project_id: Project ID
            status: New status
            started_at: Start timestamp
            completed_at: Completion timestamp
            error_message: Error message if failed
        """
        db = SessionLocal()
        try:
            project = db.query(Project).filter(Project.id == project_id).first()
            if project:
                project.status = status
                if started_at:
                    project.started_at = started_at
                if completed_at:
                    project.completed_at = completed_at
                if error_message:
                    project.error_message = error_message
                db.commit()
        finally:
            db.close()
    
    def _update_project_results(
        self,
        project_id: str,
        audio_file: Optional[str] = None,
        video_file: Optional[str] = None,
        final_file: Optional[str] = None
    ):
        """Update project with generated files.
        
        Args:
            project_id: Project ID
            audio_file: Path to audio file
            video_file: Path to video file
            final_file: Path to final output file
        """
        db = SessionLocal()
        try:
            project = db.query(Project).filter(Project.id == project_id).first()
            if project:
                if audio_file:
                    project.audio_file = audio_file
                if video_file:
                    project.video_file = video_file
                if final_file:
                    project.final_file = final_file
                db.commit()
        finally:
            db.close()


# Singleton instance
_workflow = None


def get_workflow() -> ContentWorkflow:
    """Get singleton workflow instance.
    
    Returns:
        ContentWorkflow instance
    """
    global _workflow
    if _workflow is None:
        _workflow = ContentWorkflow()
    return _workflow
