#!/usr/bin/env python3
"""
YouTube Channel Archiver - Standalone Windows Executable
Complete self-contained application with dependency management.
"""

import tkinter as tk
from tkinter import ttk, filedialog, messagebox, scrolledtext
import subprocess
import threading
import sys
import os
import platform
from pathlib import Path
import queue
import tempfile

class YouTubeArchiverStandalone:
    def __init__(self, root):
        self.root = root
        self.root.title("YouTube Channel Archiver v1.0")
        self.root.geometry("700x600")
        self.root.resizable(True, True)
        
        # Queue for thread communication
        self.output_queue = queue.Queue()
        
        # Variables
        self.channel_url = tk.StringVar()
        self.output_dir = tk.StringVar(value=str(Path.home() / "Downloads" / "YouTube_Archives"))
        self.quality = tk.StringVar(value="best")
        self.audio_only = tk.BooleanVar()
        self.download_thumbnails = tk.BooleanVar(value=True)
        self.download_metadata = tk.BooleanVar(value=True)
        
        # Track processes
        self.download_process = None
        self.is_downloading = False
        self.dependencies_checked = False
        
        self.create_widgets()
        
        # Check dependencies in background
        self.root.after(1000, self.check_all_dependencies)
        
        # Start checking queue for output updates
        self.root.after(100, self.check_queue)
    
    def create_widgets(self):
        # Main frame with padding
        main_frame = ttk.Frame(self.root, padding="15")
        main_frame.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))
        
        # Configure grid weights
        self.root.columnconfigure(0, weight=1)
        self.root.rowconfigure(0, weight=1)
        main_frame.columnconfigure(1, weight=1)
        
        # Title and info
        title_frame = ttk.Frame(main_frame)
        title_frame.grid(row=0, column=0, columnspan=3, pady=(0, 20))
        
        title_label = ttk.Label(title_frame, text="YouTube Channel Archiver", 
                               font=('Arial', 18, 'bold'))
        title_label.pack()
        
        subtitle_label = ttk.Label(title_frame, text="Download entire YouTube channels with ease", 
                                  font=('Arial', 10))
        subtitle_label.pack()
        
        # Status frame
        status_frame = ttk.LabelFrame(main_frame, text="System Status", padding="10")
        status_frame.grid(row=1, column=0, columnspan=3, sticky=(tk.W, tk.E), pady=(0, 15))
        status_frame.columnconfigure(1, weight=1)
        
        self.python_status = ttk.Label(status_frame, text="Python: Checking...", foreground="orange")
        self.python_status.grid(row=0, column=0, sticky=tk.W, pady=2)
        
        self.ytdlp_status = ttk.Label(status_frame, text="yt-dlp: Checking...", foreground="orange")
        self.ytdlp_status.grid(row=1, column=0, sticky=tk.W, pady=2)
        
        # Channel URL input
        ttk.Label(main_frame, text="YouTube Channel URL:", font=('Arial', 10, 'bold')).grid(
            row=2, column=0, sticky=tk.W, pady=(5, 2))
        
        url_frame = ttk.Frame(main_frame)
        url_frame.grid(row=3, column=0, columnspan=3, sticky=(tk.W, tk.E), pady=(0, 10))
        url_frame.columnconfigure(0, weight=1)
        
        url_entry = ttk.Entry(url_frame, textvariable=self.channel_url, font=('Arial', 10))
        url_entry.grid(row=0, column=0, sticky=(tk.W, tk.E), padx=(0, 10))
        
        paste_btn = ttk.Button(url_frame, text="Paste", command=self.paste_from_clipboard)
        paste_btn.grid(row=0, column=1)
        
        # Examples
        examples_label = ttk.Label(main_frame, 
                                  text="Examples: https://www.youtube.com/@channelname or https://www.youtube.com/c/channelname",
                                  font=('Arial', 8), foreground="gray")
        examples_label.grid(row=4, column=0, columnspan=3, sticky=tk.W)
        
        # Output directory
        ttk.Label(main_frame, text="Download Location:", font=('Arial', 10, 'bold')).grid(
            row=5, column=0, sticky=tk.W, pady=(15, 2))
        
        dir_frame = ttk.Frame(main_frame)
        dir_frame.grid(row=6, column=0, columnspan=3, sticky=(tk.W, tk.E), pady=(0, 10))
        dir_frame.columnconfigure(0, weight=1)
        
        dir_entry = ttk.Entry(dir_frame, textvariable=self.output_dir, font=('Arial', 10))
        dir_entry.grid(row=0, column=0, sticky=(tk.W, tk.E), padx=(0, 10))
        
        browse_btn = ttk.Button(dir_frame, text="Browse", command=self.browse_directory)
        browse_btn.grid(row=0, column=1)
        
        # Options frame
        options_frame = ttk.LabelFrame(main_frame, text="Download Options", padding="10")
        options_frame.grid(row=7, column=0, columnspan=3, sticky=(tk.W, tk.E), pady=(0, 15))
        options_frame.columnconfigure(1, weight=1)
        
        # Quality selection
        ttk.Label(options_frame, text="Video Quality:").grid(row=0, column=0, sticky=tk.W, pady=2)
        quality_combo = ttk.Combobox(options_frame, textvariable=self.quality, 
                                   values=["best", "1080p", "720p", "480p", "worst"],
                                   state="readonly", width=15)
        quality_combo.grid(row=0, column=1, sticky=tk.W, pady=2, padx=(10, 0))
        
        # Checkboxes
        ttk.Checkbutton(options_frame, text="Audio only", 
                       variable=self.audio_only).grid(row=1, column=0, sticky=tk.W, pady=2)
        
        ttk.Checkbutton(options_frame, text="Download thumbnails", 
                       variable=self.download_thumbnails).grid(row=1, column=1, sticky=tk.W, pady=2)
        
        ttk.Checkbutton(options_frame, text="Download metadata", 
                       variable=self.download_metadata).grid(row=2, column=0, sticky=tk.W, pady=2)
        
        # Download button
        self.download_btn = ttk.Button(main_frame, text="Download Channel", 
                                     command=self.start_download, 
                                     style="Accent.TButton")
        self.download_btn.grid(row=8, column=0, columnspan=3, pady=15)
        self.download_btn.state(['disabled'])  # Disabled until dependencies are ready
        
        # Progress bar
        self.progress = ttk.Progressbar(main_frame, mode='indeterminate')
        self.progress.grid(row=9, column=0, columnspan=3, sticky=(tk.W, tk.E), pady=(0, 10))
        
        # Output text area
        output_frame = ttk.LabelFrame(main_frame, text="Download Progress", padding="5")
        output_frame.grid(row=10, column=0, columnspan=3, sticky=(tk.W, tk.E, tk.N, tk.S), pady=(0, 10))
        output_frame.columnconfigure(0, weight=1)
        output_frame.rowconfigure(0, weight=1)
        main_frame.rowconfigure(10, weight=1)
        
        self.output_text = scrolledtext.ScrolledText(output_frame, height=10, width=80, 
                                                    font=('Consolas', 9))
        self.output_text.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))
        
        # Status bar
        self.status_var = tk.StringVar(value="Checking system dependencies...")
        status_bar = ttk.Label(main_frame, textvariable=self.status_var, 
                             relief=tk.SUNKEN, anchor=tk.W)
        status_bar.grid(row=11, column=0, columnspan=3, sticky=(tk.W, tk.E), pady=(5, 0))
    
    def paste_from_clipboard(self):
        """Paste URL from clipboard."""
        try:
            clipboard_content = self.root.clipboard_get()
            if "youtube.com" in clipboard_content or "youtu.be" in clipboard_content:
                self.channel_url.set(clipboard_content.strip())
            else:
                messagebox.showwarning("Invalid URL", "Clipboard doesn't contain a YouTube URL")
        except tk.TkError:
            messagebox.showwarning("Clipboard Error", "Could not access clipboard")
    
    def log_output(self, message):
        """Add message to output text area."""
        self.output_text.insert(tk.END, message + "\n")
        self.output_text.see(tk.END)
        self.root.update_idletasks()
    
    def clear_output(self):
        """Clear the output text area."""
        self.output_text.delete(1.0, tk.END)
    
    def check_all_dependencies(self):
        """Check all system dependencies."""
        self.log_output("=== System Dependency Check ===")
        
        # Check Python
        python_version = f"{sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}"
        self.log_output(f"Python {python_version} detected")
        self.python_status.config(text=f"Python: {python_version} [OK]", foreground="green")
        
        # Check yt-dlp in background thread
        threading.Thread(target=self.check_ytdlp, daemon=True).start()
    
    def check_ytdlp(self):
        """Check if yt-dlp is available, install if needed."""
        try:
            # Try to run yt-dlp
            result = subprocess.run(['yt-dlp', '--version'], 
                                  capture_output=True, text=True, check=True, timeout=10)
            version = result.stdout.strip()
            
            # Update UI from main thread
            self.root.after(0, lambda: self.ytdlp_found(version))
            
        except (subprocess.CalledProcessError, FileNotFoundError, subprocess.TimeoutExpired):
            # yt-dlp not found, try to install
            self.root.after(0, lambda: self.install_ytdlp())
    
    def ytdlp_found(self, version):
        """Called when yt-dlp is found."""
        self.log_output(f"yt-dlp {version} is available")
        self.ytdlp_status.config(text=f"yt-dlp: {version} [OK]", foreground="green")
        self.dependencies_ready()
    
    def install_ytdlp(self):
        """Install yt-dlp using pip."""
        self.log_output("yt-dlp not found. Installing...")
        self.ytdlp_status.config(text="yt-dlp: Installing...", foreground="orange")
        
        # Install in background thread
        threading.Thread(target=self.do_install_ytdlp, daemon=True).start()
    
    def do_install_ytdlp(self):
        """Actually install yt-dlp."""
        try:
            # Install yt-dlp
            result = subprocess.run([sys.executable, '-m', 'pip', 'install', 'yt-dlp'], 
                                  capture_output=True, text=True, check=True, timeout=60)
            
            # Verify installation
            result = subprocess.run(['yt-dlp', '--version'], 
                                  capture_output=True, text=True, check=True, timeout=10)
            version = result.stdout.strip()
            
            # Update UI from main thread
            self.root.after(0, lambda: self.ytdlp_installed(version))
            
        except (subprocess.CalledProcessError, subprocess.TimeoutExpired) as e:
            self.root.after(0, lambda: self.ytdlp_install_failed(str(e)))
    
    def ytdlp_installed(self, version):
        """Called when yt-dlp is successfully installed."""
        self.log_output(f"yt-dlp {version} installed successfully!")
        self.ytdlp_status.config(text=f"yt-dlp: {version} [OK]", foreground="green")
        self.dependencies_ready()
    
    def ytdlp_install_failed(self, error):
        """Called when yt-dlp installation fails."""
        self.log_output(f"Failed to install yt-dlp: {error}")
        self.ytdlp_status.config(text="yt-dlp: Installation failed [ERROR]", foreground="red")
        self.status_var.set("Error: Could not install yt-dlp. Check internet connection.")
        
        messagebox.showerror("Installation Error", 
                           "Could not install yt-dlp automatically.\n\n"
                           "Please check your internet connection and try again.\n"
                           "You can also install it manually with: pip install yt-dlp")
    
    def dependencies_ready(self):
        """Called when all dependencies are ready."""
        if not self.dependencies_checked:
            self.dependencies_checked = True
            self.download_btn.state(['!disabled'])
            self.status_var.set("Ready to download!")
            self.log_output("=== All dependencies ready! ===")
    
    def browse_directory(self):
        """Open directory browser."""
        directory = filedialog.askdirectory(initialdir=self.output_dir.get())
        if directory:
            self.output_dir.set(directory)
    
    def validate_inputs(self):
        """Validate user inputs."""
        if not self.channel_url.get().strip():
            messagebox.showerror("Error", "Please enter a YouTube channel URL")
            return False
        
        url = self.channel_url.get().strip()
        if "youtube.com" not in url and "youtu.be" not in url:
            messagebox.showerror("Error", "Please enter a valid YouTube URL")
            return False
        
        if not self.output_dir.get().strip():
            messagebox.showerror("Error", "Please select an output directory")
            return False
        
        # Check if output directory exists or can be created
        try:
            output_path = Path(self.output_dir.get())
            output_path.mkdir(parents=True, exist_ok=True)
        except Exception as e:
            messagebox.showerror("Error", f"Cannot create output directory: {e}")
            return False
        
        return True
    
    def build_command(self):
        """Build the yt-dlp command based on GUI settings."""
        cmd = ['yt-dlp']
        
        # Output template
        output_path = Path(self.output_dir.get())
        output_template = str(output_path / "%(uploader)s" / "%(upload_date)s - %(title)s.%(ext)s")
        cmd.extend(['-o', output_template])
        
        # Quality settings
        if self.audio_only.get():
            cmd.extend(['-f', 'bestaudio/best'])
        else:
            if self.quality.get() == 'best':
                cmd.extend(['-f', 'best'])
            elif self.quality.get() == 'worst':
                cmd.extend(['-f', 'worst'])
            else:
                # Custom quality
                quality_num = self.quality.get().replace('p', '')
                cmd.extend(['-f', f'best[height<={quality_num}]'])
        
        # Additional options
        cmd.extend([
            '--ignore-errors',
            '--no-overwrites',
            '--continue',
        ])
        
        # Archive file
        archive_file = output_path / 'download_archive.txt'
        cmd.extend(['--download-archive', str(archive_file)])
        
        # Metadata and thumbnails
        if self.download_metadata.get():
            cmd.extend(['--write-info-json', '--write-description'])
        
        if self.download_thumbnails.get():
            cmd.extend(['--write-thumbnail'])
        
        # Add channel URL
        cmd.append(self.channel_url.get().strip())
        
        return cmd
    
    def start_download(self):
        """Start the download process."""
        if self.is_downloading:
            self.stop_download()
            return
        
        if not self.validate_inputs():
            return
        
        # Clear previous output
        self.clear_output()
        
        # Update UI
        self.is_downloading = True
        self.download_btn.config(text="Stop Download")
        self.progress.start()
        self.status_var.set("Downloading...")
        
        # Build command
        cmd = self.build_command()
        self.log_output(f"Starting download...")
        self.log_output(f"Channel: {self.channel_url.get()}")
        self.log_output(f"Output: {self.output_dir.get()}")
        self.log_output("-" * 50)
        
        # Start download in separate thread
        self.download_thread = threading.Thread(target=self.run_download, args=(cmd,))
        self.download_thread.daemon = True
        self.download_thread.start()
    
    def run_download(self, cmd):
        """Run the download process."""
        try:
            # Start the process
            self.download_process = subprocess.Popen(
                cmd,
                stdout=subprocess.PIPE,
                stderr=subprocess.STDOUT,
                text=True,
                bufsize=1,
                universal_newlines=True,
                creationflags=subprocess.CREATE_NO_WINDOW if platform.system() == "Windows" else 0
            )
            
            # Read output line by line
            for line in iter(self.download_process.stdout.readline, ''):
                if not self.is_downloading:
                    break
                self.output_queue.put(('output', line.rstrip()))
            
            # Wait for process to complete
            return_code = self.download_process.wait()
            
            if return_code == 0:
                self.output_queue.put(('status', 'Download completed successfully!'))
            else:
                self.output_queue.put(('status', f'Download finished with some errors (code {return_code})'))
                
        except Exception as e:
            self.output_queue.put(('error', f'Error during download: {str(e)}'))
        finally:
            self.output_queue.put(('finished', None))
    
    def stop_download(self):
        """Stop the current download."""
        if self.download_process and self.download_process.poll() is None:
            self.download_process.terminate()
            self.log_output("\n[WARNING] Download stopped by user")
        
        self.download_finished()
    
    def download_finished(self):
        """Handle download completion."""
        self.is_downloading = False
        self.download_btn.config(text="Download Channel")
        self.progress.stop()
        self.status_var.set("Ready to download!")
    
    def check_queue(self):
        """Check the queue for messages from the download thread."""
        try:
            while True:
                msg_type, message = self.output_queue.get_nowait()
                
                if msg_type == 'output':
                    self.log_output(message)
                elif msg_type == 'status':
                    self.log_output(f"\n{message}")
                    self.status_var.set(message)
                elif msg_type == 'error':
                    self.log_output(f"\n[ERROR] {message}")
                    messagebox.showerror("Download Error", message)
                elif msg_type == 'finished':
                    self.download_finished()
                    break
        except queue.Empty:
            pass
        
        # Schedule next check
        self.root.after(100, self.check_queue)

def main():
    # Set up the main window
    root = tk.Tk()
    
    # Configure styling
    style = ttk.Style()
    try:
        style.theme_use('vista')  # Windows native theme
    except:
        try:
            style.theme_use('clam')  # Fallback modern theme
        except:
            pass  # Use default
    
    # Configure button style
    style.configure('Accent.TButton', font=('Arial', 10, 'bold'))
    
    # Create the application
    app = YouTubeArchiverStandalone(root)
    
    # Handle window closing
    def on_closing():
        if app.is_downloading:
            if messagebox.askokcancel("Quit", "Download in progress. Stop and quit?"):
                app.stop_download()
                root.destroy()
        else:
            root.destroy()
    
    root.protocol("WM_DELETE_WINDOW", on_closing)
    
    # Center window on screen
    root.update_idletasks()
    x = (root.winfo_screenwidth() // 2) - (root.winfo_width() // 2)
    y = (root.winfo_screenheight() // 2) - (root.winfo_height() // 2)
    root.geometry(f"+{x}+{y}")
    
    # Set minimum size
    root.minsize(600, 500)
    
    # Run the application
    root.mainloop()

if __name__ == "__main__":
    main()
