#!/usr/bin/env python3
#--------------------------------------------------------------------------------------------------------
# Name: Linux Lite - Lite Updates
# Architecture: amd64
# Author: Jerry Bezencon
# Website: https://www.linuxliteos.com
# Language: Python/GTK4
# Licence: GPLv2
#--------------------------------------------------------------------------------------------------------

import gi
gi.require_version('Gtk', '4.0')
gi.require_version('Adw', '1')

from gi.repository import Gtk, Adw, Gio, GLib, Pango, Gdk
import subprocess
import os
import threading
import re
import argparse
from datetime import datetime
from pathlib import Path


# Application version
APP_VERSION = "7.0"


# Helper script that runs as root - handles all apt operations
ROOT_HELPER_SCRIPT = '''#!/bin/bash
# Root helper for Linux Lite Updates
# Reads commands from stdin and executes them

export DEBIAN_FRONTEND=noninteractive
# Force English locale so apt-get output matches the prefixes the GUI parses
# (Get:/Hit:/Ign:/Fetched, Unpacking/Setting up/Processing, Inst …). Without
# this, non-English systems get translated apt output and the live progress
# strip stays empty for the entire run.
export LANG=C.UTF-8
export LC_ALL=C.UTF-8
LOGFILE="/var/log/llupdates.log"

# Initialize log file
echo "===================================" > "$LOGFILE"
echo "Linux Lite Updates Log" >> "$LOGFILE"
echo "Date: $(date)" >> "$LOGFILE"
echo "===================================" >> "$LOGFILE"
echo "" >> "$LOGFILE"

log_msg() {
    echo "$1" | tee -a "$LOGFILE"
}

log_output() {
    tee -a "$LOGFILE"
}

# Check for dpkg lock
check_dpkg_lock() {
    if fuser /var/lib/dpkg/lock-frontend >/dev/null 2>&1; then
        echo "LOCK:dpkg lock-frontend is held by another process"
        return 1
    fi
    if fuser /var/lib/apt/lists/lock >/dev/null 2>&1; then
        echo "LOCK:apt lists lock is held by another process"
        return 1
    fi
    if fuser /var/cache/apt/archives/lock >/dev/null 2>&1; then
        echo "LOCK:apt archives lock is held by another process"
        return 1
    fi
    return 0
}

while read -r cmd; do
    case "$cmd" in
        CHECK_LOCK)
            if check_dpkg_lock; then
                log_msg "STATUS:LOCK_OK"
            else
                log_msg "STATUS:LOCK_HELD"
            fi
            ;;
        UPDATE)
            echo "" >> "$LOGFILE"
            echo "--- Updating Package Lists ---" >> "$LOGFILE"
            echo "Started: $(date)" >> "$LOGFILE"
            echo "" >> "$LOGFILE"
            log_msg "STATUS:UPDATING"
            apt-get update 2>&1 | log_output
            result=${PIPESTATUS[0]}
            echo "" >> "$LOGFILE"
            echo "Finished: $(date)" >> "$LOGFILE"
            if [ $result -eq 0 ]; then
                log_msg "STATUS:UPDATE_OK"
            else
                log_msg "STATUS:UPDATE_FAILED"
            fi
            ;;
        CHECK)
            echo "" >> "$LOGFILE"
            echo "--- Checking for Available Updates ---" >> "$LOGFILE"
            echo "Started: $(date)" >> "$LOGFILE"
            echo "" >> "$LOGFILE"
            log_msg "STATUS:CHECKING"
            apt-get --just-print dist-upgrade 2>&1 | log_output
            echo "" >> "$LOGFILE"
            echo "Finished: $(date)" >> "$LOGFILE"
            log_msg "STATUS:CHECK_DONE"
            ;;
        SIZES)
            # Get download sizes for packages
            log_msg "STATUS:GETTING_SIZES"
            apt-get --print-uris dist-upgrade 2>/dev/null | grep "^'" | while read line; do
                size=$(echo "$line" | awk '{print $3}')
                name=$(echo "$line" | awk '{print $1}' | sed "s/.*\\///" | sed "s/_.*//")
                echo "SIZE:$name:$size"
            done
            log_msg "STATUS:SIZES_DONE"
            ;;
        CHANGELOG:*)
            # Get changelog for a package - format: CHANGELOG:packagename
            pkg="${cmd#CHANGELOG:}"
            log_msg "STATUS:GETTING_CHANGELOG:$pkg"
            apt-get changelog "$pkg" 2>&1 | head -100
            log_msg "STATUS:CHANGELOG_DONE"
            ;;
        UPGRADE)
            echo "" >> "$LOGFILE"
            echo "--- Installing Updates ---" >> "$LOGFILE"
            echo "Started: $(date)" >> "$LOGFILE"
            echo "" >> "$LOGFILE"
            log_msg "STATUS:UPGRADING"
            apt-get -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold" dist-upgrade -y 2>&1 | log_output
            result=${PIPESTATUS[0]}
            echo "" >> "$LOGFILE"
            echo "Finished: $(date)" >> "$LOGFILE"
            if [ $result -eq 0 ]; then
                log_msg "STATUS:UPGRADE_OK"
            else
                log_msg "STATUS:UPGRADE_FAILED"
            fi
            ;;
        CLEAR_AVAIL)
            dpkg --clear-avail 2>/dev/null
            log_msg "STATUS:CLEARED"
            ;;
        PLYMOUTH:*)
            # Plymouth update command - format: PLYMOUTH:/path/to/tmp/file
            tmp_file="${cmd#PLYMOUTH:}"
            plymouth_file="/usr/share/plymouth/themes/ubuntu-text/ubuntu-text.plymouth"
            echo "" >> "$LOGFILE"
            echo "--- Plymouth Theme Update ---" >> "$LOGFILE"
            if [ -f "$tmp_file" ] && [ -f "$plymouth_file" ]; then
                cp "$tmp_file" "$plymouth_file"
                echo "Updating initramfs..." >> "$LOGFILE"
                update-initramfs -u 2>&1 | log_output
                rm -f "$tmp_file"
                echo "Plymouth theme updated successfully" >> "$LOGFILE"
                log_msg "STATUS:PLYMOUTH_OK"
            else
                echo "Plymouth update skipped (files not found)" >> "$LOGFILE"
                log_msg "STATUS:PLYMOUTH_SKIPPED"
            fi
            ;;
        REBOOT)
            echo "" >> "$LOGFILE"
            echo "--- System Reboot Requested ---" >> "$LOGFILE"
            echo "Time: $(date)" >> "$LOGFILE"
            log_msg "STATUS:REBOOTING"
            reboot
            ;;
        QUIT)
            echo "" >> "$LOGFILE"
            echo "--- Session Ended ---" >> "$LOGFILE"
            echo "Time: $(date)" >> "$LOGFILE"
            echo "===================================" >> "$LOGFILE"
            log_msg "STATUS:QUIT"
            exit 0
            ;;
    esac
done
'''


class UpdateItem:
    """Represents a single package update."""
    def __init__(self, name: str, installed: str, available: str, 
                 is_security: bool = False, size: int = 0, changelog: str = ""):
        self.name = name
        self.installed = installed
        self.available = available
        self.is_security = is_security
        self.size = size  # Size in bytes
        self.changelog = changelog
    
    def size_str(self) -> str:
        """Return human-readable size string."""
        if self.size < 1024:
            return f"{self.size} B"
        elif self.size < 1024 * 1024:
            return f"{self.size / 1024:.1f} KB"
        else:
            return f"{self.size / (1024 * 1024):.1f} MB"


class LiteUpdatesApp(Adw.Application):
    """Main application class for Linux Lite Updates."""
    
    APP_NAME = "Linux Lite Updates"
    APP_ID = "com.linuxliteos.updates"
    ICON_NAME = "lite-updates"
    LOG_FILE = "/var/log/llupdates.log"
    TIMESTAMP_FILE = os.path.expanduser("~/.cache/lite-updates-last-check")
    
    def __init__(self):
        super().__init__(
            application_id=self.APP_ID,
            flags=Gio.ApplicationFlags.FLAGS_NONE
        )
        self.window = None
        self.updates = []
        self.temp_files = []
        self.root_helper = None
        self.helper_script_path = None
        self.original_user = os.environ.get('SUDO_USER', os.environ.get('USER', 'root'))
        self.last_check_time = None
        self.total_download_size = 0
        self.security_count = 0
        
    def _apply_css(self):
        css = b"""
        .dark-text { color: #1e1e1e; }
        .dim-label { opacity: 0.75; }
        """
        provider = Gtk.CssProvider()
        provider.load_from_data(css)
        Gtk.StyleContext.add_provider_for_display(
            Gdk.Display.get_default(), provider,
            Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION
        )

    def do_activate(self):
        """Handle application activation."""
        self._apply_css()
        if not self.window:
            self.window = LiteUpdatesWindow(application=self)

        # Show initial confirmation dialog without showing the main window
        self.window.show_start_dialog()
    
    def save_check_timestamp(self):
        """Save the current timestamp as last check time."""
        try:
            cache_dir = os.path.dirname(self.TIMESTAMP_FILE)
            if not os.path.exists(cache_dir):
                os.makedirs(cache_dir, exist_ok=True)
            with open(self.TIMESTAMP_FILE, 'w') as f:
                f.write(datetime.now().isoformat())
            self.last_check_time = datetime.now()
        except Exception as e:
            print(f"[DEBUG] Could not save timestamp: {e}")
    
    def load_check_timestamp(self) -> str:
        """Load and return the last check timestamp as a formatted string."""
        try:
            if os.path.exists(self.TIMESTAMP_FILE):
                with open(self.TIMESTAMP_FILE, 'r') as f:
                    ts = datetime.fromisoformat(f.read().strip())
                    self.last_check_time = ts
                    return ts.strftime("%A, %d %B %Y at %H:%M")
        except Exception as e:
            print(f"[DEBUG] Could not load timestamp: {e}")
        return "Never"
    
    def start_root_helper(self, callback):
        """Start the root helper process with pkexec."""
        # Write helper script to a known location that matches polkit policy
        self.helper_script_path = '/tmp/lite-updates-helper.sh'
        with open(self.helper_script_path, 'w') as f:
            f.write(ROOT_HELPER_SCRIPT)
        os.chmod(self.helper_script_path, 0o755)
        
        def start_helper_thread():
            try:
                self.root_helper = subprocess.Popen(
                    ['pkexec', self.helper_script_path],
                    stdin=subprocess.PIPE,
                    stdout=subprocess.PIPE,
                    stderr=subprocess.STDOUT,
                    text=True,
                    bufsize=1
                )
                # Check if process started successfully
                if self.root_helper.poll() is None:
                    GLib.idle_add(callback, True)
                else:
                    GLib.idle_add(callback, False)
            except Exception as e:
                print(f"[DEBUG] Failed to start root helper: {e}")
                GLib.idle_add(callback, False)
        
        thread = threading.Thread(target=start_helper_thread)
        thread.daemon = True
        thread.start()
    
    def send_command(self, cmd):
        """Send a command to the root helper."""
        if self.root_helper and self.root_helper.poll() is None:
            try:
                self.root_helper.stdin.write(cmd + '\n')
                self.root_helper.stdin.flush()
                return True
            except Exception as e:
                print(f"[DEBUG] Failed to send command: {e}")
                return False
        return False
    
    def read_helper_output(self):
        """Generator to read output from root helper."""
        if self.root_helper:
            for line in self.root_helper.stdout:
                yield line.rstrip('\n')
    
    def cleanup(self):
        """Clean up resources."""
        if self.root_helper:
            try:
                self.root_helper.stdin.write('QUIT\n')
                self.root_helper.stdin.flush()
                self.root_helper.wait(timeout=5)
            except Exception:
                try:
                    self.root_helper.kill()
                except Exception:
                    pass
        
        if self.helper_script_path and os.path.exists(self.helper_script_path):
            try:
                os.remove(self.helper_script_path)
            except Exception:
                pass
        
        for f in self.temp_files:
            try:
                if os.path.exists(f):
                    os.remove(f)
            except Exception:
                pass


class LiteUpdatesWindow(Adw.ApplicationWindow):
    """Main window for the Linux Lite Updates application."""
    
    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self.app = self.get_application()
        self.set_title(self.app.APP_NAME)
        self.set_default_size(700, 500)
        self.set_icon_name(self.app.ICON_NAME)

        # Center window on screen after it's realized
        self.connect("realize", self._on_realize)

        # Create header bar
        self.header = Adw.HeaderBar()
        
        # Main content box
        self.main_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
        
        # Create toolbar view for proper header integration
        toolbar_view = Adw.ToolbarView()
        toolbar_view.add_top_bar(self.header)
        toolbar_view.set_content(self.main_box)
        
        self.set_content(toolbar_view)
        
        # Connect close handler
        self.connect("close-request", self._on_close_request)
        
        # Initialize views
        self.setup_status_page()
        self.setup_updates_view()
        self.setup_progress_view()
        self.setup_log_view()
        
        # Start with status page
        self.show_status_page("Starting...", "software-update-available-symbolic")
    
    def _on_realize(self, window):
        """Called when window is realized - schedule centering."""
        GLib.timeout_add(200, self._center_window)

    def _center_window(self):
        """Center the window on the primary monitor using wmctrl."""
        try:
            display = Gdk.Display.get_default()
            if not display:
                return False
            monitors = display.get_monitors()
            if monitors.get_n_items() == 0:
                return False
            monitor = monitors.get_item(0)
            geometry = monitor.get_geometry()
            win_width, win_height = 700, 500
            x = geometry.x + (geometry.width - win_width) // 2
            y = geometry.y + (geometry.height - win_height) // 2
            subprocess.run(
                ['wmctrl', '-r', self.app.APP_NAME, '-e', f'0,{x},{y},{win_width},{win_height}'],
                capture_output=True, timeout=2
            )
        except Exception:
            pass
        return False

    def _on_close_request(self, window):
        """Handle window close - cleanup root helper."""
        self.app.cleanup()
        return False  # Allow close to proceed
        
    def setup_status_page(self):
        """Create the status page for showing messages."""
        self.status_page = Adw.StatusPage()
        self.status_page.set_vexpand(True)
        
    def setup_updates_view(self):
        """Create the updates list view."""
        self.updates_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=0)
        self.updates_box.set_vexpand(True)
        
        # Scrolled window for updates list
        scrolled = Gtk.ScrolledWindow()
        scrolled.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC)
        scrolled.set_vexpand(True)
        
        # Main content box inside scroll
        content_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=12)
        
        # Clamp for proper width
        self.updates_clamp = Adw.Clamp()
        self.updates_clamp.set_maximum_size(800)
        self.updates_clamp.set_margin_top(24)
        self.updates_clamp.set_margin_bottom(24)
        self.updates_clamp.set_margin_start(12)
        self.updates_clamp.set_margin_end(12)
        
        # Summary banner
        self.summary_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=8)
        self.summary_box.add_css_class("card")
        self.summary_box.set_margin_bottom(12)
        
        # Summary content
        summary_content = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=16)
        summary_content.set_margin_top(16)
        summary_content.set_margin_bottom(16)
        summary_content.set_margin_start(16)
        summary_content.set_margin_end(16)
        
        # Update icon
        update_icon = Gtk.Image.new_from_icon_name("software-update-available-symbolic")
        update_icon.set_pixel_size(48)
        summary_content.append(update_icon)
        
        # Summary text
        summary_text_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=4)
        summary_text_box.set_hexpand(True)
        
        self.summary_title = Gtk.Label()
        self.summary_title.set_halign(Gtk.Align.START)
        self.summary_title.add_css_class("title-3")
        
        self.summary_details = Gtk.Label()
        self.summary_details.set_halign(Gtk.Align.START)
        self.summary_details.add_css_class("dim-label")
        
        self.summary_security = Gtk.Label()
        self.summary_security.set_halign(Gtk.Align.START)
        
        summary_text_box.append(self.summary_title)
        summary_text_box.append(self.summary_details)
        summary_text_box.append(self.summary_security)
        
        summary_content.append(summary_text_box)
        self.summary_box.append(summary_content)
        
        # Last checked label
        self.last_checked_label = Gtk.Label()
        self.last_checked_label.set_halign(Gtk.Align.START)
        self.last_checked_label.add_css_class("dim-label")
        self.last_checked_label.set_margin_start(16)
        self.last_checked_label.set_margin_bottom(12)
        self.summary_box.append(self.last_checked_label)
        
        # List box for updates
        self.updates_group = Adw.PreferencesGroup()
        self.updates_group.set_title("Available Updates")
        self.updates_group.set_description("The following packages will be upgraded")
        
        content_box.append(self.summary_box)
        content_box.append(self.updates_group)
        
        self.updates_clamp.set_child(content_box)
        scrolled.set_child(self.updates_clamp)
        self.updates_box.append(scrolled)
        
        # Bottom action bar
        action_bar = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=12)
        action_bar.set_halign(Gtk.Align.END)
        action_bar.set_margin_top(12)
        action_bar.set_margin_bottom(12)
        action_bar.set_margin_start(12)
        action_bar.set_margin_end(12)
        
        self.cancel_btn = Gtk.Button(label="Cancel")
        self.cancel_btn.connect("clicked", self.on_cancel_clicked)
        
        self.update_btn = Gtk.Button(label="Update Now")
        self.update_btn.add_css_class("suggested-action")
        self.update_btn.connect("clicked", self.on_update_clicked)
        
        action_bar.append(self.cancel_btn)
        action_bar.append(self.update_btn)
        self.updates_box.append(action_bar)
        
    def setup_progress_view(self):
        """Create the progress view for update operations."""
        self.progress_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=24)
        self.progress_box.set_valign(Gtk.Align.CENTER)
        self.progress_box.set_halign(Gtk.Align.CENTER)
        self.progress_box.set_vexpand(True)
        self.progress_box.set_margin_start(48)
        self.progress_box.set_margin_end(48)
        
        # Spinner for indeterminate progress
        self.spinner = Gtk.Spinner()
        self.spinner.set_size_request(48, 48)
        
        # Progress bar
        self.progress_bar = Gtk.ProgressBar()
        self.progress_bar.set_show_text(True)
        self.progress_bar.set_size_request(300, -1)
        
        # Progress label
        self.progress_label = Gtk.Label()
        self.progress_label.set_wrap(True)
        self.progress_label.set_justify(Gtk.Justification.CENTER)
        self.progress_label.add_css_class("title-3")
        
        # Progress detail label
        self.progress_detail = Gtk.Label()
        self.progress_detail.set_wrap(True)
        self.progress_detail.set_justify(Gtk.Justification.CENTER)
        self.progress_detail.add_css_class("dim-label")
        self.progress_detail.set_ellipsize(Pango.EllipsizeMode.MIDDLE)
        self.progress_detail.set_max_width_chars(60)
        
        self.progress_box.append(self.spinner)
        self.progress_box.append(self.progress_bar)
        self.progress_box.append(self.progress_label)
        self.progress_box.append(self.progress_detail)
        
    def setup_log_view(self):
        """Create the log viewer."""
        self.log_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=0)
        self.log_box.set_vexpand(True)
        
        # Scrolled window for log text
        scrolled = Gtk.ScrolledWindow()
        scrolled.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
        scrolled.set_vexpand(True)
        
        # Text view for log content
        self.log_view = Gtk.TextView()
        self.log_view.set_editable(False)
        self.log_view.set_monospace(True)
        self.log_view.set_wrap_mode(Gtk.WrapMode.WORD_CHAR)
        self.log_view.set_left_margin(12)
        self.log_view.set_right_margin(12)
        self.log_view.set_top_margin(12)
        self.log_view.set_bottom_margin(12)
        
        self.log_buffer = self.log_view.get_buffer()
        scrolled.set_child(self.log_view)
        self.log_box.append(scrolled)
        
        # Log action bar
        log_action_bar = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=12)
        log_action_bar.set_halign(Gtk.Align.END)
        log_action_bar.set_margin_top(12)
        log_action_bar.set_margin_bottom(12)
        log_action_bar.set_margin_start(12)
        log_action_bar.set_margin_end(12)
        
        self.log_close_btn = Gtk.Button(label="Close")
        self.log_close_btn.connect("clicked", self.on_log_close_clicked)
        
        self.log_save_btn = Gtk.Button(label="Save Log")
        self.log_save_btn.add_css_class("suggested-action")
        self.log_save_btn.connect("clicked", self.on_save_log_clicked)
        
        log_action_bar.append(self.log_close_btn)
        log_action_bar.append(self.log_save_btn)
        self.log_box.append(log_action_bar)
        
    def clear_main_box(self):
        """Remove all children from main box."""
        while child := self.main_box.get_first_child():
            self.main_box.remove(child)
            
    def show_status_page(self, title: str, icon: str, description: str = ""):
        """Display the status page with given message."""
        self.clear_main_box()
        self.status_page.set_title(title)
        self.status_page.set_icon_name(icon)
        self.status_page.set_description(description)
        self.main_box.append(self.status_page)
        
    def show_updates_list(self):
        """Display the updates list view."""
        self.clear_main_box()
        self.main_box.append(self.updates_box)
        
    def show_progress(self, message: str, detail: str = "", pulse: bool = False):
        """Display the progress view."""
        self.clear_main_box()
        self.progress_label.set_text(message)
        self.progress_detail.set_text(detail)
        
        if pulse:
            self.spinner.set_visible(True)
            self.spinner.start()
            self.progress_bar.set_visible(False)
        else:
            self.spinner.set_visible(False)
            self.spinner.stop()
            self.progress_bar.set_visible(True)
            
        self.main_box.append(self.progress_box)
        
    def show_log_viewer(self, title: str = "Updates Log"):
        """Display the log viewer."""
        self.clear_main_box()
        self.header.set_title_widget(Gtk.Label(label=title))
        
        # Load log content
        try:
            with open(self.app.LOG_FILE, 'r') as f:
                self.log_buffer.set_text(f.read())
        except Exception as e:
            self.log_buffer.set_text(f"Error loading log: {e}")
            
        self.main_box.append(self.log_box)
    
    def show_start_dialog(self):
        """Present the main window and start fetching updates immediately."""
        self.show_progress(
            "Fetching Updates List",
            "Linux Lite is now fetching the Updates list...",
            pulse=True
        )
        self.present()
        self.check_internet_and_start()
        
    def check_internet_and_start(self):
        """Check internet connectivity and start update process."""
        self.show_progress("Checking internet connection...", pulse=True)
        thread = threading.Thread(target=self._check_internet_thread)
        thread.daemon = True
        thread.start()
        
    def _check_internet_thread(self):
        """Thread function to check internet."""
        try:
            result = subprocess.run(
                ['curl', '-sk', 'google.com'],
                capture_output=True,
                timeout=10
            )
            has_internet = result.returncode == 0
        except Exception:
            has_internet = False
            
        GLib.idle_add(self._on_internet_check_done, has_internet)
        
    def _on_internet_check_done(self, has_internet: bool):
        """Handle internet check result."""
        if not has_internet:
            self.show_no_internet_dialog()
        else:
            self.kill_package_managers()
            # Start the root helper (single pkexec for all operations)
            self.show_progress("Authenticating...", "Please enter your password", pulse=True)
            self.app.start_root_helper(self._on_helper_started)
        return False
            
    def _on_helper_started(self, success: bool):
        """Handle root helper start result."""
        if success:
            # Check for dpkg lock before proceeding
            self.show_progress("Checking package manager...", pulse=True)
            thread = threading.Thread(target=self._check_dpkg_lock)
            thread.daemon = True
            thread.start()
        else:
            self.present()
            self.show_status_page(
                "Authentication Cancelled",
                "dialog-password-symbolic",
                "Updates require administrator privileges."
            )
            close_btn = Gtk.Button(label="Close")
            close_btn.set_halign(Gtk.Align.CENTER)
            close_btn.connect("clicked", lambda b: self.close())
            self.status_page.set_child(close_btn)
        return False
    
    def _check_dpkg_lock(self):
        """Check if dpkg is locked by another process."""
        try:
            self.app.send_command('CHECK_LOCK')
            
            for line in self.app.read_helper_output():
                print(f"[DEBUG] Lock check: {line}")
                if line.startswith('STATUS:LOCK_OK'):
                    GLib.idle_add(self.fetch_updates)
                    return
                elif line.startswith('STATUS:LOCK_HELD'):
                    GLib.idle_add(self._on_dpkg_locked)
                    return
                elif line.startswith('LOCK:'):
                    # Lock info message
                    lock_msg = line[5:]
                    print(f"[DEBUG] Lock held: {lock_msg}")
            
            # If we get here, assume it's ok
            GLib.idle_add(self.fetch_updates)
            
        except Exception as e:
            print(f"[DEBUG] Lock check error: {e}")
            GLib.idle_add(self.fetch_updates)
    
    def _on_dpkg_locked(self):
        """Handle when dpkg is locked by another process."""
        dialog = Adw.MessageDialog(
            transient_for=self,
            heading="Package Manager Busy",
            body="Another package manager is currently running.\n\nPlease close any other software installation programs (such as Synaptic, Software Center, or another terminal running apt) and try again."
        )
        dialog.add_response("cancel", "Cancel")
        dialog.add_response("retry", "Try Again")
        dialog.set_response_appearance("retry", Adw.ResponseAppearance.SUGGESTED)
        dialog.connect("response", self._on_dpkg_lock_response)
        dialog.present()
        return False
    
    def _on_dpkg_lock_response(self, dialog, response):
        """Handle dpkg lock dialog response."""
        dialog.close()
        if response == "retry":
            self._check_dpkg_lock_thread()
        else:
            self.close()
    
    def _check_dpkg_lock_thread(self):
        """Start dpkg lock check in thread."""
        self.show_progress("Checking package manager...", pulse=True)
        thread = threading.Thread(target=self._check_dpkg_lock)
        thread.daemon = True
        thread.start()
            
    def show_no_internet_dialog(self):
        """Show dialog when there's no internet connection."""
        dialog = Adw.MessageDialog(
            transient_for=self,
            heading="No Internet Connection",
            body="Your computer is not connected to the Internet.\n\nLinux Lite cannot check for Updates.\nPlease check your internet connection and try again."
        )
        dialog.add_response("ok", "OK")
        dialog.connect("response", lambda d, r: self.close())
        dialog.present()
        
    def kill_package_managers(self):
        """Kill any running package managers."""
        for proc in ['synaptic', 'gdebi-gtk']:
            try:
                subprocess.run(['pkill', '-9', proc], capture_output=True)
            except Exception:
                pass
                
    def fetch_updates(self):
        """Fetch available updates."""
        self.show_progress("Updating package lists...", "This may take a moment", pulse=True)
        thread = threading.Thread(target=self._fetch_updates_thread)
        thread.daemon = True
        thread.start()
        
    def _fetch_updates_thread(self):
        """Thread function to fetch updates using root helper."""
        update_success = False
        
        try:
            print("[DEBUG] Sending UPDATE command to root helper...")
            self.app.send_command('UPDATE')
            
            # Read output until we get status
            for line in self.app.read_helper_output():
                print(f"[DEBUG] Helper: {line}")
                
                if line.startswith('STATUS:'):
                    status = line[7:]
                    if status == 'UPDATE_OK':
                        update_success = True
                        break
                    elif status == 'UPDATE_FAILED':
                        update_success = False
                        break
                else:
                    if line.startswith(('Get:', 'Hit:', 'Ign:', 'Fetched')):
                        GLib.idle_add(self._update_fetch_progress, line.strip()[:80])
                    
        except Exception as e:
            print(f"[DEBUG] Update exception: {e}")
            GLib.idle_add(self._on_fetch_error, f"apt-get update failed: {str(e)}")
            return
            
        if not update_success:
            GLib.idle_add(self._on_fetch_error, "Failed to update package lists")
            return
            
        # Get list of available updates
        try:
            GLib.idle_add(self._update_fetch_progress, "Checking for available updates...")
            print("[DEBUG] Sending CHECK command to root helper...")
            self.app.send_command('CHECK')
            
            check_output = []
            for line in self.app.read_helper_output():
                print(f"[DEBUG] Helper: {line}")
                if line.startswith('STATUS:'):
                    if line[7:] == 'CHECK_DONE':
                        break
                else:
                    check_output.append(line)
            
            updates = []
            full_output = '\n'.join(check_output)

            # Pattern to extract package info including repository
            # Format: Inst package [old_ver] (new_ver repository)
            pattern = r'Inst\s+([\w\-\d\.~:+]+)\s+\[([\w\-\d\.~:+]+)\]\s+\(([\w\-\d\.~:+]+)\s+([^\)]+)\)'
            for match in re.finditer(pattern, full_output):
                pkg_name = match.group(1)
                old_ver = match.group(2)
                new_ver = match.group(3)
                repo_info = match.group(4) if match.lastindex >= 4 else ""
                
                # Detect security update from repository info
                is_security = 'security' in repo_info.lower() or '-security' in repo_info.lower()
                
                updates.append(UpdateItem(
                    name=pkg_name,
                    installed=old_ver,
                    available=new_ver,
                    is_security=is_security
                ))
            
            # If no matches with repo info, try simpler pattern
            if not updates:
                pattern = r'Inst\s+([\w\-\d\.~:+]+)\s+\[([\w\-\d\.~:+]+)\]\s+\(([\w\-\d\.~:+]+)'
                for match in re.finditer(pattern, full_output):
                    updates.append(UpdateItem(
                        name=match.group(1),
                        installed=match.group(2),
                        available=match.group(3)
                    ))
            
            print(f"[DEBUG] Parsed {len(updates)} updates")

            # Get download sizes using SIZES command
            total_dl_size = 0
            if updates:
                GLib.idle_add(self._update_fetch_progress, "Calculating download size...")
                self.app.send_command('SIZES')
                size_map = {}
                for line in self.app.read_helper_output():
                    print(f"[DEBUG] Helper: {line}")
                    if line.startswith('SIZE:'):
                        parts = line.split(':')
                        if len(parts) == 3:
                            try:
                                size_map[parts[1]] = int(parts[2])
                            except ValueError:
                                pass
                    elif line.startswith('STATUS:SIZES_DONE'):
                        break

                for update in updates:
                    if update.name in size_map:
                        update.size = size_map[update.name]
                        total_dl_size += update.size

            self.app.total_download_size = total_dl_size
            print(f"[DEBUG] Total download size: {total_dl_size}")
            GLib.idle_add(self._on_updates_fetched, updates)
            
        except Exception as e:
            print(f"[DEBUG] Check exception: {e}")
            GLib.idle_add(self._on_fetch_error, f"Error checking updates: {str(e)}")
            
    def _update_fetch_progress(self, detail: str):
        """Update the progress detail during fetch."""
        self.progress_detail.set_text(detail)
        return False
        
    def _on_fetch_error(self, error: str):
        """Handle fetch error."""
        self.present()
        self.show_error_dialog("Error Fetching Updates", error, show_log=True)
        return False
        
    def _on_updates_fetched(self, updates: list):
        """Handle fetched updates list."""
        print(f"[DEBUG] _on_updates_fetched called with {len(updates)} updates")
        self.app.updates = updates
        
        # Save check timestamp
        self.app.save_check_timestamp()
        
        # Count security updates
        self.app.security_count = sum(1 for u in updates if u.is_security)
        self.app.total_download_size = sum(u.size for u in updates)
        
        print(f"[DEBUG] Security updates: {self.app.security_count}")
        
        if not updates:
            print("[DEBUG] No updates, showing up-to-date dialog")
            self.present()
            self.show_up_to_date_dialog()
        else:
            print("[DEBUG] Populating updates list...")
            self.populate_updates_list(updates)
            print("[DEBUG] Showing updates list...")
            self.present()
            self.show_updates_list()
        return False
        
    def show_up_to_date_dialog(self):
        """Show dialog when system is up to date."""
        # Get last checked time
        last_checked = self.app.load_check_timestamp()
        
        self.show_status_page(
            "System Up to Date",
            "emblem-ok-symbolic",
            f"Your system is up to date.\n\nLast checked: {last_checked}"
        )
        
        # Button box for Check Again and Close
        button_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=12)
        button_box.set_halign(Gtk.Align.CENTER)
        
        check_again_btn = Gtk.Button(label="Check Again")
        check_again_btn.connect("clicked", self._on_check_again_clicked)
        
        close_btn = Gtk.Button(label="Close")
        close_btn.connect("clicked", lambda b: self.close())
        
        button_box.append(check_again_btn)
        button_box.append(close_btn)
        
        self.status_page.set_child(button_box)
    
    def _on_check_again_clicked(self, button):
        """Handle Check Again button click."""
        # Restart the update check process
        if self.app.root_helper and self.app.root_helper.poll() is None:
            # Helper still running, reuse it
            self.fetch_updates()
        else:
            # Need to start helper again
            self.show_progress("Authenticating...", "Please enter your password", pulse=True)
            self.app.start_root_helper(self._on_helper_started)
        
    def populate_updates_list(self, updates: list):
        """Populate the updates list with package info."""
        # Rebuild the content box
        content_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=12)
        
        # Summary banner
        summary_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=8)
        summary_box.add_css_class("card")
        summary_box.set_margin_bottom(12)
        
        # Summary content
        summary_content = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=16)
        summary_content.set_margin_top(16)
        summary_content.set_margin_bottom(12)
        summary_content.set_margin_start(16)
        summary_content.set_margin_end(16)
        
        # Update icon
        update_icon = Gtk.Image.new_from_icon_name("software-update-available-symbolic")
        update_icon.set_pixel_size(48)
        summary_content.append(update_icon)
        
        # Summary text
        summary_text_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=4)
        summary_text_box.set_hexpand(True)
        
        summary_title = Gtk.Label(label=f"{len(updates)} Update(s) Available")
        summary_title.set_halign(Gtk.Align.START)
        summary_title.add_css_class("title-3")
        
        # Total download size parsed from apt output
        total_size = self.app.total_download_size
        size_str = self._format_size(total_size) if total_size > 0 else "unknown"
        summary_details = Gtk.Label(label=f"Download size: {size_str}")
        summary_details.set_halign(Gtk.Align.START)
        summary_details.add_css_class("dim-label")
        
        summary_text_box.append(summary_title)
        summary_text_box.append(summary_details)
        
        # Security updates notice
        security_count = sum(1 for u in updates if u.is_security)
        if security_count > 0:
            security_label = Gtk.Label()
            security_label.set_markup(f'<span color="#e5a50a">⚠ {security_count} security update(s)</span>')
            security_label.set_halign(Gtk.Align.START)
            summary_text_box.append(security_label)
        
        summary_content.append(summary_text_box)
        summary_box.append(summary_content)
        
        # Last checked label
        last_checked = self.app.load_check_timestamp()
        last_checked_label = Gtk.Label(label=f"Last checked: {last_checked}")
        last_checked_label.set_halign(Gtk.Align.START)
        last_checked_label.add_css_class("dim-label")
        last_checked_label.set_margin_start(16)
        last_checked_label.set_margin_bottom(12)
        summary_box.append(last_checked_label)
        
        content_box.append(summary_box)
        
        # Updates list
        updates_group = Adw.PreferencesGroup()
        updates_group.set_title("Packages")
        
        # Add update rows
        for i, update in enumerate(updates, 1):
            row = Adw.ActionRow()
            row.set_title(update.name)
            row.set_subtitle(f"{update.installed} → {update.available}")
            
            # Add number prefix with optional security badge
            prefix_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=8)
            
            num_label = Gtk.Label(label=f"{i}")
            num_label.add_css_class("dim-label")
            num_label.set_width_chars(3)
            prefix_box.append(num_label)
            
            if update.is_security:
                security_icon = Gtk.Image.new_from_icon_name("security-high-symbolic")
                security_icon.set_tooltip_text("Security Update")
                security_icon.add_css_class("warning")
                prefix_box.append(security_icon)
            
            row.add_prefix(prefix_box)
            
            # Add changelog button as suffix
            changelog_btn = Gtk.Button()
            changelog_btn.set_icon_name("document-open-symbolic")
            changelog_btn.set_tooltip_text("View Changelog")
            changelog_btn.set_valign(Gtk.Align.CENTER)
            changelog_btn.add_css_class("flat")
            changelog_btn.connect("clicked", self._on_changelog_clicked, update.name)
            row.add_suffix(changelog_btn)
            
            updates_group.add(row)
        
        content_box.append(updates_group)
        self.updates_clamp.set_child(content_box)
        
        print(f"[DEBUG] Added {len(updates)} rows to updates group")
    
    def _format_size(self, size_bytes: int) -> str:
        """Format byte size to human readable string."""
        if size_bytes < 1024:
            return f"{size_bytes} B"
        elif size_bytes < 1024 * 1024:
            return f"{size_bytes / 1024:.1f} KB"
        else:
            return f"{size_bytes / (1024 * 1024):.1f} MB"
    
    def _on_changelog_clicked(self, button, package_name: str):
        """Handle changelog button click."""
        self._show_changelog_dialog(package_name)
    
    def _show_changelog_dialog(self, package_name: str):
        """Show changelog dialog for a package."""
        dialog = Adw.MessageDialog(
            transient_for=self,
            heading=f"Changelog: {package_name}",
            body="Fetching changelog..."
        )
        dialog.add_response("close", "Close")
        dialog.present()
        
        # Fetch changelog in background
        def fetch_changelog():
            try:
                if self.app.root_helper and self.app.root_helper.poll() is None:
                    self.app.send_command(f'CHANGELOG:{package_name}')
                    
                    changelog_lines = []
                    for line in self.app.read_helper_output():
                        if line.startswith('STATUS:CHANGELOG_DONE'):
                            break
                        elif not line.startswith('STATUS:'):
                            changelog_lines.append(line)
                    
                    changelog = '\n'.join(changelog_lines[:50])  # Limit to first 50 lines
                    if not changelog:
                        changelog = "No changelog available."
                else:
                    # Fallback - run apt-get changelog directly (won't work without root for some packages)
                    result = subprocess.run(
                        ['apt-get', 'changelog', package_name],
                        capture_output=True, text=True, timeout=30
                    )
                    changelog = result.stdout[:3000] if result.stdout else "No changelog available."
                    
                GLib.idle_add(self._update_changelog_dialog, dialog, changelog)
            except Exception as e:
                GLib.idle_add(self._update_changelog_dialog, dialog, f"Error fetching changelog: {e}")
        
        thread = threading.Thread(target=fetch_changelog)
        thread.daemon = True
        thread.start()
    
    def _update_changelog_dialog(self, dialog, changelog: str):
        """Update changelog dialog with content."""
        # Truncate if too long for dialog
        if len(changelog) > 2000:
            changelog = changelog[:2000] + "\n\n... (truncated)"
        dialog.set_body(changelog)
        return False
            
    def on_cancel_clicked(self, button):
        """Handle cancel button click."""
        dialog = Adw.MessageDialog(
            transient_for=self,
            heading="Cancel Updates?",
            body="Are you sure you want to cancel the update process?"
        )
        dialog.add_response("no", "No")
        dialog.add_response("yes", "Yes, Cancel")
        dialog.set_response_appearance("yes", Adw.ResponseAppearance.DESTRUCTIVE)
        dialog.connect("response", self._on_cancel_confirm)
        dialog.present()
        
    def _on_cancel_confirm(self, dialog, response):
        """Handle cancel confirmation."""
        dialog.close()
        if response == "yes":
            self.show_toast("Updates have been canceled")
            self.close()
            
    def on_update_clicked(self, button):
        """Handle update button click."""
        self.perform_updates()
        
    def perform_updates(self):
        """Perform the actual system update."""
        self.show_progress("Downloading packages...", "This may take a while", pulse=False)
        self.progress_bar.set_fraction(0)
        
        thread = threading.Thread(target=self._perform_updates_thread)
        thread.daemon = True
        thread.start()
        
    def _perform_updates_thread(self):
        """Thread function to perform updates using root helper."""
        try:
            print("[DEBUG] Sending UPGRADE command to root helper...")
            self.app.send_command('UPGRADE')
            
            total_packages = len(self.app.updates)
            downloaded = 0
            installing = False
            success = False
            
            for line in self.app.read_helper_output():
                print(f"[DEBUG] Helper: {line}")
                
                if line.startswith('STATUS:'):
                    status = line[7:]
                    if status == 'UPGRADE_OK':
                        success = True
                        break
                    elif status == 'UPGRADE_FAILED':
                        success = False
                        break
                else:
                    # Parse progress
                    if line.startswith('Get:'):
                        downloaded += 1
                        progress = min(downloaded / max(total_packages, 1), 1.0)
                        GLib.idle_add(self._update_install_progress, 
                                    "Downloading packages...", 
                                    line.strip()[:80],
                                    progress * 0.5)
                                    
                    elif any(line.startswith(s) for s in ['Unpacking', 'Setting up', 'Processing']):
                        if not installing:
                            installing = True
                            GLib.idle_add(self._update_install_progress,
                                        "Installing packages...",
                                        "", 0.5)
                        GLib.idle_add(self._update_install_progress,
                                    "Installing packages...",
                                    line.strip()[:80],
                                    0.5 + (0.5 * (downloaded / max(total_packages, 1))))
                
        except Exception as e:
            print(f"[DEBUG] Upgrade exception: {e}")
            GLib.idle_add(self._on_update_error, str(e))
            return
            
        if success:
            GLib.idle_add(self._on_updates_complete)
        else:
            GLib.idle_add(self._on_update_error, "Update process failed")
            
    def _update_install_progress(self, message: str, detail: str, fraction: float):
        """Update progress during installation."""
        self.progress_label.set_text(message)
        self.progress_detail.set_text(detail)
        self.progress_bar.set_fraction(min(fraction, 1.0))
        return False
        
    def _on_update_error(self, error: str):
        """Handle update error."""
        self.show_error_dialog("Error During Update", error, show_log=True)
        return False
        
    def _on_updates_complete(self):
        """Handle successful update completion with an inline page."""
        # Check for plymouth theme update (Linux Lite specific)
        self._check_plymouth_update()

        # Show inline completion page (no popup = no taskbar icon hijack)
        self.show_completion_page()
        return False

    def show_completion_page(self):
        """Show an inline completion page inside the main window."""
        self.clear_main_box()

        reboot_required = os.path.exists('/var/run/reboot-required')

        if reboot_required:
            heading = "Updates Complete — Restart Required"
            description = ("Linux Lite Updates completed successfully.\n\n"
                           "These updates require the system to be restarted "
                           "for the changes to take effect.")
            icon = "system-reboot-symbolic"
        else:
            heading = "Updates Complete"
            description = "Linux Lite Updates completed successfully."
            icon = "emblem-ok-symbolic"

        page = Adw.StatusPage()
        page.set_vexpand(True)
        page.set_icon_name(icon)
        page.set_title(heading)
        page.set_description(description)

        btn_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=12)
        btn_box.set_halign(Gtk.Align.CENTER)

        log_btn = Gtk.Button(label="View Log")
        log_btn.add_css_class("pill")
        log_btn.connect("clicked", lambda b: self.show_log_viewer("Updates Log"))
        btn_box.append(log_btn)

        if reboot_required:
            later_btn = Gtk.Button(label="Continue Using Computer")
            later_btn.add_css_class("pill")
            later_btn.connect("clicked", lambda b: self.close())
            btn_box.append(later_btn)

            restart_btn = Gtk.Button(label="Restart Now")
            restart_btn.add_css_class("pill")
            restart_btn.add_css_class("suggested-action")
            restart_btn.connect("clicked", self._on_restart_clicked)
            btn_box.append(restart_btn)
        else:
            close_btn = Gtk.Button(label="Close")
            close_btn.add_css_class("pill")
            close_btn.add_css_class("suggested-action")
            close_btn.connect("clicked", lambda b: self.close())
            btn_box.append(close_btn)

        page.set_child(btn_box)
        self.main_box.append(page)

    def _on_restart_clicked(self, button):
        if self.app.root_helper and self.app.root_helper.poll() is None:
            self.app.send_command('REBOOT')
        else:
            subprocess.run(['pkexec', 'reboot'])
        self.close()

    def _check_plymouth_update(self):
        """Check and update Plymouth theme if needed.
        
        Note: This is a Linux Lite-specific customization that updates the
        boot splash theme after system updates. Uses the existing root helper.
        """
        plymouth_file = "/usr/share/plymouth/themes/ubuntu-text/ubuntu-text.plymouth"
        if not os.path.exists(plymouth_file):
            return
            
        try:
            with open(plymouth_file, 'r') as f:
                content = f.read()
                
            # Only update if the ubuntu colors are present
            if '0x988592' not in content:
                return
                
            # Get Linux Lite version
            llver = ""
            if os.path.exists('/etc/llver'):
                with open('/etc/llver', 'r') as f:
                    llver = f.read().strip()
                    
            # Update plymouth theme content
            content = re.sub(r'^title=Ubuntu.*$', f'title={llver}', content, flags=re.MULTILINE)
            content = content.replace('black=0x2c001e', 'black=0x000000')
            content = content.replace('brown=0xff4012', 'brown=0xffff00')
            content = content.replace('blue=0x988592', 'blue=0x000000')
            
            # Write to temp file
            tmp_path = '/tmp/plymouth_update_content.plymouth'
            with open(tmp_path, 'w') as f:
                f.write(content)
            
            # Use root helper to update plymouth
            if self.app.root_helper and self.app.root_helper.poll() is None:
                self.app.send_command(f'PLYMOUTH:{tmp_path}')
                # Read response (but don't block forever)
                try:
                    for line in self.app.read_helper_output():
                        if line.startswith('STATUS:PLYMOUTH'):
                            break
                except Exception:
                    pass
                    
        except Exception as e:
            print(f"[DEBUG] Plymouth update skipped: {e}")
            
    def on_log_close_clicked(self, button):
        """Handle log close button click — go back to the completion page."""
        self.show_completion_page()
        
    def on_save_log_clicked(self, button):
        """Handle save log button click."""
        dialog = Gtk.FileDialog()
        dialog.set_title("Save Updates Log")
        
        # Set default filename
        date_str = datetime.now().strftime("%A-%d-%B-%Y-%H%M%S")
        home = Path.home()
        dialog.set_initial_name(f"llupdates-{date_str}.log")
        dialog.set_initial_folder(Gio.File.new_for_path(str(home)))
        
        # Create filter
        filter_log = Gtk.FileFilter()
        filter_log.set_name("Log files")
        filter_log.add_pattern("*.log")
        filter_log.add_pattern("*.txt")
        
        filters = Gio.ListStore.new(Gtk.FileFilter)
        filters.append(filter_log)
        dialog.set_filters(filters)
        
        dialog.save(self, None, self._on_save_log_response)
        
    def _on_save_log_response(self, dialog, result):
        """Handle save log dialog response."""
        try:
            file = dialog.save_finish(result)
            if file:
                path = file.get_path()
                # Copy log file
                subprocess.run(['cp', self.app.LOG_FILE, path])
                # Change ownership to original user
                if self.app.original_user:
                    subprocess.run(['chown', f'{self.app.original_user}:{self.app.original_user}', path])
                self.show_toast(f"Log saved to {path}")
        except Exception as e:
            if "Dismissed" not in str(e):
                self.show_error_dialog("Error Saving Log", str(e))
                
    def show_toast(self, message: str):
        """Show a toast notification."""
        toast = Adw.Toast(title=message)
        toast.set_timeout(3)
        
        # Create toast overlay if needed
        if not hasattr(self, 'toast_overlay'):
            self.toast_overlay = Adw.ToastOverlay()
            content = self.get_content()
            self.set_content(self.toast_overlay)
            self.toast_overlay.set_child(content)
            
        self.toast_overlay.add_toast(toast)
        
    def show_error_dialog(self, heading: str, body: str, show_log: bool = False):
        """Show an error dialog."""
        dialog = Adw.MessageDialog(
            transient_for=self,
            heading=heading,
            body=body
        )
        dialog.add_response("ok", "OK")
        if show_log:
            dialog.add_response("log", "View Log")
            dialog.set_response_appearance("log", Adw.ResponseAppearance.SUGGESTED)
        dialog.connect("response", self._on_error_response, show_log)
        dialog.present()
        
    def _on_error_response(self, dialog, response, show_log):
        """Handle error dialog response."""
        dialog.close()
        if response == "log" and show_log:
            self.show_log_viewer("Error Log")
        else:
            self.close()


def main():
    """Main entry point."""
    parser = argparse.ArgumentParser(
        prog='lite-updates',
        description='Linux Lite Updates - A GUI tool to easily install Updates in Linux Lite.',
        epilog='Website: https://www.linuxliteos.com'
    )
    parser.add_argument('-v', '--version', action='version', 
                        version=f'Linux Lite Updates {APP_VERSION}')
    parser.add_argument('-c', '--check-only', action='store_true',
                        help='Check for updates and exit (no GUI)')
    parser.add_argument('-l', '--list', action='store_true',
                        help='List available updates and exit (no GUI)')
    
    args = parser.parse_args()
    
    # Handle check-only mode
    if args.check_only or args.list:
        # Run apt-get update and check for updates without GUI
        print("Checking for updates...")
        result = subprocess.run(['apt-get', '--just-print', 'dist-upgrade'],
                              capture_output=True, text=True)
        
        updates = []
        pattern = r'Inst\s+([\w\-\d\.~:+]+)\s+\[([\w\-\d\.~:+]+)\]\s+\(([\w\-\d\.~:+]+)'
        for match in re.finditer(pattern, result.stdout):
            updates.append((match.group(1), match.group(2), match.group(3)))
        
        if not updates:
            print("Your system is up to date.")
            return 0
        
        if args.list:
            print(f"\n{len(updates)} update(s) available:\n")
            for name, old_ver, new_ver in updates:
                print(f"  {name}: {old_ver} → {new_ver}")
            print()
        else:
            print(f"{len(updates)} update(s) available.")
        
        return 0
    
    # Normal GUI mode - verify display is available
    if not os.environ.get('DISPLAY') and not os.environ.get('WAYLAND_DISPLAY'):
        print("Error: No display server found. lite-updates requires a graphical environment.")
        print("Do not run lite-updates with pkexec or sudo. It handles privilege escalation internally.")
        return 1

    app = LiteUpdatesApp()
    return app.run(None)


if __name__ == "__main__":
    exit(main())
