#!/usr/bin/env python3
#
# Copyright (c) 2025 Stormux
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.

"""AI Assistant plugin for Cthulhu screen reader."""

import logging
import os
import json
import base64
from io import BytesIO

import gi
gi.require_version('Gdk', '3.0')
gi.require_version('GdkPixbuf', '2.0')
gi.require_version('Atspi', '2.0')
gi.require_version('Gtk', '3.0')
from gi.repository import Gdk, GdkPixbuf, Atspi, Gtk

from cthulhu.plugin import Plugin, cthulhu_hookimpl
from cthulhu import settings
from cthulhu import settings_manager
from cthulhu import cthulhu_state
from cthulhu import ax_object
from cthulhu.ax_text import AXText
from cthulhu.ax_value import AXValue
from cthulhu import ax_utilities
from cthulhu.ax_utilities_state import AXUtilitiesState
from cthulhu.plugins.AIAssistant.ai_providers import create_provider

logger = logging.getLogger(__name__)

class AIAssistant(Plugin):
    """AI-powered accessibility assistant plugin.
    
    Provides AI-enhanced accessibility features including:
    - Screen analysis using screenshots and AT-SPI data
    - Natural language queries about UI elements
    - Safe action assistance with user confirmation
    - Multi-provider AI support (Claude, ChatGPT, Gemini, Ollama)
    """
    
    def __init__(self, *args, **kwargs):
        """Initialize the AI Assistant plugin."""
        super().__init__(*args, **kwargs)

        logger.info("AI ASSISTANT: Plugin __init__ starting")
        logger.info("AI ASSISTANT: Plugin initialized successfully")
        
        # Menu and keybinding storage
        self._kb_binding_menu = None
        self._menu_gui = None
        
        # AI provider and settings
        self._provider_type = None
        self._ai_provider = None
        self._api_key = None
        self._ollama_model = None
        self._ollama_endpoint = None
        self._settings_manager = settings_manager.getManager()
        
        # Plugin enabled state
        self._enabled = False
        
        # Pre-captured screen data (to avoid capturing dialog itself)
        self._current_screen_data = None
        self._prefs_grid = None
        self._prefs_widgets = {}
        
    @cthulhu_hookimpl
    def activate(self, plugin=None):
        """Activate the AI Assistant plugin."""
        if plugin is not None and plugin is not self:
            return
            
        # Prevent multiple activations
        if self._enabled:
            logger.info("AI ASSISTANT: Already activated, skipping")
            return
            
        try:
            logger.info("AI ASSISTANT: === Plugin activation starting ===")

            # Check if AI Assistant is enabled in settings
            enabled = self._settings_manager.getSetting('aiAssistantEnabled')
            logger.info(f"AI ASSISTANT: Enabled setting: {enabled}")
            if not enabled:
                logger.info("AI Assistant is disabled in settings, skipping activation")
                return
                
            # Load AI settings
            self._load_ai_settings()

            # Check if we have valid configuration
            config_valid = self._validate_configuration()
            logger.info(f"AI Assistant configuration valid: {config_valid}")

            # Initialize AI provider (may fail but we still want menu access)
            if config_valid:
                provider_init = self._initialize_ai_provider()
            else:
                logger.warning("AI Assistant configuration invalid, menu will show error messages")
                provider_init = False

            # Always register keybindings so menu is accessible even with config issues
            self._register_keybindings()

            self._enabled = True
            logger.info("AI Assistant plugin activated successfully")

        except Exception as e:
            logger.error(f"Error activating AI Assistant plugin: {e}")
            import traceback
            logger.error(traceback.format_exc())
    
    @cthulhu_hookimpl
    def deactivate(self, plugin=None):
        """Deactivate the AI Assistant plugin."""
        if plugin is not None and plugin is not self:
            return
            
        logger.info("Deactivating AI Assistant plugin")
        
        # Unregister keybindings
        self._unregister_keybindings()
        
        self._enabled = False

    def getPreferencesGUI(self):
        if not self._prefs_grid:
            self._prefs_grid = self._build_preferences_gui()
        self._load_preferences_into_widgets()
        return self._prefs_grid, "AI Assistant"

    def getPreferencesFromGUI(self):
        if not self._prefs_widgets:
            return {}

        provider_values = self._prefs_widgets.get("provider_values", [])
        quality_values = self._prefs_widgets.get("quality_values", [])
        provider_index = self._prefs_widgets["provider_combo"].get_active()
        quality_index = self._prefs_widgets["quality_combo"].get_active()

        if provider_index < 0 or provider_index >= len(provider_values):
            provider = settings.aiProvider
        else:
            provider = provider_values[provider_index]

        if quality_index < 0 or quality_index >= len(quality_values):
            screenshot_quality = settings.aiScreenshotQuality
        else:
            screenshot_quality = quality_values[quality_index]

        return {
            "aiAssistantEnabled": self._prefs_widgets["enable_check"].get_active(),
            "aiProvider": provider,
            "aiApiKeyFile": self._prefs_widgets["api_key_entry"].get_text().strip(),
            "aiOllamaModel": self._prefs_widgets["ollama_model_entry"].get_text().strip(),
            "aiOllamaEndpoint": self._prefs_widgets["ollama_endpoint_entry"].get_text().strip(),
            "aiConfirmationRequired": self._prefs_widgets["confirmation_check"].get_active(),
            "aiScreenshotQuality": screenshot_quality,
        }

    def _build_preferences_gui(self):
        grid = Gtk.Grid(
            row_spacing=6,
            column_spacing=12,
            margin_left=12,
            margin_right=12,
            margin_top=12,
            margin_bottom=12,
        )

        row = 0

        enable_check = Gtk.CheckButton(label="Enable AI Assistant")
        enable_check.connect("toggled", self._on_ai_enabled_toggled)
        grid.attach(enable_check, 0, row, 2, 1)
        row += 1

        provider_label = Gtk.Label(label="_Provider:")
        provider_label.set_use_underline(True)
        provider_label.set_halign(Gtk.Align.START)

        provider_combo = Gtk.ComboBoxText()
        provider_combo.set_hexpand(True)
        provider_values = [
            settings.AI_PROVIDER_CLAUDE_CODE,
            settings.AI_PROVIDER_CODEX,
            settings.AI_PROVIDER_GEMINI,
            settings.AI_PROVIDER_OLLAMA,
        ]
        provider_labels = ["Claude Code", "Codex", "Gemini", "Ollama"]
        for label in provider_labels:
            provider_combo.append_text(label)
        provider_combo.connect("changed", self._on_provider_changed)
        provider_label.set_mnemonic_widget(provider_combo)

        grid.attach(provider_label, 0, row, 1, 1)
        grid.attach(provider_combo, 1, row, 1, 1)
        row += 1

        api_key_label = Gtk.Label(label="API _Key File:")
        api_key_label.set_use_underline(True)
        api_key_label.set_halign(Gtk.Align.START)
        api_key_entry = Gtk.Entry()
        api_key_entry.set_hexpand(True)
        api_key_label.set_mnemonic_widget(api_key_entry)

        grid.attach(api_key_label, 0, row, 1, 1)
        grid.attach(api_key_entry, 1, row, 1, 1)
        row += 1

        ollama_model_label = Gtk.Label(label="_Ollama Model:")
        ollama_model_label.set_use_underline(True)
        ollama_model_label.set_halign(Gtk.Align.START)
        ollama_model_entry = Gtk.Entry()
        ollama_model_entry.set_hexpand(True)
        ollama_model_label.set_mnemonic_widget(ollama_model_entry)

        grid.attach(ollama_model_label, 0, row, 1, 1)
        grid.attach(ollama_model_entry, 1, row, 1, 1)
        row += 1

        ollama_endpoint_label = Gtk.Label(label="Ollama _Endpoint:")
        ollama_endpoint_label.set_use_underline(True)
        ollama_endpoint_label.set_halign(Gtk.Align.START)
        ollama_endpoint_entry = Gtk.Entry()
        ollama_endpoint_entry.set_hexpand(True)
        ollama_endpoint_label.set_mnemonic_widget(ollama_endpoint_entry)

        grid.attach(ollama_endpoint_label, 0, row, 1, 1)
        grid.attach(ollama_endpoint_entry, 1, row, 1, 1)
        row += 1

        confirmation_check = Gtk.CheckButton(label="Require confirmation before actions")
        grid.attach(confirmation_check, 0, row, 2, 1)
        row += 1

        quality_label = Gtk.Label(label="Screenshot _Quality:")
        quality_label.set_use_underline(True)
        quality_label.set_halign(Gtk.Align.START)
        quality_combo = Gtk.ComboBoxText()
        quality_combo.set_hexpand(True)
        quality_values = [
            settings.AI_SCREENSHOT_QUALITY_LOW,
            settings.AI_SCREENSHOT_QUALITY_MEDIUM,
            settings.AI_SCREENSHOT_QUALITY_HIGH,
        ]
        quality_labels = ["Low", "Medium", "High"]
        for label in quality_labels:
            quality_combo.append_text(label)
        quality_label.set_mnemonic_widget(quality_combo)

        grid.attach(quality_label, 0, row, 1, 1)
        grid.attach(quality_combo, 1, row, 1, 1)

        self._prefs_widgets = {
            "enable_check": enable_check,
            "provider_combo": provider_combo,
            "provider_values": provider_values,
            "api_key_entry": api_key_entry,
            "ollama_model_entry": ollama_model_entry,
            "ollama_endpoint_entry": ollama_endpoint_entry,
            "confirmation_check": confirmation_check,
            "quality_combo": quality_combo,
            "quality_values": quality_values,
        }

        return grid

    def _load_preferences_into_widgets(self):
        if not self._prefs_widgets:
            return

        enabled = self._settings_manager.getSetting("aiAssistantEnabled")
        if enabled is None:
            enabled = settings.aiAssistantEnabled
        self._prefs_widgets["enable_check"].set_active(enabled)

        provider = self._settings_manager.getSetting("aiProvider") or settings.aiProvider
        provider_values = self._prefs_widgets.get("provider_values", [])
        try:
            provider_index = provider_values.index(provider)
        except ValueError:
            provider_index = 0
        self._prefs_widgets["provider_combo"].set_active(provider_index)

        api_key_file = self._settings_manager.getSetting("aiApiKeyFile") or settings.aiApiKeyFile
        self._prefs_widgets["api_key_entry"].set_text(api_key_file or "")

        ollama_model = self._settings_manager.getSetting("aiOllamaModel") or settings.aiOllamaModel
        self._prefs_widgets["ollama_model_entry"].set_text(ollama_model or "")

        ollama_endpoint = self._settings_manager.getSetting("aiOllamaEndpoint") or settings.aiOllamaEndpoint
        self._prefs_widgets["ollama_endpoint_entry"].set_text(ollama_endpoint or "")

        confirmation_required = self._settings_manager.getSetting("aiConfirmationRequired")
        if confirmation_required is None:
            confirmation_required = settings.aiConfirmationRequired
        self._prefs_widgets["confirmation_check"].set_active(confirmation_required)

        screenshot_quality = self._settings_manager.getSetting("aiScreenshotQuality") or settings.aiScreenshotQuality
        quality_values = self._prefs_widgets.get("quality_values", [])
        try:
            quality_index = quality_values.index(screenshot_quality)
        except ValueError:
            quality_index = 0
        self._prefs_widgets["quality_combo"].set_active(quality_index)

        self._update_preferences_sensitivity()

    def _on_ai_enabled_toggled(self, widget):
        self._update_preferences_sensitivity()

    def _on_provider_changed(self, widget):
        self._update_preferences_sensitivity()

    def _update_preferences_sensitivity(self):
        if not self._prefs_widgets:
            return

        provider_values = self._prefs_widgets.get("provider_values", [])
        provider_index = self._prefs_widgets["provider_combo"].get_active()
        provider = provider_values[provider_index] if 0 <= provider_index < len(provider_values) else settings.aiProvider

        is_gemini = provider == settings.AI_PROVIDER_GEMINI
        is_ollama = provider == settings.AI_PROVIDER_OLLAMA

        # Keep preferences editable even when the feature is disabled so users
        # can prepare configuration before turning AI assistant on.
        self._prefs_widgets["provider_combo"].set_sensitive(True)
        self._prefs_widgets["api_key_entry"].set_sensitive(is_gemini)
        self._prefs_widgets["ollama_model_entry"].set_sensitive(is_ollama)
        self._prefs_widgets["ollama_endpoint_entry"].set_sensitive(is_ollama)
        self._prefs_widgets["confirmation_check"].set_sensitive(True)
        self._prefs_widgets["quality_combo"].set_sensitive(True)
        
    def refresh_settings(self):
        """Refresh plugin settings and reinitialize provider. Called when settings change."""
        try:
            logger.info("AI Assistant: Refreshing settings")

            # Reload settings
            self._load_ai_settings()

            # Validate new configuration
            config_valid = self._validate_configuration()

            # Reinitialize provider if configuration is valid
            if config_valid:
                old_provider = self._ai_provider
                provider_init = self._initialize_ai_provider()
                if provider_init:
                    logger.info(f"AI Assistant provider changed to: {self._provider_type}")
                else:
                    logger.warning("Failed to initialize new provider")
                    self._ai_provider = None
            else:
                logger.warning("New configuration invalid, clearing provider")
                self._ai_provider = None

        except Exception as e:
            logger.error(f"Error refreshing AI Assistant settings: {e}")
        
    def _load_ai_settings(self):
        """Load AI Assistant settings from Cthulhu configuration."""
        try:
            # Get provider
            provider = self._settings_manager.getSetting('aiProvider')
            self._provider_type = provider or settings.AI_PROVIDER_CLAUDE_CODE
            
            # Load API key from file
            api_key_file = self._settings_manager.getSetting('aiApiKeyFile')
            if api_key_file and os.path.isfile(api_key_file):
                with open(api_key_file, 'r') as f:
                    self._api_key = f.read().strip()
            else:
                self._api_key = None
                
            # Load Ollama model and endpoint
            self._ollama_model = self._settings_manager.getSetting('aiOllamaModel')
            if not self._ollama_model:
                self._ollama_model = settings.aiOllamaModel
                
            self._ollama_endpoint = self._settings_manager.getSetting('aiOllamaEndpoint')
            if not self._ollama_endpoint:
                self._ollama_endpoint = settings.aiOllamaEndpoint
                
            logger.info(f"AI settings loaded: provider={self._provider_type}, "
                       f"api_key_configured={bool(self._api_key)}, "
                       f"ollama_model={self._ollama_model}, "
                       f"ollama_endpoint={self._ollama_endpoint}")
                       
        except Exception as e:
            logger.error(f"Error loading AI settings: {e}")
            
    def _validate_configuration(self):
        """Validate AI Assistant configuration."""
        logger.info(f"Validating AI configuration - provider_type: {self._provider_type}")
        if not self._provider_type:
            logger.warning("No AI provider configured")
            return False
            
        # Check provider-specific requirements
        if self._provider_type == settings.AI_PROVIDER_OLLAMA:
            logger.info("Checking Ollama availability")
            return self._check_ollama_availability()
        elif self._provider_type == settings.AI_PROVIDER_CLAUDE_CODE:
            logger.info("Checking Claude Code availability")
            result = self._check_claude_code_availability()
            logger.info(f"Claude Code availability check result: {result}")
            return result
        elif self._provider_type == settings.AI_PROVIDER_CODEX:
            logger.info("Checking Codex CLI availability")
            result = self._check_codex_availability()
            logger.info(f"Codex CLI availability check result: {result}")
            return result
        elif self._provider_type == settings.AI_PROVIDER_GEMINI:
            logger.info("Checking Gemini API key")
            if not self._api_key:
                logger.warning("No API key configured for Gemini")
                return False
            logger.info("Gemini configuration validated")
            return True
        else:
            logger.warning(f"Unknown provider type: {self._provider_type}")
            return False
        
    def _check_ollama_availability(self):
        """Check if Ollama is available and has vision models."""
        try:
            import requests
            # Check if Ollama is running at the configured endpoint
            endpoint_url = f"{self._ollama_endpoint}/api/version"
            response = requests.get(endpoint_url, timeout=5)
            if response.status_code == 200:
                logger.info(f"Ollama service is available at {self._ollama_endpoint}")
                return True
            else:
                logger.warning(f"Ollama service not responding at {self._ollama_endpoint}")
                return False
        except Exception as e:
            logger.warning(f"Ollama not available at {self._ollama_endpoint}: {e}")
            return False
            
    def _check_claude_code_availability(self):
        """Check if Claude Code CLI is available."""
        try:
            import subprocess
            import shutil
            
            # First check if claude command exists in PATH
            if not shutil.which('claude'):
                logger.warning("Claude Code CLI not found in PATH")
                return False
                
            # Quick test to see if it responds
            result = subprocess.run(['claude', '--version'], 
                                   capture_output=True, text=True, timeout=5)
            if result.returncode == 0:
                logger.info("Claude Code CLI is available")
                return True
            else:
                logger.warning(f"Claude Code CLI not responding: {result.stderr}")
                return False
        except subprocess.TimeoutExpired:
            logger.warning("Claude Code CLI timeout")
            return False
        except Exception as e:
            logger.warning(f"Claude Code CLI not available: {e}")
            return False

    def _check_codex_availability(self):
        """Check if Codex CLI is available."""
        try:
            import subprocess
            import shutil

            codex_path = shutil.which('codex')
            if not codex_path and os.path.isfile('/usr/bin/codex'):
                codex_path = '/usr/bin/codex'

            if not codex_path:
                logger.warning("Codex CLI not found in PATH")
                return False

            result = subprocess.run([codex_path, '--version'],
                                   capture_output=True, text=True, timeout=5)
            if result.returncode == 0:
                logger.info("Codex CLI is available")
                return True
            else:
                logger.warning(f"Codex CLI not responding: {result.stderr}")
                return False
        except subprocess.TimeoutExpired:
            logger.warning("Codex CLI timeout")
            return False
        except Exception as e:
            logger.warning(f"Codex CLI not available: {e}")
            return False
            
    def _initialize_ai_provider(self):
        """Initialize the AI provider based on settings."""
        try:
            if self._provider_type == settings.AI_PROVIDER_CLAUDE_CODE:
                self._ai_provider = create_provider("claude_code")
            elif self._provider_type == settings.AI_PROVIDER_CODEX:
                self._ai_provider = create_provider("codex")
            elif self._provider_type == settings.AI_PROVIDER_OLLAMA:
                self._ai_provider = create_provider("ollama", model=self._ollama_model, base_url=self._ollama_endpoint)
            elif self._provider_type == settings.AI_PROVIDER_GEMINI:
                self._ai_provider = create_provider("gemini", api_key=self._api_key)
            else:
                logger.error(f"Unsupported provider type: {self._provider_type}")
                return False
                
            logger.info(f"AI provider initialized: {self._provider_type}")
            return True
            
        except Exception as e:
            logger.error(f"Error initializing AI provider: {e}")
            return False
            
    def _register_keybindings(self):
        """Register AI Assistant menu keybinding."""
        try:
            # Single keybinding to show AI Assistant menu
            self._kb_binding_menu = self.registerGestureByString(
                self._show_ai_menu,
                "Show AI Assistant menu",
                'kb:cthulhu+shift+control+a'
            )

            logger.info("AI Assistant menu keybinding registered")

        except Exception as e:
            logger.error(f"Error registering AI menu keybinding: {e}")
            
    def _unregister_keybindings(self):
        """Unregister AI Assistant keybindings."""
        # Keybindings are automatically cleaned up when plugin deactivates
        self._kb_binding_menu = None
        self._menu_gui = None
        
    def _show_ai_menu(self, script=None, inputEvent=None):
        """Show the AI Assistant menu."""
        try:
            logger.info("AI ASSISTANT: _show_ai_menu called!")

            # IMPORTANT: Capture screen data BEFORE showing menu
            # This ensures we get the actual screen content, not the menu itself
            self._pre_menu_screen_data = self._collect_ai_data()
            logger.info("Pre-captured screen data for menu actions")

            # Now show the menu
            self._menu_gui = AIAssistantMenu(self._handle_menu_selection)
            self._menu_gui.show_gui()
            return True
        except Exception as e:
            logger.error(f"Error showing AI menu: {e}")
            import traceback
            traceback.print_exc()
            return False
    
    def _handle_menu_selection(self, action_id):
        """Handle AI Assistant menu selection."""
        try:
            logger.info(f"AI menu selection: {action_id}")
            
            if action_id == "ask_question":
                self._handle_ai_question_with_data(self._pre_menu_screen_data)
            elif action_id == "describe_screen":
                self._handle_ai_describe_with_data(self._pre_menu_screen_data)
            elif action_id == "request_action":
                self._handle_ai_activate_with_data(self._pre_menu_screen_data)
            elif action_id == "analyze_images":
                self._handle_ai_image_analysis_with_data(self._pre_menu_screen_data)
            elif action_id == "browse_image_file":
                self._handle_browse_image_file()
            else:
                logger.warning(f"Unknown AI menu action: {action_id}")
                
        except Exception as e:
            logger.error(f"Error handling menu selection {action_id}: {e}")
    
    def _handle_ai_image_analysis_with_data(self, data):
        """Handle AI image analysis request with pre-captured data."""
        try:
            logger.info("AI image analysis requested with pre-captured data")
            
            if not self._enabled:
                self._present_message("AI Assistant is not enabled")
                return True
                
            if not self._ai_provider:
                self._present_message("AI provider not available. Check configuration.")
                return True
                
            if not data:
                self._present_message("No screen data available for image analysis")
                return True
                
            provider_name = self._provider_type.replace('_', ' ').title()
            self._present_message(f"AI Assistant ({provider_name}) analyzing images...")
            
            # Use AI to analyze images on screen
            try:
                response = self._ai_provider.analyze_images(None, 
                                                          data.get("screenshot"), 
                                                          data.get("accessibility"))
                self._show_description_dialog(response)
            except Exception as e:
                logger.error(f"Error getting AI image analysis: {e}")
                self._present_message(f"Error getting AI image analysis: {e}")
            
            return True
            
        except Exception as e:
            logger.error(f"Error in image analysis with data: {e}")
            self._present_message(f"Error analyzing images: {e}")
            return False
    
    def _handle_browse_image_file(self):
        """Handle browsing for an image file to analyze."""
        try:
            logger.info("AI image file browsing requested")

            if not self._enabled:
                self._present_message("AI Assistant is not enabled")
                return True

            if not self._ai_provider:
                self._present_message("AI provider not available. Check configuration.")
                return True

            # Show file chooser dialog
            image_file = self._show_image_file_chooser()
            
            if image_file:
                provider_name = self._provider_type.replace('_', ' ').title()
                self._present_message(f"AI Assistant ({provider_name}) analyzing selected image...")
                
                # Load and analyze the image file
                try:
                    image_data = self._load_image_file(image_file)
                    if image_data:
                        # For file browsing, use a special prompt to avoid confusion
                        response = self._ai_provider.analyze_images("ANALYZE_SINGLE_IMAGE_FILE", image_data, {})
                        self._show_description_dialog(response)
                    else:
                        self._present_message("Could not load the selected image file")
                except Exception as e:
                    logger.error(f"Error analyzing image file: {e}")
                    self._present_message(f"Error analyzing image file: {e}")
            else:
                self._present_message("No image file selected")
            
            return True
            
        except Exception as e:
            logger.error(f"Error in browse image file: {e}")
            self._present_message(f"Error browsing for image file: {e}")
            return False
    
    def _handle_ai_describe_with_data(self, data):
        """Handle AI screen description request with pre-captured data."""
        try:
            logger.info("AI screen description requested with pre-captured data")
            
            if not self._enabled:
                self._present_message("AI Assistant is not enabled")
                return True
                
            # Use AI to describe the current screen
            if not self._ai_provider:
                self._present_message("AI provider not available. Check configuration.")
                return True
                
            provider_name = self._provider_type.replace('_', ' ').title()
            self._present_message(f"AI Assistant ({provider_name}) analyzing screen...")
            
            # Use pre-captured data
            if data:
                try:
                    response = self._ai_provider.describe_screen(
                        data.get('screenshot'), 
                        data.get('accessibility')
                    )
                    self._show_description_dialog(response)
                except Exception as e:
                    logger.error(f"Error getting AI screen description: {e}")
                    self._present_message(f"Error getting AI screen description: {e}")
            else:
                self._present_message("Could not collect screen data for analysis")
                
            return True
        except Exception as e:
            logger.error(f"Error in AI describe handler: {e}")
            return False
    
    def _handle_ai_question_with_data(self, data):
        """Handle AI question request with pre-captured data."""
        try:
            logger.info("AI question requested with pre-captured data")
            
            if not self._enabled:
                self._present_message("AI Assistant is not enabled")
                return True
                
            if not self._ai_provider:
                self._present_message("AI provider not available. Check configuration.")
                return True
            
            # Store the pre-captured data for use by the question dialog
            self._current_screen_data = data
            
            # Show question dialog
            self._show_question_dialog()
            return True
        except Exception as e:
            logger.error(f"Error in AI question handler: {e}")
            return False
    
    def _handle_ai_activate_with_data(self, data):
        """Handle AI action request with pre-captured data."""
        try:
            logger.info("AI action requested with pre-captured data")
            
            if not self._enabled:
                self._present_message("AI Assistant is not enabled")
                return True
                
            if not self._ai_provider:
                self._present_message("AI provider not available. Check configuration.")
                return True
            
            # Store the pre-captured data for use by the action dialog
            self._current_screen_data = data
            
            # Show action dialog
            self._show_action_dialog()
            return True
        except Exception as e:
            logger.error(f"Error in AI action handler: {e}")
            return False
        
    def _handle_ai_activate(self, script=None, inputEvent=None):
        """Handle main AI Assistant activation - now shows action dialog."""
        try:
            logger.info("AI Assistant activation requested")

            if not self._enabled:
                self._present_message("AI Assistant is not enabled")
                return True
                
            if not self._ai_provider:
                self._present_message("AI provider not available. Check configuration.")
                return True
                
            # NEW: Show action request dialog for Phase 5
            self._present_message("AI Assistant capturing screen data for action...")
            self._current_screen_data = self._collect_ai_data()
            
            if not self._current_screen_data:
                self._present_message("Could not collect screen data for action")
                return True
                
            # Show action request dialog
            self._show_action_dialog()
                
            return True
            
        except Exception as e:
            logger.error(f"Error in AI activate handler: {e}")
            return False
            
    def _handle_ai_question(self, script=None, inputEvent=None):
        """Handle AI question request."""
        try:
            logger.info("AI question requested")
            
            if not self._enabled:
                self._present_message("AI Assistant is not enabled")
                return True
                
            if not self._ai_provider:
                self._present_message("AI provider not available. Check configuration.")
                return True
                
            # IMPORTANT: Collect screen data BEFORE opening dialog
            # This captures the actual window the user is asking about
            self._present_message("AI Assistant capturing screen data...")
            self._current_screen_data = self._collect_ai_data()
            
            if not self._current_screen_data:
                self._present_message("Could not collect screen data for analysis")
                return True
                
            # Now show question dialog
            self._show_question_dialog()
                
            return True
            
        except Exception as e:
            logger.error(f"Error in AI question handler: {e}")
            return False
            
    def _handle_ai_describe(self, script=None, inputEvent=None):
        """Handle AI screen description request."""
        try:
            logger.info("AI screen description requested")
            
            if not self._enabled:
                self._present_message("AI Assistant is not enabled")
                return True
                
            # Use AI to describe the current screen
            if not self._ai_provider:
                self._present_message("AI provider not available. Check configuration.")
                return True
                
            provider_name = self._provider_type.replace('_', ' ').title()
            self._present_message(f"AI Assistant ({provider_name}) analyzing screen...")
            
            # Collect data and get AI description
            data = self._collect_ai_data()
            if data:
                try:
                    response = self._ai_provider.describe_screen(
                        data.get('screenshot'), 
                        data.get('accessibility')
                    )
                    self._show_description_dialog(response)
                except Exception as e:
                    logger.error(f"Error getting AI screen description: {e}")
                    self._present_message(f"Error getting AI screen description: {e}")
            else:
                self._present_message("Could not collect screen data for analysis")
                
            return True
            
        except Exception as e:
            logger.error(f"Error in AI describe handler: {e}")
            return False
            
    def _present_message(self, message):
        """Present a message to the user via speech."""
        try:
            if self.app:
                state = self.app.getDynamicApiManager().getAPI('CthulhuState')
                if state and state.activeScript:
                    state.activeScript.presentMessage(message, resetStyles=False)
                else:
                    logger.warning("No active script available for message presentation")
            else:
                logger.warning("No app reference available for message presentation")
        except Exception as e:
            logger.error(f"Error presenting message: {e}")
            
    def _capture_screenshot(self):
        """Capture a screenshot of the current display."""
        try:
            # Get the default display and root window
            display = Gdk.Display.get_default()
            if not display:
                logger.error("No display available for screenshot")
                return None
                
            screen = display.get_default_screen()
            root_window = screen.get_root_window()
            
            # Get screen dimensions
            width = screen.get_width()
            height = screen.get_height()
            
            # Capture the screenshot
            pixbuf = Gdk.pixbuf_get_from_window(root_window, 0, 0, width, height)
            
            if not pixbuf:
                logger.error("Failed to capture screenshot")
                return None
                
            # Convert to base64 for AI transmission
            success, buffer = pixbuf.save_to_bufferv("png", [], [])
            if not success:
                logger.error("Failed to save pixbuf to buffer")
                return None
            image_data = base64.b64encode(buffer).decode('utf-8')
            
            logger.info(f"Screenshot captured: {width}x{height}")
            return {
                'format': 'png',
                'width': width,
                'height': height,
                'data': image_data
            }
            
        except Exception as e:
            logger.error(f"Error capturing screenshot: {e}")
            return None
            
    def _get_accessibility_tree(self):
        """Get accessibility tree information for the current focus."""
        try:
            # Get the current focus object
            focus_obj = cthulhu_state.locusOfFocus
            if not focus_obj:
                logger.warning("No focus object available")
                return None
                
            # Collect accessibility information
            tree_data = {
                'focus': self._serialize_ax_object(focus_obj),
                'context': [],
                'actionable_elements': []  # New: For action system
            }
            
            # Get parent context (up to 3 levels)
            parent = ax_object.AXObject.get_parent(focus_obj)
            level = 0
            while parent and level < 3:
                tree_data['context'].append(self._serialize_ax_object(parent))
                parent = ax_object.AXObject.get_parent(parent)
                level += 1
                
            # Get children of focus (if any) 
            child_count = ax_object.AXObject.get_child_count(focus_obj)
            if child_count > 0:
                children = []
                for i in range(min(child_count, 10)):  # Limit to first 10
                    child = ax_object.AXObject.get_child(focus_obj, i)
                    if child:
                        children.append(self._serialize_ax_object(child))
                if children:
                    tree_data['children'] = children
                
            # NEW: Collect actionable elements for AI actions
            tree_data['actionable_elements'] = self._collect_actionable_elements()
                
            logger.info(f"Accessibility tree collected for {ax_object.AXObject.get_name(focus_obj) or 'unnamed object'}")
            return tree_data
            
        except Exception as e:
            logger.error(f"Error getting accessibility tree: {e}")
            return None
            
    def _serialize_ax_object(self, obj, include_actions=False):
        """Serialize an accessibility object to JSON-compatible format."""
        try:
            if not obj:
                return None
                
            data = {
                'name': ax_object.AXObject.get_name(obj) or '',
                'role': ax_object.AXObject.get_role_name(obj) or '',
                'description': ax_object.AXObject.get_description(obj) or '',
                'text': self._get_object_text(obj),
                'value': self._get_object_value(obj),
                'states': self._get_object_states(obj),
                'attributes': self._get_object_attributes(obj),
                'position': self._get_object_position(obj)
            }
            
            # Include action information for actionable elements
            if include_actions:
                data['actions'] = self._get_object_actions(obj)
                data['is_actionable'] = self._is_actionable_element(obj)
                data['action_coordinates'] = self._get_action_coordinates(obj)
                
            return data
            
        except Exception as e:
            logger.error(f"Error serializing accessibility object: {e}")
            return None
            
    def _get_object_text(self, obj):
        """Get text content from an accessibility object."""
        try:
            # Use script utilities to get displayed text if available
            if cthulhu_state.activeScript and hasattr(cthulhu_state.activeScript, 'utilities'):
                try:
                    text = cthulhu_state.activeScript.utilities.displayedText(obj)
                    if text:
                        return text.strip()
                except:
                    pass
                    
            # Fallback: try direct AT-SPI text interface
            if ax_object.AXObject.supports_text(obj):
                text = AXText.get_all_text(obj)
                if text:
                    return text.strip()
            
            return ""
            
        except Exception as e:
            logger.error(f"Error getting object text: {e}")
            return ""
            
    def _get_object_value(self, obj):
        """Get value from an accessibility object."""
        try:
            if ax_object.AXObject.supports_value(obj):
                value = AXValue.get_current_value(obj)
                return str(value) if value is not None else ""
            return ""
            
        except Exception as e:
            logger.error(f"Error getting object value: {e}")
            return ""
            
    def _get_object_states(self, obj):
        """Get state information from an accessibility object."""
        try:
            states = []
            if AXUtilitiesState.is_focused(obj):
                states.append("focused")
            if AXUtilitiesState.is_selected(obj):
                states.append("selected")
            if AXUtilitiesState.is_expanded(obj):
                states.append("expanded")
            if AXUtilitiesState.is_checked(obj):
                states.append("checked")
            if AXUtilitiesState.is_sensitive(obj):
                states.append("sensitive")
            if AXUtilitiesState.is_showing(obj):
                states.append("showing")
            if AXUtilitiesState.is_visible(obj):
                states.append("visible")
                
            return states
            
        except Exception as e:
            logger.error(f"Error getting object states: {e}")
            return []
            
    def _get_object_attributes(self, obj):
        """Get attributes from an accessibility object."""
        try:
            attrs = {}
            
            # Get object attributes from AT-SPI
            try:
                if hasattr(obj, 'get_attributes'):
                    obj_attrs = obj.get_attributes()
                    if obj_attrs:
                        attrs['object_attributes'] = dict(obj_attrs)
            except:
                pass
                
            return attrs
            
        except Exception as e:
            logger.error(f"Error getting object attributes: {e}")
            return {}
            
    def _get_object_position(self, obj):
        """Get position and size information from an accessibility object."""
        try:
            if ax_object.AXObject.supports_component(obj):
                extents = Atspi.Component.get_extents(obj, Atspi.CoordType.SCREEN)
                return {
                    'x': extents.x,
                    'y': extents.y,
                    'width': extents.width,
                    'height': extents.height
                }
            return None

        except Exception as e:
            logger.error(f"Error getting object position: {e}")
            return None
            
    def _collect_ai_data(self):
        """Collect both screenshot and accessibility data for AI analysis."""
        try:
            logger.info("Collecting AI data (screenshot + accessibility tree)")
            
            # Collect both types of data
            screenshot = self._capture_screenshot()
            accessibility_tree = self._get_accessibility_tree()
            
            data = {
                'timestamp': __import__('time').time(),
                'screenshot': screenshot,
                'accessibility': accessibility_tree
            }
            
            # Add current application context
            if cthulhu_state.activeScript:
                app_name = getattr(cthulhu_state.activeScript, 'name', 'unknown')
                data['application'] = app_name
                
            logger.info("AI data collection completed")
            return data
            
        except Exception as e:
            logger.error(f"Error collecting AI data: {e}")
            return None
            
    def _show_question_dialog(self):
        """Show a dialog for the user to enter their question."""
        try:
            provider_name = self._provider_type.replace('_', ' ').title()
            dialog = Gtk.Dialog(
                title=f"AI Assistant Question ({provider_name})",
                parent=None,
                flags=Gtk.DialogFlags.MODAL,
                buttons=(
                    Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,
                    Gtk.STOCK_OK, Gtk.ResponseType.OK
                )
            )
            
            dialog.set_default_size(500, 200)
            
            # Create the question entry
            content_area = dialog.get_content_area()
            
            label = Gtk.Label(label="Enter your question about the current screen:")
            label.set_halign(Gtk.Align.START)
            content_area.pack_start(label, False, False, 10)
            
            entry = Gtk.Entry()
            entry.set_placeholder_text("What would you like to know?")
            entry.set_activates_default(True)
            content_area.pack_start(entry, False, False, 10)
            
            dialog.set_default_response(Gtk.ResponseType.OK)
            # Show all widgets including buttons
            dialog.show_all()
            
            # Set focus to the entry
            entry.grab_focus()
            
            response = dialog.run()
            
            if response == Gtk.ResponseType.OK:
                question = entry.get_text().strip()
                if question:
                    # Transform dialog to show processing and response
                    self._transform_dialog_for_response(dialog, question)
                else:
                    dialog.destroy()
                    self._present_message("No question entered")
            else:
                dialog.destroy()
                self._present_message("Question cancelled")
                
        except Exception as e:
            logger.error(f"Error showing question dialog: {e}")
            self._present_message(f"Error showing question dialog: {e}")
            
    def _transform_dialog_for_response(self, dialog, question):
        """Transform the question dialog to show AI processing and response."""
        try:
            # Clear existing content
            content_area = dialog.get_content_area()
            for child in content_area.get_children():
                content_area.remove(child)
                
            # Remove existing buttons
            for child in dialog.get_action_area().get_children():
                dialog.get_action_area().remove(child)
                
            # Change title  
            provider_name = self._provider_type.replace('_', ' ').title()
            dialog.set_title(f"AI Assistant Response ({provider_name})")
            
            # Show question and processing message
            question_label = Gtk.Label()
            question_label.set_markup(f"<b>Question:</b> {question}")
            question_label.set_line_wrap(True)
            question_label.set_halign(Gtk.Align.START)
            content_area.pack_start(question_label, False, False, 10)
            
            # Create scrollable text view for response (same as description dialog)
            scrolled_window = Gtk.ScrolledWindow()
            scrolled_window.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
            scrolled_window.set_shadow_type(Gtk.ShadowType.IN)
            
            self._response_text_view = Gtk.TextView()
            self._response_text_view.set_editable(False)  # Read-only
            self._response_text_view.set_cursor_visible(True)  # Allow cursor navigation for screen readers
            self._response_text_view.set_wrap_mode(Gtk.WrapMode.WORD)
            
            # Set initial processing text
            text_buffer = self._response_text_view.get_buffer()
            text_buffer.set_text("Processing your question...")
            
            scrolled_window.add(self._response_text_view)
            content_area.pack_start(scrolled_window, True, True, 10)
            
            # Add close button
            close_button = dialog.add_button(Gtk.STOCK_CLOSE, Gtk.ResponseType.CLOSE)
            dialog.set_default_response(Gtk.ResponseType.CLOSE)
            
            # Resize for response content
            dialog.set_default_size(600, 400)
            dialog.show_all()
            
            # Focus the response text view so screen reader announces it
            self._response_text_view.grab_focus()
            
            # Process question asynchronously
            self._process_user_question_async(dialog, question)
            
        except Exception as e:
            logger.error(f"Error transforming dialog: {e}")
            dialog.destroy()
            self._present_message(f"Error showing response: {e}")
            
    def _process_user_question_async(self, dialog, question):
        """Process the user's question and update dialog with response."""
        try:
            # Use the pre-captured screen data (captured before dialog opened)
            data = self._current_screen_data
            if data:
                try:
                    response = self._ai_provider.answer_question(
                        question, 
                        data.get('screenshot'), 
                        data.get('accessibility')
                    )
                    
                    # Update the response text view
                    text_buffer = self._response_text_view.get_buffer()
                    text_buffer.set_text(f"Response:\n{response}")
                    
                    # Also speak the response
                    self._present_message(response)
                    
                    # Set up dialog close handler
                    def on_response(dialog, response_id):
                        dialog.destroy()
                        
                    dialog.connect("response", on_response)
                    
                except Exception as e:
                    logger.error(f"Error getting AI response: {e}")
                    text_buffer = self._response_text_view.get_buffer()
                    text_buffer.set_text(f"Error: {e}")
                    self._present_message(f"Error getting AI response: {e}")
            else:
                text_buffer = self._response_text_view.get_buffer()
                text_buffer.set_text("Error: No screen data available")
                self._present_message("No screen data available")
                
        except Exception as e:
            logger.error(f"Error processing user question: {e}")
            text_buffer = self._response_text_view.get_buffer()
            text_buffer.set_text(f"Error: {e}")
            self._present_message(f"Error processing question: {e}")
            
    # ============================================================================
    def _show_description_dialog(self, description):
        """Show a read-only dialog with the screen description."""
        try:
            provider_name = self._provider_type.replace('_', ' ').title()
            dialog = Gtk.Dialog(
                title=f"AI Screen Description ({provider_name})",
                parent=None,
                flags=Gtk.DialogFlags.MODAL,
                buttons=(Gtk.STOCK_CLOSE, Gtk.ResponseType.CLOSE)
            )
            
            dialog.set_default_size(600, 400)
            
            content_area = dialog.get_content_area()
            
            # Create scrollable text view
            scrolled_window = Gtk.ScrolledWindow()
            scrolled_window.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
            scrolled_window.set_shadow_type(Gtk.ShadowType.IN)
            
            text_view = Gtk.TextView()
            text_view.set_editable(False)  # Read-only
            text_view.set_cursor_visible(True)  # Allow cursor navigation for screen readers
            text_view.set_wrap_mode(Gtk.WrapMode.WORD)
            
            # Set the description text
            text_buffer = text_view.get_buffer()
            text_buffer.set_text(description)
            
            scrolled_window.add(text_view)
            content_area.pack_start(scrolled_window, True, True, 10)
            
            dialog.set_default_response(Gtk.ResponseType.CLOSE)
            dialog.show_all()
            
            # Focus the text view so screen reader reads the content
            text_view.grab_focus()
            
            # Show dialog and wait for user to close
            response = dialog.run()
            dialog.destroy()
            
        except Exception as e:
            logger.error(f"Error showing description dialog: {e}")
            self._present_message(f"Error showing description: {e}")

    # NEW: Action System Methods for Phase 5
    # ============================================================================
    
    def _collect_actionable_elements(self):
        """Collect all actionable elements in the current window for AI analysis."""
        try:
            logger.info("Collecting actionable elements for action system")
            actionable_elements = []
            
            # Get the current application's window
            if not cthulhu_state.activeScript:
                return actionable_elements
                
            # Start from the application root
            app = cthulhu_state.activeScript.app
            if not app:
                return actionable_elements
                
            # Recursively find actionable elements
            self._find_actionable_elements_recursive(app, actionable_elements, max_depth=5)
            
            logger.info(f"Found {len(actionable_elements)} actionable elements")
            return actionable_elements
            
        except Exception as e:
            logger.error(f"Error collecting actionable elements: {e}")
            return []
            
    def _find_actionable_elements_recursive(self, obj, actionable_elements, current_depth=0, max_depth=5):
        """Recursively find actionable elements in the accessibility tree."""
        try:
            if current_depth >= max_depth:
                return
                
            if not obj:
                return
                
            # Check if this element is actionable
            if self._is_actionable_element(obj):
                element_data = self._serialize_ax_object(obj, include_actions=True)
                if element_data and element_data.get('is_actionable'):
                    actionable_elements.append(element_data)
                    
            # Recurse through children
            try:
                child_count = ax_object.AXObject.get_child_count(obj)
                for i in range(min(child_count, 20)):  # Limit children to prevent overflow
                    child = ax_object.AXObject.get_child(obj, i)
                    if child:
                        self._find_actionable_elements_recursive(
                            child, actionable_elements, current_depth + 1, max_depth
                        )
            except:
                pass
                
        except Exception as e:
            logger.debug(f"Error in recursive element search: {e}")
            
    def _is_actionable_element(self, obj):
        """Determine if an accessibility object is actionable for AI operations."""
        try:
            if not obj:
                return False
                
            # Get role and states
            role = ax_object.AXObject.get_role_name(obj)
            states = self._get_object_states(obj)
            
            # Define actionable roles
            actionable_roles = {
                'push button', 'button', 'toggle button', 'radio button', 'check box',
                'menu item', 'list item', 'tree item', 'tab', 'link', 'entry', 'text',
                'password text', 'combo box', 'spin button', 'slider', 'scroll bar'
            }
            
            # Check if role is actionable
            if role and role.lower() in actionable_roles:
                # Additional checks for enabled/visible states
                if states:
                    state_names = [state.lower() for state in states]
                    # Element must be enabled and visible
                    if 'enabled' in state_names and 'visible' in state_names:
                        return True
                        
            # Check for specific action interfaces
            try:
                actions = self._get_object_actions(obj)
                if actions and len(actions) > 0:
                    return True
            except:
                pass
                
            return False
            
        except Exception as e:
            logger.debug(f"Error checking if element is actionable: {e}")
            return False
            
    def _get_object_actions(self, obj):
        """Get available actions for an accessibility object."""
        try:
            actions = []
            
            # Check for AT-SPI action interface
            if ax_object.AXObject.supports_action(obj):
                try:
                    action_count = Atspi.Action.get_n_actions(obj)
                except Exception:
                    action_count = 0
                for i in range(action_count):
                    try:
                        action_name = Atspi.Action.get_name(obj, i)
                    except Exception:
                        action_name = ""
                    try:
                        action_desc = Atspi.Action.get_description(obj, i)
                    except Exception:
                        action_desc = ""
                    actions.append({
                        'name': action_name or '',
                        'description': action_desc or '',
                        'index': i
                    })
                
            return actions
            
        except Exception as e:
            logger.debug(f"Error getting object actions: {e}")
            return []
            
    def _get_action_coordinates(self, obj):
        """Get coordinates for performing actions on an object."""
        try:
            position = self._get_object_position(obj)
            if position and 'x' in position and 'y' in position:
                # Calculate center point for clicking
                center_x = position['x'] + (position.get('width', 0) // 2)
                center_y = position['y'] + (position.get('height', 0) // 2)
                
                return {
                    'center_x': center_x,
                    'center_y': center_y,
                    'bounds': position
                }
                
            return None
            
        except Exception as e:
            logger.debug(f"Error getting action coordinates: {e}")
            return None
            
    # ============================================================================
    # Action Dialog and Execution Methods
    # ============================================================================
    
    def _show_action_dialog(self):
        """Show dialog for entering action requests or analyzing images."""
        try:
            # Create dialog without parent first
            dialog = Gtk.Dialog(
                title="AI Assistant Actions",
                flags=Gtk.DialogFlags.MODAL | Gtk.DialogFlags.DESTROY_WITH_PARENT,
                buttons=(
                    Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,
                    "Analyze", Gtk.ResponseType.OK
                )
            )
            
            # Make dialog fully accessible
            dialog.set_resizable(True)
            dialog.set_modal(True)
            dialog.set_type_hint(Gdk.WindowTypeHint.DIALOG)
            
            dialog.set_default_size(600, 350)
            
            content_area = dialog.get_content_area()
            
            # Mode selection radio buttons
            mode_label = Gtk.Label()
            mode_label.set_markup("<b>Choose analysis mode:</b>")
            mode_label.set_halign(Gtk.Align.START)
            content_area.pack_start(mode_label, False, False, 10)
            
            # Action mode radio button (default)
            action_radio = Gtk.RadioButton(label="Request an action")
            action_radio.set_active(True)
            content_area.pack_start(action_radio, False, False, 5)
            
            # Image analysis mode radio button
            image_radio = Gtk.RadioButton.new_with_label_from_widget(action_radio, "Analyze image content")
            content_area.pack_start(image_radio, False, False, 5)
            
            # Instruction label
            instruction_label = Gtk.Label()
            instruction_label.set_markup("<b>Tell the AI what you want to do:</b>\n" +
                           "Examples: 'Click the Continue button', 'Enter storm into username field', " +
                           "'Copy this text to clipboard'")
            instruction_label.set_line_wrap(True)
            instruction_label.set_halign(Gtk.Align.START)
            content_area.pack_start(instruction_label, False, False, 10)
            
            # Action entry
            entry = Gtk.Entry()
            entry.set_placeholder_text("What would you like me to do?")
            entry.set_activates_default(True)
            content_area.pack_start(entry, False, False, 10)
            
            # Update UI based on radio button selection
            def on_radio_toggled(radio_button):
                if action_radio.get_active():
                    instruction_label.set_markup("<b>Tell the AI what you want to do:</b>\n" +
                                   "Examples: 'Click the Continue button', 'Enter storm into username field', " +
                                   "'Copy this text to clipboard'")
                    entry.set_placeholder_text("What would you like me to do?")
                    entry.set_sensitive(True)
                else:  # image mode
                    instruction_label.set_markup("<b>Image Analysis Mode:</b>\n" +
                                   "The AI will focus on describing visual content in images on the screen, " +
                                   "ignoring UI elements. Leave text field empty or add specific questions.")
                    entry.set_placeholder_text("Optional: Ask specific questions about images (leave empty for general description)")
                    entry.set_sensitive(True)
            
            action_radio.connect("toggled", on_radio_toggled)
            image_radio.connect("toggled", on_radio_toggled)
            
            dialog.set_default_response(Gtk.ResponseType.OK)
            dialog.show_all()
            
            entry.grab_focus()
            
            response = dialog.run()
            
            if response == Gtk.ResponseType.OK:
                if image_radio.get_active():
                    # Image analysis mode
                    user_question = entry.get_text().strip()
                    dialog.destroy()
                    self._handle_image_analysis(user_question)
                else:
                    # Action request mode
                    action_request = entry.get_text().strip()
                    if action_request:
                        dialog.destroy()
                        self._show_action_confirmation_dialog(action_request)
                    else:
                        dialog.destroy()
                        self._present_message("No action request entered")
            else:
                dialog.destroy()
                self._present_message("Action request cancelled")
                
        except Exception as e:
            logger.error(f"Error showing action dialog: {e}")
            self._present_message(f"Error showing action dialog: {e}")
            
    def _handle_image_analysis(self, user_question=None):
        """Handle image analysis request."""
        try:
            logger.info(f"Image analysis requested with question: '{user_question}'")
            
            self._present_message("AI Assistant analyzing images on screen...")
            
            # Use existing screen data or collect fresh data
            screen_data = self._current_screen_data
            if not screen_data:
                screen_data = self._collect_ai_data()
            
            if not screen_data:
                self._present_message("Could not collect screen data for image analysis")
                return
            
            # Get image analysis from AI provider
            if user_question:
                # User has a specific question about images
                result = self._ai_provider.analyze_images(user_question, 
                                                        screen_data.get("screenshot"), 
                                                        screen_data.get("accessibility"))
            else:
                # General image description
                result = self._ai_provider.analyze_images(None,
                                                        screen_data.get("screenshot"), 
                                                        screen_data.get("accessibility"))
            
            # Present the result
            self._present_message(f"Image analysis complete: {result}")
            
        except Exception as e:
            logger.error(f"Error in image analysis: {e}")
            self._present_message(f"Error analyzing images: {e}")
            
    def _show_action_confirmation_dialog(self, action_request):
        """Show a fresh confirmation dialog for the action."""
        try:
            # Create completely new dialog
            dialog = Gtk.Dialog(
                title="AI Assistant - Confirm Action",
                flags=Gtk.DialogFlags.MODAL | Gtk.DialogFlags.DESTROY_WITH_PARENT
            )
            
            # Add buttons directly during dialog creation
            cancel_button = dialog.add_button("Cancel", Gtk.ResponseType.CANCEL)
            execute_button = dialog.add_button("Execute Action", Gtk.ResponseType.ACCEPT)
            
            # Configure dialog
            dialog.set_default_size(600, 400)
            dialog.set_resizable(True)
            dialog.set_type_hint(Gdk.WindowTypeHint.DIALOG)
            
            # Configure buttons
            execute_button.set_sensitive(False)  # Disabled until analysis complete
            execute_button.set_can_focus(True)
            execute_button.set_can_default(True) 
            cancel_button.set_can_focus(True)
            
            dialog.set_default_response(Gtk.ResponseType.ACCEPT)
            
            content_area = dialog.get_content_area()
            
            # Status label
            status_label = Gtk.Label(label="AI is analyzing your request...")
            status_label.set_halign(Gtk.Align.START)
            content_area.pack_start(status_label, False, False, 10)
            
            # Analysis text view
            scrolled = Gtk.ScrolledWindow()
            scrolled.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
            scrolled.set_size_request(550, 300)
            
            text_view = Gtk.TextView()
            text_view.set_editable(False)
            text_view.set_wrap_mode(Gtk.WrapMode.WORD)
            scrolled.add(text_view)
            content_area.pack_start(scrolled, True, True, 10)
            
            # Store references for updating
            self._action_dialog = dialog
            self._action_status_label = status_label
            self._action_text_view = text_view
            self._action_execute_button = execute_button
            self._current_action_request = action_request
            
            # Show everything
            dialog.show_all()
            
            # Set up response handler
            def on_response(dialog, response_id):
                if response_id == Gtk.ResponseType.ACCEPT:
                    # Execute the confirmed action
                    if hasattr(self, '_parsed_action'):
                        # Close dialog FIRST, then execute action
                        dialog.destroy()
                        # Add small delay to let dialog close
                        from gi.repository import GLib
                        GLib.timeout_add(500, lambda: self._execute_confirmed_action(self._parsed_action))
                else:
                    dialog.destroy()
                
            dialog.connect("response", on_response)
            
            # Start AI analysis in background
            import threading
            analysis_thread = threading.Thread(
                target=self._analyze_action_request, 
                args=(action_request,)
            )
            analysis_thread.daemon = True
            analysis_thread.start()
            
            # Show dialog (non-blocking)
            dialog.present()
            
        except Exception as e:
            logger.error(f"Error showing confirmation dialog: {e}")
            self._present_message(f"Error showing confirmation dialog: {e}")
            
    def _transform_dialog_for_action_analysis(self, dialog, action_request):
        """Transform action dialog to show AI analysis and confirmation."""
        try:
            # Clear existing content
            content_area = dialog.get_content_area()
            for child in content_area.get_children():
                content_area.remove(child)
                
            # Clear existing buttons and add new ones
            action_area = dialog.get_action_area()
            for child in action_area.get_children():
                child.destroy()  # Use destroy instead of remove
                
            # Add new buttons with proper accessibility
            cancel_button = Gtk.Button.new_with_label("Cancel")
            execute_button = Gtk.Button.new_with_label("Execute Action")
            
            # Set up button responses
            cancel_button.connect("clicked", lambda b: dialog.response(Gtk.ResponseType.CANCEL))
            execute_button.connect("clicked", lambda b: dialog.response(Gtk.ResponseType.ACCEPT))
            
            # Add buttons to action area
            action_area.pack_start(cancel_button, False, False, 0)
            action_area.pack_start(execute_button, False, False, 0)
            
            # Make buttons accessible and focusable
            cancel_button.set_can_focus(True)
            execute_button.set_can_focus(True)
            execute_button.set_sensitive(False)  # Disabled until analysis complete
            
            # Set default response for Enter key
            dialog.set_default_response(Gtk.ResponseType.ACCEPT)
            execute_button.set_can_default(True)
            execute_button.grab_default()
            
            # Status label
            status_label = Gtk.Label(label="AI is analyzing your request...")
            status_label.set_halign(Gtk.Align.START)
            content_area.pack_start(status_label, False, False, 10)
            
            # Analysis text view
            scrolled = Gtk.ScrolledWindow()
            scrolled.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
            scrolled.set_size_request(550, 200)
            
            text_view = Gtk.TextView()
            text_view.set_editable(False)
            text_view.set_wrap_mode(Gtk.WrapMode.WORD)
            scrolled.add(text_view)
            content_area.pack_start(scrolled, True, True, 10)
            
            # Store references for updating
            self._action_dialog = dialog
            self._action_status_label = status_label
            self._action_text_view = text_view
            self._action_execute_button = execute_button
            self._current_action_request = action_request
            
            # Show all widgets including new buttons
            cancel_button.show()
            execute_button.show()
            dialog.show_all()
            
            # Start AI analysis in background
            import threading
            analysis_thread = threading.Thread(
                target=self._analyze_action_request, 
                args=(action_request,)
            )
            analysis_thread.daemon = True
            analysis_thread.start()
            
            # Set up response handlers
            def on_response(dialog, response_id):
                if response_id == Gtk.ResponseType.ACCEPT:
                    # Execute the confirmed action
                    if hasattr(self, '_parsed_action'):
                        self._execute_confirmed_action(self._parsed_action)
                dialog.destroy()
                
            dialog.connect("response", on_response)
            
        except Exception as e:
            logger.error(f"Error transforming action dialog: {e}")
            dialog.destroy()
            self._present_message(f"Error in action dialog: {e}")
            
    def _analyze_action_request(self, action_request):
        """Analyze action request using AI (runs in background thread)."""
        try:
            logger.info(f"Analyzing action request: {action_request}")
            
            # Use AI to analyze the request
            analysis = self._ai_provider.suggest_actions(
                action_request,
                self._current_screen_data.get('screenshot'),
                self._current_screen_data.get('accessibility')
            )
            
            # Update UI on main thread
            from gi.repository import GLib
            GLib.idle_add(self._update_action_analysis, analysis)
            
        except Exception as e:
            logger.error(f"Error analyzing action request: {e}")
            from gi.repository import GLib
            GLib.idle_add(self._update_action_analysis, f"Error analyzing request: {e}")
            
    def _update_action_analysis(self, analysis):
        """Update action dialog with AI analysis results."""
        try:
            if not hasattr(self, '_action_dialog'):
                return False
                
            # Update status
            self._action_status_label.set_text("AI Analysis Complete:")
            
            # Update analysis text
            buffer = self._action_text_view.get_buffer()
            buffer.set_text(analysis)
            
            # Enable execute button if analysis looks successful
            if not analysis.startswith("Error"):
                self._action_execute_button.set_sensitive(True)
                self._parsed_action = self._parse_action_response(analysis)
                # Set focus to execute button so user can press Enter
                self._action_execute_button.grab_focus()
                self._action_execute_button.grab_default()
                logger.info("Execute button enabled and focused")
            else:
                logger.error(f"Analysis failed: {analysis}")
            
            self._present_message("Action analysis complete. Review and confirm in dialog. Press Tab to navigate, Enter to execute, Escape to cancel.")
            
        except Exception as e:
            logger.error(f"Error updating action analysis: {e}")
            
        return False  # Don't repeat idle callback
        
    def _parse_action_response(self, analysis):
        """Parse AI action response into executable commands."""
        try:
            # Look for structured ACTION TYPE in AI response
            action_data = {
                'type': 'unknown',
                'target': None,
                'value': None,
                'coordinates': None,
                'description': analysis
            }
            
            analysis_lower = analysis.lower()
            
            # Look for explicit ACTION TYPE markers first
            if '**action type**:' in analysis_lower:
                import re
                action_type_match = re.search(r'\*\*action type\*\*:\s*(\w+)', analysis_lower)
                if action_type_match:
                    action_type = action_type_match.group(1).lower()
                    if action_type in ['click', 'type', 'copy', 'scroll', 'navigate']:
                        action_data['type'] = action_type
                        logger.info(f"Detected action type from AI response: {action_type}")
                        return action_data
            
            # Fallback: Look for action keywords in the original user request
            if hasattr(self, '_current_action_request'):
                request_lower = self._current_action_request.lower()
                logger.info(f"Parsing user request: {self._current_action_request}")
                
                # Check user request directly
                if 'copy' in request_lower and 'clipboard' in request_lower:
                    action_data['type'] = 'copy'
                    logger.info("Detected COPY action from user request")
                elif 'type' in request_lower or 'enter' in request_lower:
                    action_data['type'] = 'type' 
                    logger.info("Detected TYPE action from user request")
                elif 'click' in request_lower:
                    action_data['type'] = 'click'
                    logger.info("Detected CLICK action from user request")
                    
            # Final fallback: analyze AI response content
            if action_data['type'] == 'unknown':
                if 'click' in analysis_lower:
                    action_data['type'] = 'click'
                elif 'type' in analysis_lower or 'typewrite' in analysis_lower:
                    action_data['type'] = 'type'
                elif 'copy' in analysis_lower or 'clipboard' in analysis_lower:
                    action_data['type'] = 'copy'
                    
            logger.info(f"Final parsed action type: {action_data['type']}")
            return action_data
            
        except Exception as e:
            logger.error(f"Error parsing action response: {e}")
            return {'type': 'error', 'description': str(e)}
            
    def _execute_confirmed_action(self, action_data):
        """Execute the user-confirmed action."""
        try:
            logger.info(f"Executing confirmed action: {action_data}")
            
            action_type = action_data.get('type', 'unknown')
            
            if action_type == 'click':
                result = self._perform_click_action(action_data)
            elif action_type == 'type':
                result = self._perform_type_action(action_data)
            elif action_type == 'copy':
                result = self._perform_copy_action(action_data)
            else:
                result = f"Unknown action type: {action_type}"
                
            self._present_message(f"Action result: {result}")
            
        except Exception as e:
            logger.error(f"Error executing action: {e}")
            self._present_message(f"Error executing action: {e}")
            
    def _perform_click_action(self, action_data):
        """Perform a click action on a UI element."""
        try:
            # Try PyAutoGUI for universal clicking
            import pyautogui
            
            # Extract coordinates from action_data or current screen data
            coords = action_data.get('coordinates')
            if not coords and hasattr(self, '_current_screen_data'):
                # Look for actionable elements in the collected data
                actionable_elements = self._current_screen_data.get('accessibility', {}).get('actionable_elements', [])
                # For now, just click center of screen as fallback
                coords = {'center_x': 640, 'center_y': 360}
                
            if coords:
                x, y = coords.get('center_x', 640), coords.get('center_y', 360)
                pyautogui.click(x, y)
                return f"Clicked at coordinates ({x}, {y})"
            else:
                return "Could not determine click coordinates"
                
        except ImportError:
            return "PyAutoGUI not available - install with: pip install pyautogui"
        except Exception as e:
            return f"Click failed: {e}"
        
    def _perform_type_action(self, action_data):
        """Perform a text typing action."""
        try:
            import pyautogui
            
            # Extract text to type from the action description
            text_to_type = self._extract_text_from_action(action_data)
            logger.info(f"Attempting to type: '{text_to_type}'")
            
            if text_to_type:
                import time
                
                # Simple approach: Just wait a moment for dialogs to settle, then type
                # Since PyAutoGUI works fine when terminal is focused, let's not overthink it
                logger.info("Waiting briefly for focus to settle before typing")
                time.sleep(1.0)  # Give time for any dialogs to close and focus to return
                
                # Disable PyAutoGUI failsafe for this operation
                pyautogui.FAILSAFE = False
                
                logger.info(f"Starting to type '{text_to_type}' to focused application")
                
                # Type the text with more reasonable timing
                pyautogui.typewrite(text_to_type, interval=0.05)
                    
                # Check if we should press Enter
                request_lower = getattr(self, '_current_action_request', '').lower()
                description_lower = action_data.get('description', '').lower()
                
                if ('press enter' in request_lower or 'hit enter' in request_lower or 
                    'and enter' in request_lower or 'press enter' in description_lower):
                    time.sleep(0.1)
                    pyautogui.press('return')
                    logger.info("Pressed Enter after typing")
                    return f"Typed '{text_to_type}' and pressed Enter"
                else:
                    return f"Typed '{text_to_type}'"
            else:
                return "Could not determine text to type"
                
        except ImportError:
            return "PyAutoGUI not available - install with: pip install pyautogui"
        except Exception as e:
            logger.error(f"Type action failed: {e}")
            return f"Type action failed: {e}"
        
    def _perform_copy_action(self, action_data):
        """Perform a copy to clipboard action."""
        try:
            # Extract the specific text to copy from the user's request
            text_to_copy = self._extract_text_to_copy(action_data)
            
            if text_to_copy:
                # Use direct clipboard manipulation instead of Ctrl+C
                import subprocess
                
                # Use xclip on Linux (works on both X11 and Wayland via XWayland)
                try:
                    process = subprocess.Popen(['xclip', '-selection', 'clipboard'], 
                                             stdin=subprocess.PIPE, 
                                             text=True)
                    process.communicate(input=text_to_copy)
                    return f"Copied '{text_to_copy}' to clipboard"
                except FileNotFoundError:
                    # Fallback to wl-copy for pure Wayland
                    try:
                        process = subprocess.Popen(['wl-copy'], 
                                                 stdin=subprocess.PIPE, 
                                                 text=True)
                        process.communicate(input=text_to_copy)
                        return f"Copied '{text_to_copy}' to clipboard"
                    except FileNotFoundError:
                        return "Neither xclip nor wl-copy available for clipboard operations"
            else:
                # Fallback: try to copy whatever is currently selected
                import pyautogui
                pyautogui.hotkey('ctrl', 'c')
                return "Copied current selection to clipboard"
            
        except ImportError:
            return "Required clipboard tools not available"
        except Exception as e:
            return f"Copy action failed: {e}"
            
    def _extract_text_to_copy(self, action_data):
        """Extract the specific text to copy from the user request."""
        try:
            if hasattr(self, '_current_action_request'):
                request = self._current_action_request
                request_lower = request.lower()
                
                # Special case: summarize and copy requests
                if 'summar' in request_lower and ('clipboard' in request_lower or 'copy' in request_lower):
                    summary = self._generate_screen_summary()
                    if summary:
                        logger.info("Generated screen summary for clipboard")
                        return summary
                
                # Look for quoted text
                import re
                quoted_matches = re.findall(r'["\']([^"\']+)["\']', request)
                if quoted_matches:
                    return quoted_matches[0]
                    
                # Look for text after "copy"
                copy_matches = re.findall(r'copy\s+(.+?)\s+to\s+clipboard', request, re.IGNORECASE)
                if copy_matches:
                    return copy_matches[0].strip('"\'')
                    
            return None
            
        except Exception as e:
            logger.error(f"Error extracting text to copy: {e}")
            return None
            
    def _generate_screen_summary(self):
        """Generate a summary of the current screen for clipboard operations."""
        try:
            if hasattr(self, '_current_screen_data') and self._current_screen_data:
                accessibility_data = self._current_screen_data.get('accessibility', {})
                
                # Build a simple summary
                summary_parts = []
                
                # Application info
                app_name = self._current_screen_data.get('application', 'Unknown Application')
                summary_parts.append(f"Application: {app_name}")
                
                # Focus info
                focus_info = accessibility_data.get('focus', {})
                if focus_info:
                    focus_name = focus_info.get('name', '')
                    focus_role = focus_info.get('role', '')
                    if focus_name or focus_role:
                        summary_parts.append(f"Current focus: {focus_name} ({focus_role})")
                
                # Context info
                context = accessibility_data.get('context', [])
                if context and len(context) > 0:
                    parent_info = context[0]
                    parent_name = parent_info.get('name', '')
                    parent_role = parent_info.get('role', '')
                    if parent_name or parent_role:
                        summary_parts.append(f"In: {parent_name} ({parent_role})")
                
                # Actionable elements count
                actionable_elements = accessibility_data.get('actionable_elements', [])
                if actionable_elements:
                    summary_parts.append(f"Available actions: {len(actionable_elements)} interactive elements")
                
                if summary_parts:
                    return '\n'.join(summary_parts)
                else:
                    return f"Screen summary for {app_name} - focused on accessible content"
                    
            return "Unable to generate screen summary - no data available"
            
        except Exception as e:
            logger.error(f"Error generating screen summary: {e}")
            return f"Screen summary generation failed: {e}"
            
    def _extract_text_from_action(self, action_data):
        """Extract text to type from action description."""
        try:
            # First try the original user request (most reliable)
            if hasattr(self, '_current_action_request'):
                request = self._current_action_request
                logger.info(f"Extracting text from user request: {request}")
                
                import re
                
                # Enhanced extraction for commands like "type echo 'hello world'"
                if request.lower().startswith('type '):
                    # Remove "type " from the beginning
                    text = request[5:].strip()
                    
                    # Remove trailing conditions like "and press enter", "in the terminal"
                    text = re.sub(r'\s+and\s+(press\s+)?enter.*$', '', text, flags=re.IGNORECASE)
                    text = re.sub(r'\s+in\s+.+$', '', text, flags=re.IGNORECASE)
                    
                    logger.info(f"Extracted command after 'type ': '{text}'")
                    return text
                
                # Look for quoted text in various formats
                quoted_patterns = [
                    r'"([^"]*)"',        # Double quotes
                    r"'([^']*)'",        # Single quotes
                    r'`([^`]*)`'         # Backticks
                ]
                
                for pattern in quoted_patterns:
                    matches = re.findall(pattern, request)
                    if matches:
                        text = matches[0]
                        logger.info(f"Found quoted text: '{text}'")
                        return text
                    
                # Look for text after "type" with flexible matching
                type_patterns = [
                    r'type\s+(.+?)(?:\s+and\s+|\s+in\s+|\s*$)',  # type X and... or type X in...
                    r'enter\s+(.+?)(?:\s+into\s+|\s*$)',        # enter X into...
                ]
                
                for pattern in type_patterns:
                    matches = re.findall(pattern, request, re.IGNORECASE)
                    if matches:
                        text = matches[0].strip()
                        logger.info(f"Found text with pattern '{pattern}': '{text}'")
                        return text
                        
            # Fallback: try the AI description for quoted text
            description = action_data.get('description', '')
            if description:
                import re
                quoted_matches = re.findall(r'"([^"]*)"', description)
                if quoted_matches:
                    logger.info(f"Found text in AI description: '{quoted_matches[0]}'")
                    return quoted_matches[0]
                    
            logger.warning("Could not extract text to type")
            return None
            
        except Exception as e:
            logger.error(f"Error extracting text from action: {e}")
            return None
    
    def _show_image_file_chooser(self):
        """Show a file chooser dialog for selecting an image file."""
        try:
            dialog = Gtk.FileChooserDialog(
                title="Select Image File",
                parent=None,
                action=Gtk.FileChooserAction.OPEN
            )
            
            # Add buttons
            dialog.add_button("Cancel", Gtk.ResponseType.CANCEL)
            dialog.add_button("Open", Gtk.ResponseType.OK)
            
            # Set up image file filters
            filter_images = Gtk.FileFilter()
            filter_images.set_name("Image files")
            filter_images.add_mime_type("image/png")
            filter_images.add_mime_type("image/jpeg")
            filter_images.add_mime_type("image/jpg")
            filter_images.add_mime_type("image/gif")
            filter_images.add_mime_type("image/bmp")
            filter_images.add_mime_type("image/webp")
            filter_images.add_pattern("*.png")
            filter_images.add_pattern("*.jpg")
            filter_images.add_pattern("*.jpeg")
            filter_images.add_pattern("*.gif")
            filter_images.add_pattern("*.bmp")
            filter_images.add_pattern("*.webp")
            dialog.add_filter(filter_images)
            
            # Add "All files" filter as backup
            filter_all = Gtk.FileFilter()
            filter_all.set_name("All files")
            filter_all.add_pattern("*")
            dialog.add_filter(filter_all)
            
            # Run the dialog
            response = dialog.run()
            
            filename = None
            if response == Gtk.ResponseType.OK:
                filename = dialog.get_filename()
                logger.info(f"Selected image file: {filename}")
            
            dialog.destroy()
            return filename
            
        except Exception as e:
            logger.error(f"Error showing image file chooser: {e}")
            return None
    
    def _load_image_file(self, image_path):
        """Load an image file and convert it to base64 for AI analysis."""
        try:
            import base64
            import os
            
            # Read the image file
            with open(image_path, 'rb') as image_file:
                image_data = image_file.read()
            
            # Convert to base64
            base64_data = base64.b64encode(image_data).decode('utf-8')
            
            # Determine format from file extension
            _, ext = os.path.splitext(image_path.lower())
            format_map = {
                '.png': 'png',
                '.jpg': 'jpeg', 
                '.jpeg': 'jpeg',
                '.gif': 'gif',
                '.bmp': 'bmp',
                '.webp': 'webp'
            }
            image_format = format_map.get(ext, 'png')  # Default to png
            
            logger.info(f"Loaded image file: {image_path} ({len(image_data)} bytes, format: {image_format})")
            
            # Return same structure as _capture_screenshot
            return {
                'format': image_format,
                'width': 0,  # We don't know dimensions, but AI doesn't need them
                'height': 0,
                'data': base64_data
            }
            
        except Exception as e:
            logger.error(f"Error loading image file {image_path}: {e}")
            return None


class AIAssistantMenu(Gtk.Dialog):
    """A dialog containing AI Assistant options."""
    
    def __init__(self, handler):
        super().__init__(title="AI Assistant", transient_for=None, flags=0)
        self.on_option_selected = handler
        
        # Set dialog properties for better screen reader accessibility
        self.set_modal(True)
        self.set_position(Gtk.WindowPosition.CENTER)
        self.set_default_size(350, 250)
        
        # Add OK and Cancel buttons
        self.add_button("OK", Gtk.ResponseType.OK)
        self.add_button("Cancel", Gtk.ResponseType.CANCEL)
        
        # Connect response signal
        self.connect("response", self._on_response)
        
        # Add content to dialog
        content_area = self.get_content_area()
        
        # Add label
        label = Gtk.Label(label="Choose an AI Assistant action:")
        content_area.pack_start(label, False, False, 10)
        
        # Create radio button group for options
        self.radio_ask = Gtk.RadioButton.new_with_label(None, "Ask Question")
        self.radio_describe = Gtk.RadioButton.new_with_label_from_widget(self.radio_ask, "Describe Screen")  
        self.radio_action = Gtk.RadioButton.new_with_label_from_widget(self.radio_ask, "Request Action")
        self.radio_image = Gtk.RadioButton.new_with_label_from_widget(self.radio_ask, "Analyze Images")
        self.radio_browse = Gtk.RadioButton.new_with_label_from_widget(self.radio_ask, "Browse for Image File")
        
        # Pack radio buttons
        content_area.pack_start(self.radio_ask, False, False, 5)
        content_area.pack_start(self.radio_describe, False, False, 5)
        content_area.pack_start(self.radio_action, False, False, 5)
        content_area.pack_start(self.radio_image, False, False, 5)
        content_area.pack_start(self.radio_browse, False, False, 5)
        
        # Set first option as selected by default
        self.radio_ask.set_active(True)
        
        # Connect keyboard events for Enter key handling
        self.connect("key-press-event", self._on_key_press)
    
    def _on_response(self, dialog, response_id):
        """Handler for dialog response."""
        if response_id == Gtk.ResponseType.OK:
            # Determine which radio button is selected
            if self.radio_ask.get_active():
                action_id = "ask_question"
            elif self.radio_describe.get_active():
                action_id = "describe_screen" 
            elif self.radio_action.get_active():
                action_id = "request_action"
            elif self.radio_image.get_active():
                action_id = "analyze_images"
            elif self.radio_browse.get_active():
                action_id = "browse_image_file"
            else:
                action_id = None

            if action_id:
                self.on_option_selected(action_id)
                
        self.destroy()
    
    def _on_key_press(self, widget, event):
        """Handle key press events."""
        # Allow Enter key to activate OK button
        if event.keyval == 65293:  # Enter key
            self.response(Gtk.ResponseType.OK)
            return True
        return False
    
    def show_gui(self):
        """Shows the AI Assistant dialog."""
        try:
            self.show_all()

            # Present the dialog to ensure it gets focus
            self.present()

        except Exception as e:
            logger.error(f"Error in show_gui: {e}")
            import traceback
            traceback.print_exc()
