#!/usr/bin/env python3
import tkinter as tk
from tkinter import ttk, messagebox
import subprocess
import threading

class PackageSearchGUI:
    def __init__(self, root):
        self.root = root
        self.root.title("Linux Package Manager")
        self.root.geometry("750x650")

        # Search Bar Frame
        search_frame = tk.Frame(root)
        search_frame.pack(pady=10, fill=tk.X, padx=10)

        self.search_label = tk.Label(search_frame, text="Package Name:", font=("Arial", 10, "bold"))
        self.search_label.pack(side=tk.LEFT, padx=5)

        self.search_var = tk.StringVar()
        self.entry = tk.Entry(search_frame, textvariable=self.search_var, font=("Arial", 11))
        self.entry.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=5)
        self.entry.bind("<Return>", lambda e: self.search_packages())

        self.btn_search = tk.Button(search_frame, text="Search", command=self.search_packages, bg="#4CAF50", fg="white", width=10)
        self.btn_search.pack(side=tk.LEFT, padx=5)

        # Results Table
        columns = ("Package", "Description")
        self.tree = ttk.Treeview(root, columns=columns, show="headings")
        self.tree.heading("Package", text="Package Name")
        self.tree.heading("Description", text="Brief Description")
        self.tree.column("Package", width=200)
        self.tree.column("Description", width=500)
        self.tree.pack(padx=10, pady=5, fill=tk.BOTH, expand=True)

        # Installation Action Button
        self.btn_install = tk.Button(root, text="Install Selected Package", command=self.start_installation, bg="#E91E63", fg="white", font=("Arial", 10, "bold"))
        self.btn_install.pack(pady=10)

        # Live Console Output Window
        console_frame = tk.LabelFrame(root, text="Terminal Output Log")
        console_frame.pack(padx=10, pady=5, fill=tk.BOTH, expand=True)

        self.console_text = tk.Text(console_frame, bg="black", fg="#00FF00", font=("Courier", 9), state=tk.DISABLED)
        self.console_text.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=5, pady=5)

        scrollbar = tk.Scrollbar(console_frame, command=self.console_text.yview)
        scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
        self.console_text.config(yscrollcommand=scrollbar.set)

        # Progress Bar
        self.progress = ttk.Progressbar(root, orient="horizontal", mode="indeterminate")
        self.progress.pack(padx=10, pady=10, fill=tk.X)

    def log_to_console(self, text):
        """Helper to cleanly insert text into the dark console window."""
        self.console_text.config(state=tk.NORMAL)
        self.console_text.insert(tk.END, text)
        self.console_text.see(tk.END)
        self.console_text.config(state=tk.DISABLED)

    def search_packages(self):
        query = self.search_var.get().strip()
        if not query: return

        for item in self.tree.get_children():
            self.tree.delete(item)

        try:
            output = subprocess.check_output(["apt-cache", "search", query], universal_newlines=True)
            for line in output.strip().split('\n'):
                if " - " in line:
                    pkg, desc = line.split(" - ", 1)
                    self.tree.insert("", tk.END, values=(pkg.strip(), desc.strip()))
        except Exception as e:
            messagebox.showerror("Error", f"Could not search: {e}")

    def start_installation(self):
        selected = self.tree.selection()
        if not selected:
            messagebox.showwarning("Selection", "Please select a package first.")
            return
        
        pkg_name = self.tree.item(selected)['values'][0]
        
        if not messagebox.askyesno("Confirm", f"Do you want to install '{pkg_name}'?"):
            return

        # Disable buttons and start progress bar animation
        self.btn_install.config(state=tk.DISABLED)
        self.btn_search.config(state=tk.DISABLED)
        self.progress.start(10)
        
        # Clear log and give immediate visual feedback
        self.console_text.config(state=tk.NORMAL)
        self.console_text.delete("1.0", tk.END)
        self.console_text.config(state=tk.DISABLED)
        self.log_to_console(f"Starting installation process for: {pkg_name}\nWaiting for authorization...\n")

        # Run installation in a separate thread so the GUI does not freeze
        thread = threading.Thread(target=self.run_install_thread, args=(pkg_name,))
        thread.start()

    def run_install_thread(self, pkg_name):
        try:
            # Command uses pkexec for the fancy popup password box
            # We redirect standard output and error to capture logs in real-time
            cmd = ["pkexec", "apt-get", "install", "-y", pkg_name]
            process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True)

            # Read terminal output line by line as it happens
            while True:
                line = process.stdout.readline()
                if not line:
                    break
                # Safely update GUI elements from the background thread
                self.root.after(0, self.log_to_console, line)

            process.wait()

            # Check if the process exited cleanly
            if process.returncode == 0:
                self.root.after(0, lambda: messagebox.showinfo("Success", f"'{pkg_name}' installed successfully!"))
            else:
                self.root.after(0, lambda: messagebox.showerror("Failed", "Installation failed or authentication was cancelled."))

        except Exception as e:
            self.root.after(0, lambda: messagebox.showerror("Error", f"An unexpected error occurred: {e}"))
        
        finally:
            # Turn off progress bar and restore buttons when finished
            self.root.after(0, self.cleanup_ui)

    def cleanup_ui(self):
        self.progress.stop()
        self.btn_install.config(state=tk.NORMAL)
        self.btn_search.config(state=tk.NORMAL)

if __name__ == "__main__":
    root = tk.Tk()
    app = PackageSearchGUI(root)
    root.mainloop()
