"""
Video Generator module using Manim for creating educational DSA explanation videos
"""

import os
import re
import shutil
import tempfile
import subprocess
from pathlib import Path
from typing import Optional, Dict, Any, List
from datetime import datetime

from pragyan.models import Question, Solution, VideoConfig, AnimationScene


class VideoGenerator:
    """
    Generates educational videos explaining DSA problems and solutions using Manim
    """
    
    def __init__(self, config: Optional[VideoConfig] = None):
        """
        Initialize the video generator
        
        Args:
            config: Video configuration options
        """
        self.config = config or VideoConfig()
        self._temp_dir = None
        self._verify_manim_installation()
    
    def _verify_manim_installation(self):
        """Verify that Manim is properly installed"""
        try:
            import manim
            self.manim_version = manim.__version__
        except ImportError:
            raise ImportError(
                "Manim is not installed. Please install it with: pip install manim\n"
                "You may also need to install additional dependencies. "
                "See: https://docs.manim.community/en/stable/installation.html"
            )
    
    def _get_temp_dir(self) -> Path:
        """Get or create temporary directory for rendering"""
        if self._temp_dir is None:
            self._temp_dir = Path(tempfile.mkdtemp(prefix="pragyan_"))
        return self._temp_dir
    
    def _cleanup_temp_dir(self):
        """Clean up temporary directory"""
        if self._temp_dir and self._temp_dir.exists():
            shutil.rmtree(self._temp_dir, ignore_errors=True)
            self._temp_dir = None
    
    def generate_video(
        self,
        question: Question,
        solution: Solution,
        analysis: Dict[str, Any],
        video_script: List[Dict[str, Any]],
        output_filename: Optional[str] = None
    ) -> Path:
        """
        Generate a complete explanation video
        
        Args:
            question: The DSA question
            solution: The generated solution
            analysis: Question analysis
            video_script: List of scene descriptions
            output_filename: Optional custom output filename
            
        Returns:
            Path to the generated video file
        """
        temp_dir = self._get_temp_dir()
        
        # Generate Manim scene code
        scene_code = self._generate_complete_scene(question, solution, analysis, video_script)
        
        # Write scene to file
        scene_file = temp_dir / "dsa_explanation.py"
        with open(scene_file, "w", encoding="utf-8") as f:
            f.write(scene_code)
        
        # Render the video
        output_path = self._render_video(scene_file, output_filename)
        
        return output_path
    
    def _generate_complete_scene(
        self,
        question: Question,
        solution: Solution,
        analysis: Dict[str, Any],
        video_script: List[Dict[str, Any]]
    ) -> str:
        """Generate complete Manim scene code"""
        
        # Escape strings for Python code
        def escape_str(s: str) -> str:
            if not s:
                return ""
            return s.replace('\\', '\\\\').replace('"', '\\"').replace('\n', '\\n').replace('\r', '')
        
        title = escape_str(question.title)
        concept = escape_str(solution.concept)
        approach = escape_str(solution.approach)
        code = escape_str(solution.code)
        time_comp = escape_str(solution.time_complexity)
        space_comp = escape_str(solution.space_complexity)
        explanation = escape_str(solution.explanation[:500] if solution.explanation else "")
        walkthrough = escape_str(solution.example_walkthrough[:800] if solution.example_walkthrough else "")
        
        # Get topics
        topics = analysis.get("topics", [])
        if isinstance(topics, list):
            topics_str = ", ".join(topics[:5])
        else:
            topics_str = str(topics)
        topics_str = escape_str(topics_str)
        
        # Generate step-by-step
        steps = solution.step_by_step[:6] if solution.step_by_step else []
        steps_code = "[\n"
        for step in steps:
            steps_code += f'            "{escape_str(step)}",\n'
        steps_code += "        ]"
        
        scene_code = f'''"""
Manim scene for DSA explanation video
Generated by Pragyan
"""

from manim import *
import textwrap

# Configuration
config.pixel_height = {self.config.pixel_height}
config.pixel_width = {self.config.pixel_width}
config.frame_rate = {self.config.fps}
config.background_color = "{self.config.background_color}"


class DSAExplanation(Scene):
    """Complete DSA explanation video scene"""
    
    def construct(self):
        """Build the complete video"""
        self.camera.background_color = "{self.config.background_color}"
        
        # Scene data
        title = "{title}"
        concept = "{concept}"
        approach = "{approach}"
        code_text = """{code}"""
        time_complexity = "{time_comp}"
        space_complexity = "{space_comp}"
        explanation = "{explanation}"
        walkthrough = "{walkthrough}"
        topics = "{topics_str}"
        steps = {steps_code}
        
        # Scene 1: Introduction
        self.intro_scene(title, topics)
        
        # Scene 2: Problem Overview
        self.problem_scene(title, explanation)
        
        # Scene 3: Concept Explanation
        self.concept_scene(concept, approach)
        
        # Scene 4: Step by Step Approach
        self.steps_scene(steps)
        
        # Scene 5: Code Walkthrough
        self.code_scene(code_text)
        
        # Scene 6: Example Walkthrough
        self.example_scene(walkthrough)
        
        # Scene 7: Complexity Analysis
        self.complexity_scene(time_complexity, space_complexity)
        
        # Scene 8: Outro
        self.outro_scene(title)
    
    def intro_scene(self, title: str, topics: str):
        """Introduction scene with title and topics"""
        # Title
        title_text = Text(
            self.wrap_text(title, 40),
            font_size=48,
            color=BLUE
        ).to_edge(UP, buff=1.5)
        
        # Subtitle
        subtitle = Text(
            "DSA Problem Explained",
            font_size=32,
            color=WHITE
        ).next_to(title_text, DOWN, buff=0.5)
        
        # Topics
        if topics:
            topics_text = Text(
                f"Topics: {{topics}}",
                font_size=24,
                color=YELLOW
            ).next_to(subtitle, DOWN, buff=0.5)
        
        # Animations
        self.play(Write(title_text), run_time=1.5)
        self.play(FadeIn(subtitle, shift=UP), run_time=1)
        if topics:
            self.play(FadeIn(topics_text, shift=UP), run_time=0.8)
        
        self.wait(2)
        self.play(FadeOut(VGroup(*self.mobjects)), run_time=0.8)
    
    def problem_scene(self, title: str, explanation: str):
        """Problem overview scene"""
        # Header
        header = Text(
            "Problem Overview",
            font_size=40,
            color=GREEN
        ).to_edge(UP, buff=0.5)
        
        # Problem title
        problem_title = Text(
            self.wrap_text(title, 50),
            font_size=28,
            color=WHITE
        ).next_to(header, DOWN, buff=0.8)
        
        # Explanation
        exp_text = Text(
            self.wrap_text(explanation[:300], 60),
            font_size=20,
            color=GRAY_A
        ).next_to(problem_title, DOWN, buff=0.5)
        
        self.play(Write(header), run_time=0.8)
        self.play(FadeIn(problem_title, shift=DOWN), run_time=1)
        self.play(FadeIn(exp_text), run_time=1.5)
        
        self.wait(3)
        self.play(FadeOut(VGroup(*self.mobjects)), run_time=0.8)
    
    def concept_scene(self, concept: str, approach: str):
        """Explain the main concept"""
        # Header
        header = Text(
            "Key Concept",
            font_size=40,
            color=ORANGE
        ).to_edge(UP, buff=0.5)
        
        # Concept box
        concept_box = Rectangle(
            width=10,
            height=2,
            color=ORANGE,
            fill_opacity=0.1
        ).next_to(header, DOWN, buff=0.8)
        
        concept_text = Text(
            self.wrap_text(concept, 45),
            font_size=28,
            color=WHITE
        ).move_to(concept_box.get_center())
        
        # Approach
        approach_header = Text(
            "Approach:",
            font_size=28,
            color=YELLOW
        ).next_to(concept_box, DOWN, buff=0.8)
        
        approach_text = Text(
            self.wrap_text(approach[:250], 55),
            font_size=22,
            color=GRAY_A
        ).next_to(approach_header, DOWN, buff=0.3)
        
        self.play(Write(header), run_time=0.8)
        self.play(Create(concept_box), Write(concept_text), run_time=1.2)
        self.play(FadeIn(approach_header), FadeIn(approach_text), run_time=1.5)
        
        self.wait(4)
        self.play(FadeOut(VGroup(*self.mobjects)), run_time=0.8)
    
    def steps_scene(self, steps: list):
        """Show step-by-step approach"""
        if not steps:
            return
        
        # Header
        header = Text(
            "Step-by-Step Approach",
            font_size=40,
            color=PURPLE
        ).to_edge(UP, buff=0.5)
        
        self.play(Write(header), run_time=0.8)
        
        # Show steps one by one
        step_group = VGroup()
        start_y = 2
        
        for i, step in enumerate(steps[:5]):
            step_text = Text(
                self.wrap_text(f"{{i+1}}. {{step}}", 55),
                font_size=22,
                color=WHITE
            ).move_to([0, start_y - i * 0.9, 0])
            
            step_group.add(step_text)
            
            # Number highlight
            num_circle = Circle(
                radius=0.25,
                color=PURPLE,
                fill_opacity=0.3
            ).move_to(step_text.get_left() + LEFT * 0.3)
            
            self.play(
                GrowFromCenter(num_circle),
                Write(step_text),
                run_time=0.8
            )
            self.wait(0.5)
        
        self.wait(2)
        self.play(FadeOut(VGroup(*self.mobjects)), run_time=0.8)
    
    def code_scene(self, code_text: str):
        """Show the solution code"""
        # Header
        header = Text(
            "Solution Code",
            font_size=40,
            color=GREEN
        ).to_edge(UP, buff=0.3)
        
        # Code display (limit lines for readability)
        code_lines = code_text.split("\\\\n")[:20]
        display_code = "\\n".join(code_lines)
        
        code = Code(
            code=display_code,
            language="python",
            font_size=14,
            background="rectangle",
            background_stroke_color=GREEN,
            line_spacing=0.6,
        ).scale(0.7).next_to(header, DOWN, buff=0.4)
        
        self.play(Write(header), run_time=0.8)
        self.play(FadeIn(code), run_time=1.5)
        
        # Highlight effect
        highlight = SurroundingRectangle(code, color=YELLOW, buff=0.1)
        self.play(Create(highlight), run_time=0.5)
        self.play(FadeOut(highlight), run_time=0.5)
        
        self.wait(4)
        self.play(FadeOut(VGroup(*self.mobjects)), run_time=0.8)
    
    def example_scene(self, walkthrough: str):
        """Example walkthrough scene"""
        if not walkthrough:
            return
        
        # Header
        header = Text(
            "Example Walkthrough",
            font_size=40,
            color=TEAL
        ).to_edge(UP, buff=0.5)
        
        # Walkthrough text
        walk_text = Text(
            self.wrap_text(walkthrough[:400], 55),
            font_size=20,
            color=WHITE
        ).next_to(header, DOWN, buff=0.8)
        
        self.play(Write(header), run_time=0.8)
        self.play(Write(walk_text), run_time=3)
        
        self.wait(4)
        self.play(FadeOut(VGroup(*self.mobjects)), run_time=0.8)
    
    def complexity_scene(self, time_comp: str, space_comp: str):
        """Complexity analysis scene"""
        # Header
        header = Text(
            "Complexity Analysis",
            font_size=40,
            color=RED
        ).to_edge(UP, buff=0.5)
        
        # Time complexity box
        time_box = Rectangle(
            width=5,
            height=2,
            color=BLUE,
            fill_opacity=0.1
        ).move_to([-2.5, 0.5, 0])
        
        time_label = Text("Time", font_size=24, color=BLUE).move_to(time_box.get_top() + UP * 0.3)
        time_text = Text(
            self.wrap_text(time_comp[:50], 25),
            font_size=20,
            color=WHITE
        ).move_to(time_box.get_center())
        
        # Space complexity box
        space_box = Rectangle(
            width=5,
            height=2,
            color=GREEN,
            fill_opacity=0.1
        ).move_to([2.5, 0.5, 0])
        
        space_label = Text("Space", font_size=24, color=GREEN).move_to(space_box.get_top() + UP * 0.3)
        space_text = Text(
            self.wrap_text(space_comp[:50], 25),
            font_size=20,
            color=WHITE
        ).move_to(space_box.get_center())
        
        self.play(Write(header), run_time=0.8)
        self.play(
            Create(time_box), Write(time_label), Write(time_text),
            Create(space_box), Write(space_label), Write(space_text),
            run_time=1.5
        )
        
        self.wait(3)
        self.play(FadeOut(VGroup(*self.mobjects)), run_time=0.8)
    
    def outro_scene(self, title: str):
        """Outro scene"""
        # Thank you
        thanks = Text(
            "Solution Complete!",
            font_size=48,
            color=GOLD
        ).move_to(ORIGIN)
        
        # Problem name
        problem = Text(
            self.wrap_text(title, 45),
            font_size=28,
            color=WHITE
        ).next_to(thanks, DOWN, buff=0.5)
        
        # Generated by
        generated = Text(
            "Generated by Pragyan",
            font_size=20,
            color=GRAY
        ).to_edge(DOWN, buff=0.5)
        
        self.play(GrowFromCenter(thanks), run_time=1)
        self.play(FadeIn(problem), run_time=0.8)
        self.play(FadeIn(generated), run_time=0.5)
        
        self.wait(2)
        self.play(FadeOut(VGroup(*self.mobjects)), run_time=1)
    
    @staticmethod
    def wrap_text(text: str, width: int) -> str:
        """Wrap text to specified width"""
        if not text:
            return ""
        import textwrap
        return "\\n".join(textwrap.wrap(text, width))


# Additional scene for more detailed animations
class CodeWalkthrough(Scene):
    """Detailed code walkthrough with line-by-line highlighting"""
    
    def construct(self):
        pass  # To be implemented with LLM-generated content


class AlgorithmVisualization(Scene):
    """Algorithm visualization with data structure animations"""
    
    def construct(self):
        pass  # To be implemented with LLM-generated content
'''
        
        return scene_code
    
    def _render_video(self, scene_file: Path, output_filename: Optional[str] = None) -> Path:
        """
        Render the Manim scene to video
        
        Args:
            scene_file: Path to the Python file containing the scene
            output_filename: Optional custom output filename
            
        Returns:
            Path to the rendered video
        """
        # Determine quality flag
        quality_map = {
            "low_quality": "-ql",
            "medium_quality": "-qm",
            "high_quality": "-qh",
            "production_quality": "-qp",
        }
        quality_flag = quality_map.get(self.config.video_quality, "-qm")
        
        # Build command
        cmd = [
            "manim",
            quality_flag,
            str(scene_file),
            "DSAExplanation",
            "--media_dir", str(self._get_temp_dir() / "media"),
        ]
        
        # Run Manim
        try:
            result = subprocess.run(
                cmd,
                capture_output=True,
                text=True,
                cwd=str(scene_file.parent),
                timeout=300  # 5 minute timeout
            )
            
            if result.returncode != 0:
                error_msg = result.stderr or result.stdout
                raise RuntimeError(f"Manim rendering failed: {error_msg}")
            
        except subprocess.TimeoutExpired:
            raise RuntimeError("Video rendering timed out after 5 minutes")
        except FileNotFoundError:
            raise RuntimeError(
                "Manim command not found. Make sure Manim is installed and in PATH."
            )
        
        # Find the rendered video
        media_dir = self._get_temp_dir() / "media" / "videos"
        video_files = list(media_dir.rglob("*.mp4"))
        
        if not video_files:
            raise RuntimeError("No video file was generated")
        
        # Get the most recent video
        rendered_video = max(video_files, key=lambda p: p.stat().st_mtime)
        
        # Determine output path
        if output_filename is None:
            timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
            output_filename = f"pragyan_dsa_solution_{timestamp}.mp4"
        
        output_path = self.config.output_dir / output_filename
        
        # Ensure output directory exists
        output_path.parent.mkdir(parents=True, exist_ok=True)
        
        # Copy to output location
        shutil.copy2(rendered_video, output_path)
        
        # Cleanup
        self._cleanup_temp_dir()
        
        return output_path
    
    def generate_quick_preview(
        self,
        question: Question,
        solution: Solution,
        duration: int = 30
    ) -> Path:
        """
        Generate a quick preview video (shorter duration, lower quality)
        
        Args:
            question: The DSA question
            solution: The generated solution
            duration: Target duration in seconds
            
        Returns:
            Path to the preview video
        """
        # Use low quality for preview
        original_quality = self.config.video_quality
        self.config.video_quality = "low_quality"
        
        try:
            # Generate with minimal scenes
            preview_script = [
                {"scene_type": "intro", "title": "Preview"},
                {"scene_type": "code", "title": "Solution"},
            ]
            
            analysis = {"topics": [], "main_concept": solution.concept}
            
            return self.generate_video(
                question, solution, analysis, preview_script,
                output_filename="preview.mp4"
            )
        finally:
            self.config.video_quality = original_quality


class SimpleVideoGenerator:
    """
    Simplified video generator using MoviePy for basic video creation
    Fallback when Manim is not available or for simpler videos
    """
    
    def __init__(self, config: Optional[VideoConfig] = None):
        self.config = config or VideoConfig()
    
    def generate_slideshow(
        self,
        question: Question,
        solution: Solution,
        analysis: Dict[str, Any]
    ) -> Path:
        """
        Generate a simple slideshow-style video
        
        Args:
            question: The DSA question
            solution: The solution
            analysis: Question analysis
            
        Returns:
            Path to the generated video
        """
        try:
            from moviepy.editor import (
                TextClip, CompositeVideoClip, concatenate_videoclips,
                ColorClip
            )
        except ImportError:
            raise ImportError("MoviePy not installed. Install with: pip install moviepy")
        
        clips = []
        duration_per_slide = 5
        
        # Background
        bg_color = tuple(int(self.config.background_color.lstrip('#')[i:i+2], 16) for i in (0, 2, 4))
        
        # Slide 1: Title
        title_clip = self._create_text_slide(
            question.title,
            "DSA Problem Solution",
            duration_per_slide,
            bg_color
        )
        clips.append(title_clip)
        
        # Slide 2: Concept
        concept_clip = self._create_text_slide(
            "Concept",
            solution.concept,
            duration_per_slide,
            bg_color
        )
        clips.append(concept_clip)
        
        # Slide 3: Approach
        approach_clip = self._create_text_slide(
            "Approach",
            solution.approach[:300],
            duration_per_slide + 2,
            bg_color
        )
        clips.append(approach_clip)
        
        # Slide 4: Complexity
        complexity_clip = self._create_text_slide(
            "Complexity",
            f"Time: {solution.time_complexity}\nSpace: {solution.space_complexity}",
            duration_per_slide,
            bg_color
        )
        clips.append(complexity_clip)
        
        # Combine clips
        final_video = concatenate_videoclips(clips, method="compose")
        
        # Output path
        timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
        output_path = self.config.output_dir / f"pragyan_solution_{timestamp}.mp4"
        output_path.parent.mkdir(parents=True, exist_ok=True)
        
        # Write video
        final_video.write_videofile(
            str(output_path),
            fps=self.config.fps,
            codec="libx264",
            audio=False,
            verbose=False,
            logger=None
        )
        
        return output_path
    
    def _create_text_slide(
        self,
        title: str,
        content: str,
        duration: float,
        bg_color: tuple
    ):
        """Create a text slide"""
        from moviepy.editor import TextClip, CompositeVideoClip, ColorClip
        
        # Background
        bg = ColorClip(
            size=(self.config.pixel_width, self.config.pixel_height),
            color=bg_color,
            duration=duration
        )
        
        # Title
        title_clip = TextClip(
            title,
            fontsize=60,
            color='white',
            font='Arial-Bold',
            method='caption',
            size=(self.config.pixel_width - 100, None)
        ).set_position(('center', 100)).set_duration(duration)
        
        # Content
        content_clip = TextClip(
            content,
            fontsize=36,
            color='lightgray',
            font='Arial',
            method='caption',
            size=(self.config.pixel_width - 150, None)
        ).set_position(('center', 'center')).set_duration(duration)
        
        return CompositeVideoClip([bg, title_clip, content_clip])
