#!/usr/bin/env python3
"""
YouTube Channel Archiver GUI
A simple graphical interface for the YouTube channel archiver script.
"""

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

class YouTubeArchiverGUI:
    def __init__(self, root):
        self.root = root
        self.root.title("YouTube Channel Archiver")
        self.root.geometry("600x500")
        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.cwd()))
        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 download process
        self.download_process = None
        self.is_downloading = False
        
        # Check Python installation on Windows first
        if not self.check_python_installation():
            return  # Exit if Python installation failed
        
        self.create_widgets()
        self.check_dependencies()
        
        # Start checking queue for output updates
        self.root.after(100, self.check_queue)
    
    def check_python_installation(self):
        """Check if Python is properly installed on Windows."""
        if platform.system() != "Windows":
            return True  # Not Windows, proceed normally
        
        try:
            # Try to run python and get version
            result = subprocess.run([sys.executable, "--version"], 
                                  capture_output=True, text=True, check=True)
            python_version = result.stdout.strip()
            print(f"Python is available: {python_version}")
            return True
            
        except (subprocess.CalledProcessError, FileNotFoundError):
            # Python not found, try to install it
            return self.install_python_windows()
    
    def install_python_windows(self):
        """Install Python on Windows using the provided MSIX installer."""
        installer_path = Path("windows/python.msix")
        
        if not installer_path.exists():
            messagebox.showerror(
                "Python Installation Error",
                f"Python is not installed and the installer was not found at:\n{installer_path.absolute()}\n\n"
                "Please install Python manually from python.org or place the installer at the expected location."
            )
            return False
        
        # Show installation dialog
        result = messagebox.askyesno(
            "Python Installation Required",
            "Python is not installed on this system. Would you like to install it now?\n\n"
            f"The installer will be launched from:\n{installer_path.absolute()}"
        )
        
        if not result:
            messagebox.showinfo(
                "Installation Cancelled",
                "Python installation cancelled. The application cannot run without Python."
            )
            return False
        
        try:
            # Create a temporary window to show installation progress
            install_window = tk.Toplevel(self.root)
            install_window.title("Installing Python")
            install_window.geometry("400x200")
            install_window.resizable(False, False)
            install_window.transient(self.root)
            install_window.grab_set()
            
            # Center the window
            install_window.update_idletasks()
            x = (install_window.winfo_screenwidth() // 2) - (install_window.winfo_width() // 2)
            y = (install_window.winfo_screenheight() // 2) - (install_window.winfo_height() // 2)
            install_window.geometry(f"+{x}+{y}")
            
            # Add progress indicator and message
            frame = ttk.Frame(install_window, padding="20")
            frame.pack(fill="both", expand=True)
            
            ttk.Label(frame, text="Installing Python...", 
                     font=('Arial', 12, 'bold')).pack(pady=(0, 10))
            
            progress = ttk.Progressbar(frame, mode='indeterminate')
            progress.pack(fill="x", pady=(0, 10))
            progress.start()
            
            status_label = ttk.Label(frame, text="Launching installer...", wraplength=350)
            status_label.pack(pady=(0, 10))
            
            # Update the window
            install_window.update()
            
            # Launch the MSIX installer
            status_label.config(text="Running Python installer. Please follow the installation prompts.")
            install_window.update()
            
            # Use PowerShell to install the MSIX package
            powershell_cmd = f'Add-AppxPackage -Path "{installer_path.absolute()}"'
            
            result = subprocess.run([
                "powershell", "-Command", powershell_cmd
            ], capture_output=True, text=True)
            
            progress.stop()
            
            if result.returncode == 0:
                status_label.config(text="Installation completed successfully!")
                install_window.update()
                
                # Wait a moment and then verify installation
                self.root.after(2000, lambda: self.verify_python_installation(install_window))
                return True
            else:
                # Installation failed
                error_msg = result.stderr or result.stdout or "Unknown error occurred"
                status_label.config(text=f"Installation failed: {error_msg[:100]}...")
                install_window.update()
                
                messagebox.showerror(
                    "Installation Failed",
                    f"Python installation failed:\n{error_msg}\n\n"
                    "Please try installing Python manually from python.org"
                )
                install_window.destroy()
                return False
                
        except Exception as e:
            messagebox.showerror(
                "Installation Error",
                f"An error occurred while installing Python:\n{str(e)}\n\n"
                "Please try installing Python manually from python.org"
            )
            if 'install_window' in locals():
                install_window.destroy()
            return False
    
    def verify_python_installation(self, install_window):
        """Verify that Python was installed successfully."""
        try:
            # Try to find Python in common locations
            possible_python_paths = [
                "python",
                "python3",
                "py",
                os.path.expandvars(r"%LOCALAPPDATA%\Microsoft\WindowsApps\python.exe"),
                os.path.expandvars(r"%LOCALAPPDATA%\Programs\Python\Python*\python.exe"),
            ]
            
            python_found = False
            for python_path in possible_python_paths:
                try:
                    result = subprocess.run([python_path, "--version"], 
                                          capture_output=True, text=True, check=True)
                    python_version = result.stdout.strip()
                    print(f"Python found at {python_path}: {python_version}")
                    python_found = True
                    break
                except (subprocess.CalledProcessError, FileNotFoundError):
                    continue
            
            install_window.destroy()
            
            if python_found:
                messagebox.showinfo(
                    "Installation Successful",
                    f"Python has been installed successfully!\n{python_version}\n\n"
                    "The application will now continue."
                )
                return True
            else:
                messagebox.showwarning(
                    "Installation Verification Failed",
                    "Python installation completed, but Python could not be found in the expected locations.\n\n"
                    "You may need to restart the application or your system for the installation to take effect."
                )
                return False
                
        except Exception as e:
            install_window.destroy()
            messagebox.showerror(
                "Verification Error",
                f"Could not verify Python installation:\n{str(e)}"
            )
            return False
    
    def create_widgets(self):
        # Main frame
        main_frame = ttk.Frame(self.root, padding="10")
        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
        title_label = ttk.Label(main_frame, text="YouTube Channel Archiver", 
                               font=('Arial', 16, 'bold'))
        title_label.grid(row=0, column=0, columnspan=3, pady=(0, 20))
        
        # Channel URL input
        ttk.Label(main_frame, text="Channel URL:").grid(row=1, column=0, sticky=tk.W, pady=5)
        url_entry = ttk.Entry(main_frame, textvariable=self.channel_url, width=50)
        url_entry.grid(row=1, column=1, columnspan=2, sticky=(tk.W, tk.E), pady=5, padx=(10, 0))
        
        # Output directory
        ttk.Label(main_frame, text="Output Directory:").grid(row=2, column=0, sticky=tk.W, pady=5)
        dir_entry = ttk.Entry(main_frame, textvariable=self.output_dir, width=40)
        dir_entry.grid(row=2, column=1, sticky=(tk.W, tk.E), pady=5, padx=(10, 5))
        
        browse_btn = ttk.Button(main_frame, text="Browse", command=self.browse_directory)
        browse_btn.grid(row=2, column=2, pady=5)
        
        # Options frame
        options_frame = ttk.LabelFrame(main_frame, text="Options", padding="10")
        options_frame.grid(row=3, column=0, columnspan=3, sticky=(tk.W, tk.E), pady=10)
        options_frame.columnconfigure(1, weight=1)
        
        # Quality selection
        ttk.Label(options_frame, text="Quality:").grid(row=0, column=0, sticky=tk.W, pady=2)
        quality_combo = ttk.Combobox(options_frame, textvariable=self.quality, 
                                   values=["best", "worst", "1080p", "720p", "480p"],
                                   state="readonly", width=15)
        quality_combo.grid(row=0, column=1, sticky=tk.W, pady=2, padx=(10, 0))
        
        # Checkboxes
        audio_check = ttk.Checkbutton(options_frame, text="Audio only", 
                                    variable=self.audio_only)
        audio_check.grid(row=1, column=0, sticky=tk.W, pady=2)
        
        thumb_check = ttk.Checkbutton(options_frame, text="Download thumbnails", 
                                    variable=self.download_thumbnails)
        thumb_check.grid(row=1, column=1, sticky=tk.W, pady=2)
        
        meta_check = ttk.Checkbutton(options_frame, text="Download metadata", 
                                   variable=self.download_metadata)
        meta_check.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=4, column=0, columnspan=3, pady=20)
        
        # Progress bar
        self.progress = ttk.Progressbar(main_frame, mode='indeterminate')
        self.progress.grid(row=5, column=0, columnspan=3, sticky=(tk.W, tk.E), pady=(0, 10))
        
        # Output text area
        output_frame = ttk.LabelFrame(main_frame, text="Output", padding="5")
        output_frame.grid(row=6, 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(6, weight=1)
        
        self.output_text = scrolledtext.ScrolledText(output_frame, height=12, width=70)
        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="Ready")
        status_bar = ttk.Label(main_frame, textvariable=self.status_var, 
                             relief=tk.SUNKEN, anchor=tk.W)
        status_bar.grid(row=7, column=0, columnspan=3, sticky=(tk.W, tk.E), pady=(5, 0))
    
    def check_dependencies(self):
        """Check if yt-dlp is available."""
        try:
            result = subprocess.run(['yt-dlp', '--version'], 
                                  capture_output=True, text=True, check=True)
            version = result.stdout.strip()
            self.log_output(f"✓ yt-dlp is available: {version}")
            self.status_var.set("Ready - yt-dlp found")
        except (subprocess.CalledProcessError, FileNotFoundError):
            self.log_output("⚠ yt-dlp not found. It will be installed automatically when needed.")
            self.status_var.set("Ready - yt-dlp will be auto-installed")
    
    def browse_directory(self):
        """Open directory browser."""
        directory = filedialog.askdirectory(initialdir=self.output_dir.get())
        if directory:
            self.output_dir.set(directory)
    
    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 validate_inputs(self):
        """Validate user inputs."""
        if not self.channel_url.get().strip():
            messagebox.showerror("Error", "Please enter a channel 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."""
        # Find the archiver script
        script_path = Path(__file__).parent / "youtube_archiver.py"
        if not script_path.exists():
            # If not found, assume it's in the same directory
            script_path = "youtube_archiver.py"
        
        cmd = [sys.executable, str(script_path)]
        
        # Add channel URL
        cmd.append(self.channel_url.get().strip())
        
        # Add options
        cmd.extend(["--output", self.output_dir.get()])
        cmd.extend(["--quality", self.quality.get()])
        
        if self.audio_only.get():
            cmd.append("--audio-only")
        
        if not self.download_thumbnails.get():
            cmd.append("--no-thumbnails")
        
        if not self.download_metadata.get():
            cmd.append("--no-metadata")
        
        return cmd
    
    def start_download(self):
        """Start the download process in a separate thread."""
        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 with command: {' '.join(cmd)}")
        self.log_output("-" * 60)
        
        # 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
            )
            
            # Read output line by line
            for line in iter(self.download_process.stdout.readline, ''):
                if not self.is_downloading:  # Check if stopped
                    break
                # Put output in queue for main thread to handle
                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 failed with 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⚠ 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")
    
    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❌ {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():
    root = tk.Tk()
    
    # Set up styling
    style = ttk.Style()
    
    # Try to use a modern theme
    try:
        style.theme_use('clam')  # Modern looking theme
    except:
        pass  # Use default theme if clam is not available
    
    app = YouTubeArchiverGUI(root)
    
    # Only proceed if Python installation check passed
    if not hasattr(app, 'output_queue'):
        root.destroy()
        return
    
    # Handle window closing
    def on_closing():
        if app.is_downloading:
            if messagebox.askokcancel("Quit", "Download in progress. Do you want to 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}")
    
    root.mainloop()

if __name__ == "__main__":
    main()
