Expected indented blockPylance, from moviepy.editor import VideoFileClip, concatenate_videoclips, TextClip
from moviepy.config import change_settings
import os
import time
import tomlkit
import threading
import json
from rich.console import Console
from rich.progress import Progress, SpinnerColumn, TextColumn, BarColumn, TimeElapsedColumn
from rich.table import Table

console = Console()

# Set ImageMagick path
change_settings({"IMAGEMAGICK_BINARY": r"C:\Program Files\ImageMagick-7.1.1-Q16-HDRI\magick.exe"})

def print_step(text):
    console.print(f"\n[bold blue]►[/bold blue] {text}")

def print_substep(text, success=None):
    if success is True:
        console.print(f"[bold green]✓[/bold green] {text}")
    elif success is False:
        console.print(f"[bold red]✗[/bold red] {text}")
    else:
        console.print(f"[bright_black]⧗[/bright_black] {text}")

def print_video_summary(videos):
    table = Table(title="Selected Videos Summary")
    table.add_column("Order", style="cyan")
    table.add_column("Video Name", style="green")
    table.add_column("Duration", style="yellow")
    

    total_duration = 0
    for i, video in enumerate(videos, 1):
        table.add_row(
            str(i),
            video['name'],
            f"{video['duration']:.2f}s",
            
        )
        total_duration += video['duration']

    console.print(table)
    console.print(f"\n[yellow]Total Duration: {total_duration:.2f}s[/yellow]")

class VideoConcatenator:
    def __init__(self):
        self.config = self.load_config()
        self.source_folder = self.config['paths']['source_folder']
        self.merge_folder = self.config['paths']['merge_folder']
        self.script_info_folder = os.path.join(self.merge_folder, "Script_Info")
        self.target_duration = float(self.config['merger']['target_duration']) * 60
        self.check_interval = self.config['merger']['check_interval']
        self.mode = self.config['merger']['mode']
        self.specific_subreddit = self.config['merger']['specific_subreddit']
        self.status = self.config['merger']['status']
        self.video_metadata = {}

        os.makedirs(self.merge_folder, exist_ok=True)
        os.makedirs(self.script_info_folder, exist_ok=True)
        
        self.processed_videos = self.load_processed_videos()
        
        print_step("Initializing Video Concatenator")
        print_substep(f"Mode: {self.mode}", True)
        print_substep(f"Monitoring: {self.source_folder}", True)
        print_substep(f"Target duration: {self.target_duration} seconds ({self.config['merger']['target_duration']} minutes)", True)
        if self.mode == "specific":
            print_substep(f"Specific subreddits: {self.specific_subreddit}", True)

    def load_config(self):
        try:
            with open("config.toml", "r", encoding="utf-8") as f:
                config = tomlkit.load(f)
                print_substep("Configuration loaded successfully", True)
                return config
        except FileNotFoundError:
            print_substep("Error: config.toml not found!", False)
            exit(1)

    def load_processed_videos(self):
        try:
            processed_videos_path = os.path.join(self.script_info_folder, 'processed_videos.json')
            if os.path.exists(processed_videos_path):
                with open(processed_videos_path, 'r') as f:
                    return json.load(f)
            return []
        except Exception:
            return []

    def save_processed_videos(self):
        try:
            processed_videos_path = os.path.join(self.script_info_folder, 'processed_videos.json')
            with open(processed_videos_path, 'w') as f:
                json.dump(self.processed_videos, f)
        except Exception as e:
            print_substep(f"Error saving processed videos: {str(e)}", False)

    def extract_metadata(self, video_path):
    try:
        # Get filename and remove _out.mp4
        filename = os.path.basename(video_path)
        basename = os.path.splitext(filename)[0].replace('_out', '')
        
        # Look for the last '/' in the name to separate title and ID
        if '/' in basename:
            title, post_id = basename.rsplit('/', 1)  # Split on last '/'
            # Store metadata with combined title/id for URL
            self.video_metadata[video_path] = {
                "title": title,
                "url": f"https://reddit.com/comments/{title}/{post_id}"
            }
        else:
            # If no separator found, use whole name
            self.video_metadata[video_path] = {
                "title": basename,
                "url": f"https://reddit.com/comments/{basename}"
            }
        return True
    except Exception as e:
        print_substep(f"Error extracting metadata: {str(e)}", False)
        return False

    def save_video_metadata(self, compilation_id: str, videos: list):
        metadata_db_path = os.path.join(self.script_info_folder, "video_metadata.json")
        metadata = {}
        
        if os.path.exists(metadata_db_path):
            with open(metadata_db_path, 'r') as f:
                metadata = json.load(f)
        
        metadata[compilation_id] = {
            'timestamp': time.strftime('%Y%m%d_%H%M%S'),
            'sources': []
        }
        
        for video in videos:
            if video['path'] in self.video_metadata:
                metadata[compilation_id]['sources'].append(self.video_metadata[video['path']])
        
        # Save metadata JSON
        with open(metadata_db_path, 'w') as f:
            json.dump(metadata, f, indent=2)
        
        # Create description file
        desc_path = os.path.join(self.script_info_folder, f"description_{compilation_id}.txt")
        with open(desc_path, 'w') as f:
            for video in videos:
                if video['path'] in self.video_metadata:
                    data = self.video_metadata[video['path']]
                    f.write(f"{data['title']}\n")
                    f.write(f'Source: "{data["url"]}"\n\n')

    def check_status(self):
        self.config = self.load_config()
        status = self.config['merger']['status']
        if status == "paused":
            print_step("⏸️ Processing Paused")
            with Progress(
                SpinnerColumn(),
                TextColumn("[yellow]Waiting for resume signal..."),
                refresh_per_second=4
            ) as progress:
                task = progress.add_task("", total=None)
                while self.config['merger']['status'] == "paused":
                    progress.update(task)
                    time.sleep(1)
                    self.config = self.load_config()
            print_step("▶️ Processing Resumed")
            return True
        elif status == "stopping":
            print_step("🛑 Stop Signal Received")
            return False
        return True

    def get_video_duration(self, video_path):
        try:
            clip = VideoFileClip(video_path, audio=False)
            duration = clip.duration
            clip.close()
            print_substep(f"Duration for {os.path.basename(video_path)}: {duration:.2f}s", True)
            return duration
        except Exception as e:
            print_substep(f"Error getting duration for {os.path.basename(video_path)}: {str(e)}", False)
            return 0

    def find_videos(self):
        videos = []
        total_duration = 0
        print_step("🔍 Scanning for Videos")

        if self.mode == "specific":
            subreddits = self.specific_subreddit.split("+")
        else:
            subreddits = os.listdir(self.source_folder)

        with Progress(
            SpinnerColumn(),
            TextColumn("[progress.description]{task.description}"),
            BarColumn(),
            TextColumn("[progress.percentage]{task.percentage:>3.0f}%"),
            TimeElapsedColumn(),
        ) as progress:
            scan_task = progress.add_task("Scanning subreddits...", total=len(subreddits))

            for subreddit in subreddits:
                progress.update(scan_task, description=f"Scanning r/{subreddit}")
                subreddit_path = os.path.join(self.source_folder, subreddit)

                if os.path.isdir(subreddit_path):
                    for file in os.listdir(subreddit_path):
                        if file.endswith('_out.mp4'):
                            video_path = os.path.join(subreddit_path, file)
                            if video_path in self.processed_videos:
                                print_substep(f"Skipping previously processed: {file}")
                                continue

                            duration = self.get_video_duration(video_path)
                            if duration > 0 and self.extract_metadata(video_path):
                                videos.append({
                                    'path': video_path,
                                    'name': file,
                                    'duration': duration
                                })
                                total_duration += duration

                progress.advance(scan_task)

        print_step("📊 Scan Results")
        print_substep(f"New videos found: {len(videos)}", True if videos else False)
        print_substep(f"Total duration available: {total_duration:.2f}s", True if total_duration > 0 else False)
        return videos

    def select_videos(self, available_videos):
        if not available_videos:
            print_substep("No videos available for selection", False)
            return None

        selected_videos = []
        current_duration = 0
        print_step("🎯 Selecting Videos")

        if self.mode == "fastest":
            available_videos.sort(key=lambda x: x['duration'], reverse=True)
            print_substep("Sorted videos by duration (fastest mode)", True)

        with Progress(
            SpinnerColumn(),
            TextColumn("[progress.description]{task.description}"),
            BarColumn(),
            TextColumn("{task.completed}/{task.total} videos"),
        ) as progress:
            selection_task = progress.add_task("Selecting videos...", total=len(available_videos))
            processed = 0

            # Keep adding videos until we exceed the threshold
            for video in available_videos:
                selected_videos.append(video)
                current_duration += video['duration']
                processed += 1
                progress.update(selection_task, completed=processed,
                              description=f"Added: {video['name']} (Total: {current_duration:.2f}s)")

                # Stop once we've exceeded the threshold
                if current_duration >= self.target_duration:
                    print_substep(f"Target duration met with {len(selected_videos)} videos", True)
                    break

            # Complete the progress bar
            progress.update(selection_task, completed=len(available_videos))

        print_step("📝 Selection Results")
        print_substep(f"Videos selected: {len(selected_videos)}", True if selected_videos else False)
        print_substep(f"Total duration: {current_duration:.2f}s / {self.target_duration:.2f}s", 
                     True if current_duration >= self.target_duration else False)

        # Display summary table of selected videos
        if selected_videos:
            print_step("📋 Selected Videos Summary")
            print_video_summary(selected_videos)

        # Only return if we met the minimum duration
        if current_duration < self.target_duration:
            print_substep("Could not meet minimum duration requirement", False)
            return None

        return selected_videos

    def write_video_with_progress(self, clip, output_path):
        total_frames = int(clip.duration * clip.fps)
        frame_count = [0]

        with Progress(
            SpinnerColumn(),
            TextColumn("[progress.description]{task.description}"),
            BarColumn(),
            TextColumn("[progress.percentage]{task.percentage:>3.0f}%"),
            TimeElapsedColumn(),
        ) as progress:
            render_task = progress.add_task("Rendering video...", total=total_frames)

            def progress_monitor():
                while frame_count[0] < total_frames:
                    time.sleep(0.5)
                    if os.path.exists(output_path):
                        estimated_progress = min(frame_count[0] + 1, total_frames)
                        progress.update(render_task, completed=estimated_progress)
                        frame_count[0] += 1

            monitor_thread = threading.Thread(target=progress_monitor)
            monitor_thread.daemon = True
            monitor_thread.start()

            clip.write_videofile(
                output_path,
                codec='libx264',
                audio_codec='aac',
                temp_audiofile='temp-audio.m4a',
                remove_temp=True,
                threads=2,
                preset='superfast',
                bitrate='4000k',
                verbose=False,
                logger=None,
                write_logfile=False
            )

            frame_count[0] = total_frames
            progress.update(render_task, completed=total_frames)
            monitor_thread.join(timeout=1)

    def concatenate_videos(self, selected_videos):
        if not selected_videos:
            print_substep("No videos selected for concatenation", False)
            return None

        clips = []
        try:
            print_step("🎬 Starting Video Concatenation")

            with Progress(
                SpinnerColumn(),
                TextColumn("[progress.description]{task.description}"),
                BarColumn(),
                TextColumn("{task.completed}/{task.total}"),
            ) as progress:
                load_task = progress.add_task("Loading videos...", total=len(selected_videos))

                # Create transition clip - 2 seconds long
                transition = TextClip(
                    "Next Story...",
                    fontsize=70,
                    color='black',
                    bg_color='white',
                    font='Arial',
                    size=(1080, 1920),
                    method='caption',
                    align='center'
                ).set_position(('center', 'center')).set_duration(2)

                for i, video in enumerate(selected_videos):
                    progress.update(load_task, description=f"Loading: {video['name']}")
                    try:
                        clip = VideoFileClip(video['path'], verbose=False)
                        if clip.duration > 0:
                            if i > 0:  # Add transition before each video except the first
                                clips.append(transition)
                            clips.append(clip)
                        progress.advance(load_task)
                    except Exception as e:
                        print_substep(f"Error loading {video['name']}: {str(e)}", False)
                        continue

            if not clips:
                print_substep("No valid clips to concatenate", False)
                return None

            print_substep("All videos loaded successfully", True)

            output_filename = f"compilation_{time.strftime('%Y%m%d_%H%M%S')}.mp4"
            output_path = os.path.join(self.merge_folder, output_filename)

            print_step("💾 Creating Final Video")

            # Save metadata before processing
            compilation_id = os.path.splitext(output_filename)[0]
            self.save_video_metadata(compilation_id, selected_videos)

            # Mark videos as processed before rendering
            for video in selected_videos:
                self.processed_videos.append(video['path'])
            self.save_processed_videos()

            try:
                final_clip = concatenate_videoclips(clips, method='chain')
                self.write_video_with_progress(final_clip, output_path)
                print_substep(f"Compilation saved: {output_filename}", True)
                return output_path
            except Exception as e:
                print_substep("First concatenation method failed, trying alternative...", False)
                try:
                    final_clip = concatenate_videoclips(clips, method='compose')
                    self.write_video_with_progress(final_clip, output_path)
                    print_substep(f"Compilation saved: {output_filename}", True)
                    return output_path
                except Exception as e:
                    print_substep(f"Error during concatenation: {str(e)}", False)
                    return None

        except Exception as e:
            print_substep(f"Error during concatenation: {str(e)}", False)
            return None
        finally:
            for clip in clips:
                try:
                    clip.close()
                except:
                    pass

    def monitor_and_process(self):
        print_step("🎥 Starting Video Monitor")
        print_substep("Monitoring for new videos...")

        while True:
            try:
                # Reload config to check for updated values
                self.config = self.load_config()
                self.target_duration = float(self.config['merger']['target_duration']) * 60

                if not self.check_status():
                    break

                available_videos = self.find_videos()
                if available_videos:
                    selected_videos = self.select_videos(available_videos)
                    if selected_videos:
                        output_path = self.concatenate_videos(selected_videos)
                        if output_path:
                            print_step("✨ Compilation Complete")
                            print_substep(f"Created: {os.path.basename(output_path)}", True)

                print_step(f"⏳ Waiting {self.check_interval} minutes")
                with Progress(
                    SpinnerColumn(),
                    TextColumn("[yellow]Next check in..."),
                    TimeElapsedColumn(),
                ) as progress:
                    wait_task = progress.add_task("", total=None)
                    for _ in range(self.check_interval * 60):
                        progress.update(wait_task)
                        time.sleep(1)

            except KeyboardInterrupt:
                print_step("👋 Stopping Monitor")
                break
            except Exception as e:
                print_step("❌ Error Occurred")
                print_substep(f"Error: {str(e)}", False)
                print_substep(f"Retrying in {self.check_interval} minutes", True)
                time.sleep(self.check_interval * 60)

def main():
    console.print("\n[bold cyan]🎥 Video Concatenation Monitor[/bold cyan]")
    console.print("[cyan]━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━[/cyan]")
    print_substep("⚙️ You can modify config.toml at any time to change settings")
    print_substep("⏯️ Set status to 'paused', 'running', or 'stopping'")
    print_substep("🔄 Monitor will check for new videos automatically")
    console.print("[cyan]━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━[/cyan]\n")

    concatenator = VideoConcatenator()

    # Example of how to reset specific videos - uncomment and modify as needed
    # concatenator.remove_from_processed([
    #     "AITA for leaving in the middle of Thanksgiving dinner because of pumpkin pie_out.mp4",
    #     "I live too much in my imagination that it freaks me out_out.mp4"
    # ])

    concatenator.monitor_and_process()

if __name__ == "__main__":
    main()