"""
Main application class for the hosts TUI application.

This module contains the main application class that orchestrates
all the handlers and provides the primary user interface.
"""

from textual.app import App, ComposeResult
from textual.containers import Horizontal, Vertical
from textual.widgets import Header, Static, DataTable, Input, Checkbox, RadioSet, RadioButton
from textual.reactive import reactive

from ..core.parser import HostsParser
from ..core.models import HostsFile
from ..core.config import Config
from ..core.manager import HostsManager
from ..core.dns import DNSService
from ..core.filters import EntryFilter, FilterOptions
from .config_modal import ConfigModal
from .password_modal import PasswordModal
from .add_entry_modal import AddEntryModal
from .delete_confirmation_modal import DeleteConfirmationModal
from .filter_modal import FilterModal
from .custom_footer import CustomFooter
from .styles import HOSTS_MANAGER_CSS
from .keybindings import HOSTS_MANAGER_BINDINGS
from .table_handler import TableHandler
from .details_handler import DetailsHandler
from .edit_handler import EditHandler
from .navigation_handler import NavigationHandler


class HostsManagerApp(App):
    """
    Main application class for the hosts TUI manager.

    Provides a two-pane interface for managing hosts file entries
    with read-only mode by default and explicit edit mode.
    """

    ENABLE_COMMAND_PALETTE = False
    CSS = HOSTS_MANAGER_CSS
    BINDINGS = HOSTS_MANAGER_BINDINGS

    help_visible = False

    # Reactive attributes
    hosts_file: reactive[HostsFile] = reactive(HostsFile())
    selected_entry_index: reactive[int] = reactive(0)
    edit_mode: reactive[bool] = reactive(False)
    entry_edit_mode: reactive[bool] = reactive(False)
    sort_column: reactive[str] = reactive("")  # "ip" or "hostname"
    sort_ascending: reactive[bool] = reactive(True)
    search_term: reactive[str] = reactive("")

    def __init__(self):
        super().__init__()
        self.title = "/etc/hosts Manager"

        # Initialize core components
        self.parser = HostsParser()
        self.config = Config()
        self.manager = HostsManager()

        # Initialize DNS service
        dns_config = self.config.get("dns_resolution", {})
        self.dns_service = DNSService(
            enabled=dns_config.get("enabled", True),
            timeout=dns_config.get("timeout", 5.0)
        )

        # Initialize filtering system
        self.entry_filter = EntryFilter()
        self.current_filter_options = FilterOptions()

        # Initialize handlers
        self.table_handler = TableHandler(self)
        self.details_handler = DetailsHandler(self)
        self.edit_handler = EditHandler(self)
        self.navigation_handler = NavigationHandler(self)

        # State for edit mode
        self.original_entry_values = None

    def compose(self) -> ComposeResult:
        """Create child widgets for the app."""
        yield Header()
        yield CustomFooter(id="custom-footer")

        # Search bar above the panes
        with Horizontal(classes="search-container") as search_container:
            search_container.border_title = "Search"
            yield Input(
                placeholder="Filter by hostname, IP address, or comment...",
                id="search-input",
                classes="search-input",
            )

        with Horizontal(classes="hosts-container"):
            # Left pane - entries table
            with Vertical(classes="common-pane left-pane") as left_pane:
                left_pane.border_title = "Host Entries"
                yield DataTable(id="entries-table")

            # Right pane - entry details or edit form
            with Vertical(classes="common-pane right-pane") as right_pane:
                right_pane.border_title = "Entry Details"

                # Details display form (disabled inputs)
                with Vertical(id="entry-details-display", classes="entry-form"):
                    with Vertical(
                        classes="default-section section-no-top-margin"
                    ) as ip_address:
                        ip_address.border_title = "IP Address"
                        yield Input(
                            placeholder="No entry selected",
                            id="details-ip-input",
                            disabled=True,
                            classes="default-input",
                        )

                    with Vertical(classes="default-section") as hostnames:
                        hostnames.border_title = "Hostnames (comma-separated)"
                        yield Input(
                            placeholder="No entry selected",
                            id="details-hostname-input",
                            disabled=True,
                            classes="default-input",
                        )

                    with Vertical(classes="default-section") as dns_name:
                        dns_name.border_title = "DNS Name"
                        yield Input(
                            placeholder="No DNS name",
                            id="details-dns-name-input",
                            disabled=True,
                            classes="default-input",
                        )

                    with Vertical(classes="default-section") as dns_status:
                        dns_status.border_title = "DNS Status"
                        yield Input(
                            placeholder="No DNS status",
                            id="details-dns-status-input",
                            disabled=True,
                            classes="default-input",
                        )

                    with Vertical(classes="default-section") as dns_resolved:
                        dns_resolved.border_title = "Last Resolved"
                        yield Input(
                            placeholder="Not resolved yet",
                            id="details-dns-resolved-input",
                            disabled=True,
                            classes="default-input",
                        )

                    with Vertical(classes="default-section") as comment:
                        comment.border_title = "Comment:"
                        yield Input(
                            placeholder="No entry selected",
                            id="details-comment-input",
                            disabled=True,
                            classes="default-input",
                        )

                    with Vertical(classes="default-section") as active:
                        active.border_title = "Active"
                        yield Checkbox(
                            "Active",
                            id="details-active-checkbox",
                            disabled=True,
                            classes="default-checkbox",
                        )

                # Edit form (initially hidden)
                with Vertical(id="entry-edit-form", classes="entry-form hidden"):
                    # Entry Type Selection
                    with Vertical(classes="default-flex-section section-no-top-margin") as entry_type:
                        entry_type.border_title = "Entry Type"
                        with RadioSet(id="edit-entry-type-radio", classes="default-radio-set"):
                            yield RadioButton("IP Address Entry", value=True, id="edit-ip-entry-radio")
                            yield RadioButton("DNS Name Entry", id="edit-dns-entry-radio")

                    # IP Address Section
                    with Vertical(classes="default-section", id="edit-ip-section") as ip_address:
                        ip_address.border_title = "IP Address"
                        yield Input(
                            placeholder="Enter IP address",
                            id="ip-input",
                            classes="default-input",
                        )

                    # DNS Name Section (initially hidden)
                    with Vertical(classes="default-section hidden", id="edit-dns-section") as dns_name:
                        dns_name.border_title = "DNS Name (to resolve)"
                        yield Input(
                            placeholder="e.g., example.com",
                            id="dns-name-input",
                            classes="default-input",
                        )

                    with Vertical(classes="default-section") as hostnames:
                        hostnames.border_title = "Hostnames (comma-separated)"
                        yield Input(
                            placeholder="Enter hostnames",
                            id="hostname-input",
                            classes="default-input",
                        )

                    with Vertical(classes="default-section") as comment:
                        comment.border_title = "Comment:"
                        yield Input(
                            placeholder="Enter comment (optional)",
                            id="comment-input",
                            classes="default-input",
                        )

                    with Vertical(classes="default-section") as active:
                        active.border_title = "Active"
                        yield Checkbox(
                            "Active", id="active-checkbox", classes="default-checkbox"
                        )

        # Status bar for error/temporary messages (overlay, doesn't affect layout)
        yield Static("", id="status-bar", classes="status-bar hidden")

    def on_ready(self) -> None:
        """Called when the app is ready."""
        self.load_hosts_file()
        self._setup_footer()

    def load_hosts_file(self) -> None:
        """Load the hosts file and populate the table."""
        try:
            # Remember the currently selected entry before reload
            previous_entry = None
            if self.hosts_file.entries and self.selected_entry_index < len(
                self.hosts_file.entries
            ):
                previous_entry = self.hosts_file.entries[self.selected_entry_index]

            # Load the hosts file
            self.hosts_file = self.parser.parse()
            self.table_handler.populate_entries_table()
            self.table_handler.restore_cursor_position(previous_entry)
            self.update_status()
        except Exception as e:
            self.update_status(f"❌ Error loading hosts file: {e}")

    def _setup_footer(self) -> None:
        """Setup the footer with initial content based on keybindings."""
        try:
            footer = self.query_one("#custom-footer", CustomFooter)

            # Clear existing items
            footer.clear_left_items()
            footer.clear_right_items()

            # Process keybindings and add to appropriate sections
            for binding in self.BINDINGS:
                # Skip tuple-style bindings and only process Binding objects
                if not hasattr(binding, "show"):
                    continue

                # Only show bindings marked with show=True
                if binding.show:
                    # Get the display key
                    key_display = getattr(binding, "key_display", None) or binding.key

                    # Get the description
                    description = binding.description or binding.action

                    # Determine positioning from id attribute
                    binding_id = getattr(binding, "id", None)
                    if binding_id and binding_id.startswith("left:"):
                        footer.add_left_item(key_display, description)
                    elif binding_id and binding_id.startswith("right:"):
                        footer.add_right_item(key_display, description)
                    else:
                        # Default to right if no specific positioning
                        footer.add_right_item(key_display, description)

            # Status section will be updated by update_status
            self._update_footer_status()
        except Exception:
            pass  # Footer not ready yet

    def _update_footer_status(self) -> None:
        """Update the footer status section."""
        try:
            footer = self.query_one("#custom-footer", CustomFooter)
            mode = "Edit" if self.edit_mode else "Read-only"
            entry_count = len(self.hosts_file.entries)
            active_count = len(self.hosts_file.get_active_entries())

            status = f"{entry_count} entries ({active_count} active) | {mode}"
            footer.set_status(status)
        except Exception:
            pass  # Footer not ready yet

    def update_status(self, message: str = "") -> None:
        """Update the header subtitle and status bar with status information."""
        if message:
            # Show temporary message in the status bar
            try:
                status_bar = self.query_one("#status-bar", Static)
                status_bar.update(message)
                status_bar.remove_class("hidden")

                if message.startswith("❌"):
                    # Auto-clear error message after 5 seconds
                    self.set_timer(5.0, lambda: self._clear_status_message())
                else:
                    # Auto-clear regular message after 3 seconds
                    self.set_timer(3.0, lambda: self._clear_status_message())
            except Exception:
                # Fallback if status bar not found (during initialization)
                pass

        # Always update the header subtitle with current status
        # Update the footer status
        self._update_footer_status()

    def _clear_status_message(self) -> None:
        """Clear the temporary status message."""
        try:
            status_bar = self.query_one("#status-bar", Static)
            status_bar.update("")
            status_bar.add_class("hidden")
        except Exception:
            pass

    # Event handlers
    def on_data_table_row_highlighted(self, event: DataTable.RowHighlighted) -> None:
        """Handle row highlighting (cursor movement) in the DataTable."""
        if event.data_table.id == "entries-table":
            # Convert display index to actual index
            self.selected_entry_index = (
                self.table_handler.display_index_to_actual_index(event.cursor_row)
            )
            self.details_handler.update_entry_details()

    def on_data_table_row_selected(self, event: DataTable.RowSelected) -> None:
        """Handle row selection in the DataTable."""
        if event.data_table.id == "entries-table":
            # Convert display index to actual index
            self.selected_entry_index = (
                self.table_handler.display_index_to_actual_index(event.cursor_row)
            )
            self.details_handler.update_entry_details()

    def on_data_table_header_selected(self, event: DataTable.HeaderSelected) -> None:
        """Handle column header clicks for sorting."""
        if event.data_table.id == "entries-table":
            # Check if the column key contains "IP Address" (handles sort indicators)
            if "IP Address" in str(event.column_key):
                self.action_sort_by_ip()
            elif "Canonical Hostname" in str(event.column_key):
                self.action_sort_by_hostname()

    def on_key(self, event) -> None:
        """Handle key events to override default tab behavior in edit mode."""
        # Handle tab navigation for search bar and data table
        if event.key == "tab" and not self.entry_edit_mode:
            search_input = self.query_one("#search-input", Input)
            entries_table = self.query_one("#entries-table", DataTable)

            # Check which widget currently has focus
            if self.focused == search_input:
                # Focus on entries table
                entries_table.focus()
                event.prevent_default()
                return
            elif self.focused == entries_table:
                # Focus on search input
                search_input.focus()
                event.prevent_default()
                return

        # Delegate to edit handler for edit mode navigation
        if self.edit_handler.handle_entry_edit_key_event(event):
            return  # Event was handled by edit handler

    def on_input_changed(self, event: Input.Changed) -> None:
        """Handle input field changes (no auto-save - changes saved on exit)."""
        if event.input.id == "search-input":
            # Update search term and filter entries
            self.search_term = event.value.strip()
            # Also update the current filter options to keep them synchronized
            self.current_filter_options.search_term = self.search_term if self.search_term else None
            self.table_handler.populate_entries_table()
            self.details_handler.update_entry_details()
        else:
            # Edit form input changes are tracked but not automatically saved
            # Changes will be validated and saved when exiting edit mode
            pass

    def on_checkbox_changed(self, event: Checkbox.Changed) -> None:
        """Handle checkbox changes (no auto-save - changes saved on exit)."""
        # Checkbox changes are tracked but not automatically saved
        # Changes will be validated and saved when exiting edit mode
        pass

    def on_radio_set_changed(self, event) -> None:
        """Handle entry type radio button changes in edit mode."""
        if hasattr(event, 'radio_set') and event.radio_set.id == "edit-entry-type-radio":
            pressed_radio = event.pressed
            if pressed_radio and pressed_radio.id == "edit-ip-entry-radio":
                # Handle switch to IP entry type
                self.edit_handler.handle_entry_type_change("ip")
            elif pressed_radio and pressed_radio.id == "edit-dns-entry-radio":
                # Handle switch to DNS entry type
                self.edit_handler.handle_entry_type_change("dns")

    # Action handlers
    def action_reload(self) -> None:
        """Reload the hosts file."""
        # Reset sort state on reload
        self.sort_column = ""
        self.sort_ascending = True
        self.load_hosts_file()
        self.update_status("Hosts file reloaded")

    def action_help(self) -> None:
        """Show help panel."""
        if self.help_visible:
            self.action_hide_help_panel()
            self.help_visible = False
        else:
            self.action_show_help_panel()
            self.help_visible = True

    def action_config(self) -> None:
        """Show configuration modal."""

        def handle_config_result(config_changed: bool) -> None:
            if config_changed:
                # Reload the table to apply new filtering
                self.table_handler.populate_entries_table()
                self.update_status("Configuration saved")

        self.push_screen(ConfigModal(self.config), handle_config_result)

    def action_sort_by_ip(self) -> None:
        """Sort entries by IP address, toggle ascending/descending."""
        self.table_handler.sort_entries_by_ip()
        direction = "ascending" if self.sort_ascending else "descending"
        self.update_status(f"Sorted by IP address ({direction})")

    def action_sort_by_hostname(self) -> None:
        """Sort entries by canonical hostname, toggle ascending/descending."""
        self.table_handler.sort_entries_by_hostname()
        direction = "ascending" if self.sort_ascending else "descending"
        self.update_status(f"Sorted by hostname ({direction})")

    def action_toggle_edit_mode(self) -> None:
        """Toggle between read-only and edit mode."""
        if self.edit_mode:
            # Exit edit mode
            success, message = self.manager.exit_edit_mode()
            if success:
                self.edit_mode = False
                self.update_status(message)
            else:
                self.update_status(f"Error exiting edit mode: {message}")
        else:
            # Enter edit mode - first try without password
            success, message = self.manager.enter_edit_mode()
            if success:
                self.edit_mode = True
                self.update_status(message)
            elif "Password required" in message:
                # Show password modal
                self._request_sudo_password()
            else:
                self.update_status(f"Error entering edit mode: {message}")

    def _request_sudo_password(self) -> None:
        """Show password modal and attempt sudo authentication."""

        def handle_password(password: str) -> None:
            if password is None:
                # User cancelled
                self.update_status("Edit mode cancelled")
                return

            # Try to enter edit mode with password
            success, message = self.manager.enter_edit_mode(password)
            if success:
                self.edit_mode = True
                self.update_status(message)
            elif "Incorrect password" in message:
                # Show error and try again
                self.update_status("❌ Incorrect password. Please try again.")
                self.set_timer(2.0, lambda: self._request_sudo_password())
            else:
                self.update_status(f"❌ Error entering edit mode: {message}")

        self.push_screen(PasswordModal(), handle_password)

    def action_edit_entry(self) -> None:
        """Enter edit mode for the selected entry."""
        if not self.edit_mode:
            self.update_status(
                "❌ Cannot edit entry: Application is in read-only mode. Press 'Ctrl+E' to enable edit mode."
            )
            return

        if not self.hosts_file.entries:
            self.update_status("No entries to edit")
            return

        if self.selected_entry_index >= len(self.hosts_file.entries):
            self.update_status("Invalid entry selected")
            return

        entry = self.hosts_file.entries[self.selected_entry_index]
        if entry.is_default_entry():
            self.update_status("❌ Cannot edit system default entry")
            return

        # Store original values for change detection
        self.original_entry_values = {
            "ip_address": entry.ip_address,
            "hostnames": entry.hostnames.copy(),
            "comment": entry.comment,
            "is_active": entry.is_active,
            "dns_name": getattr(entry, 'dns_name', None),
        }

        self.entry_edit_mode = True
        self.details_handler.update_entry_details()

        # Focus on the IP address input field
        ip_input = self.query_one("#edit-entry-type-radio", RadioSet)
        ip_input.focus()

        self.update_status("Editing entry - Use Tab/Shift+Tab to navigate, ESC to exit")

    def action_exit_edit_entry(self) -> None:
        """Exit entry edit mode and return focus to the entries table."""
        self.edit_handler.exit_edit_entry_with_confirmation()

    def action_next_field(self) -> None:
        """Move to the next field in edit mode."""
        self.edit_handler.navigate_to_next_field()

    def action_prev_field(self) -> None:
        """Move to the previous field in edit mode."""
        self.edit_handler.navigate_to_prev_field()

    def action_toggle_entry(self) -> None:
        """Toggle the active state of the selected entry."""
        self.navigation_handler.toggle_entry()

    def action_move_entry_up(self) -> None:
        """Move the selected entry up in the list."""
        self.navigation_handler.move_entry_up()

    def action_move_entry_down(self) -> None:
        """Move the selected entry down in the list."""
        self.navigation_handler.move_entry_down()

    def action_save_file(self) -> None:
        """Save the hosts file to disk."""
        self.navigation_handler.save_hosts_file()

    def action_add_entry(self) -> None:
        """Show the add entry modal."""
        if not self.edit_mode:
            self.update_status(
                "❌ Cannot add entry: Application is in read-only mode. Press 'Ctrl+E' to enable edit mode."
            )
            return

        def handle_add_entry_result(new_entry) -> None:
            if new_entry is None:
                # User cancelled
                self.update_status("Entry creation cancelled")
                return

            # Add the entry using the command-based manager method
            result = self.manager.execute_add_command(self.hosts_file, new_entry)
            if result.success:
                # Save the changes
                save_success, save_message = self.manager.save_hosts_file(self.hosts_file)
                if save_success:
                    # Refresh the table
                    self.table_handler.populate_entries_table()
                    # Move cursor to the newly added entry (last entry)
                    self.selected_entry_index = len(self.hosts_file.entries) - 1
                    self.table_handler.restore_cursor_position(new_entry)
                    
                    # For DNS entries, trigger resolution and provide feedback
                    if hasattr(new_entry, 'dns_name') and new_entry.dns_name:
                        self.update_status(f"✅ {result.message} - Starting DNS resolution for {new_entry.dns_name}")
                        # Trigger DNS resolution in background
                        self._resolve_new_dns_entry(new_entry)
                    else:
                        self.update_status(f"✅ {result.message} - Changes saved automatically")
                else:
                    self.update_status(f"Entry added but save failed: {save_message}")
            else:
                self.update_status(f"❌ {result.message}")

        self.push_screen(AddEntryModal(), handle_add_entry_result)

    def action_delete_entry(self) -> None:
        """Show the delete confirmation modal for the selected entry."""
        if not self.edit_mode:
            self.update_status(
                "❌ Cannot delete entry: Application is in read-only mode. Press 'Ctrl+E' to enable edit mode."
            )
            return

        if not self.hosts_file.entries:
            self.update_status("No entries to delete")
            return

        if self.selected_entry_index >= len(self.hosts_file.entries):
            self.update_status("Invalid entry selected")
            return

        entry = self.hosts_file.entries[self.selected_entry_index]
        if entry.is_default_entry():
            self.update_status("❌ Cannot delete system default entry")
            return

        def handle_delete_confirmation(confirmed: bool) -> None:
            if not confirmed:
                self.update_status("Entry deletion cancelled")
                return

            # Delete the entry using the command-based manager method
            result = self.manager.execute_delete_command(
                self.hosts_file, self.selected_entry_index
            )
            if result.success:
                # Save the changes
                save_success, save_message = self.manager.save_hosts_file(self.hosts_file)
                if save_success:
                    # Adjust selected index if needed
                    if self.selected_entry_index >= len(self.hosts_file.entries):
                        self.selected_entry_index = max(0, len(self.hosts_file.entries) - 1)

                    # Refresh the table
                    self.table_handler.populate_entries_table()
                    self.details_handler.update_entry_details()
                    self.update_status(f"✅ {result.message} - Changes saved automatically")
                else:
                    self.update_status(f"Entry deleted but save failed: {save_message}")
            else:
                self.update_status(f"❌ {result.message}")

        self.push_screen(DeleteConfirmationModal(entry), handle_delete_confirmation)

    def action_quit(self) -> None:
        """Quit the application."""
        self.navigation_handler.quit_application()

    def action_undo(self) -> None:
        """Undo the last operation."""
        if not self.edit_mode:
            self.update_status("❌ Cannot undo: Application is in read-only mode")
            return

        if not self.manager.can_undo():
            self.update_status("Nothing to undo")
            return

        # Get description before undoing
        description = self.manager.get_undo_description()
        
        # Perform undo
        result = self.manager.undo_last_operation(self.hosts_file)
        if result.success:
            # Refresh the table and update UI
            self.table_handler.populate_entries_table()
            self.details_handler.update_entry_details()
            self.update_status(f"✅ Undone: {description or 'operation'}")
        else:
            self.update_status(f"❌ Undo failed: {result.message}")

    def action_redo(self) -> None:
        """Redo the last undone operation."""
        if not self.edit_mode:
            self.update_status("❌ Cannot redo: Application is in read-only mode")
            return

        if not self.manager.can_redo():
            self.update_status("Nothing to redo")
            return

        # Get description before redoing
        description = self.manager.get_redo_description()
        
        # Perform redo
        result = self.manager.redo_last_operation(self.hosts_file)
        if result.success:
            # Refresh the table and update UI
            self.table_handler.populate_entries_table()
            self.details_handler.update_entry_details()
            self.update_status(f"✅ Redone: {description or 'operation'}")
        else:
            self.update_status(f"❌ Redo failed: {result.message}")

    def action_refresh_dns(self) -> None:
        """Manually refresh DNS resolution for all entries."""
        if not self.edit_mode:
            self.update_status(
                "❌ Cannot resolve DNS names: Application is in read-only mode. Press 'Ctrl+E' to enable edit mode."
            )
            return

        if not self.hosts_file.entries:
            self.update_status("No entries to resolve")
            return

        # Get entries that need DNS resolution
        dns_entries = self.hosts_file.get_dns_entries()
        if not dns_entries:
            self.update_status("No entries with DNS names found")
            return

        # Remember the currently selected entry before DNS update
        current_entry = None
        if self.hosts_file.entries and self.selected_entry_index < len(
            self.hosts_file.entries
        ):
            current_entry = self.hosts_file.entries[self.selected_entry_index]

        async def refresh_dns():
            try:
                # Extract DNS names (not hostnames!) from entries
                dns_names = [entry.dns_name for entry in dns_entries if entry.dns_name]
                
                if not dns_names:
                    self.update_status("No valid DNS names found to resolve")
                    return
                
                resolved_count = 0
                failed_count = 0
                
                # Resolve each DNS name and apply results back to entries
                for dns_name in dns_names:
                    resolution = await self.dns_service.resolve_entry_async(dns_name)
                    
                    # Find the corresponding entry and update it
                    for entry in dns_entries:
                        if entry.dns_name == dns_name:
                            # Apply resolution results to entry fields
                            entry.last_resolved = resolution.resolved_at
                            entry.dns_resolution_status = resolution.status.value
                            
                            if resolution.is_success():
                                # Update both resolved_ip and ip_address for the hosts file
                                entry.ip_address = resolution.resolved_ip
                                entry.resolved_ip = resolution.resolved_ip
                                resolved_count += 1
                            else:
                                failed_count += 1
                            break
                
                # Save hosts file with updated DNS information
                if resolved_count > 0 or failed_count > 0:
                    save_success, save_message = self.manager.save_hosts_file(self.hosts_file)
                    if not save_success:
                        self.update_status(f"❌ DNS resolution completed but save failed: {save_message}")
                        return
                
                # Update the UI and restore cursor position
                self.table_handler.populate_entries_table()
                self.table_handler.restore_cursor_position(current_entry)
                self.details_handler.update_entry_details()
                
                # Provide detailed status message
                if failed_count == 0:
                    self.update_status(f"✅  DNS resolution completed for {resolved_count} entries")
                elif resolved_count == 0:
                    self.update_status(f"❌  DNS resolution failed for all {failed_count} entries")
                else:
                    self.update_status(f"⚠️  DNS resolution: {resolved_count} succeeded, {failed_count} failed")
                    
            except Exception as e:
                self.update_status(f"❌ DNS resolution failed: {e}")

        # Run DNS resolution in background
        self.run_worker(refresh_dns(), exclusive=False)
        self.update_status("🔄 Starting DNS resolution...")

    def action_update_single_dns(self) -> None:
        """Manually refresh DNS resolution for the currently selected entry."""
        if not self.edit_mode:
            self.update_status(
                "❌ Cannot resolve DNS names: Application is in read-only mode. Press 'Ctrl+E' to enable edit mode."
            )
            return

        if not self.hosts_file.entries:
            self.update_status("No entries available")
            return

        if self.selected_entry_index >= len(self.hosts_file.entries):
            self.update_status("Invalid entry selected")
            return

        entry = self.hosts_file.entries[self.selected_entry_index]
        
        # Check if the entry has a DNS name to resolve
        if not hasattr(entry, 'dns_name') or not entry.dns_name:
            self.update_status("❌ Selected entry has no DNS name to resolve")
            return

        # Remember the currently selected entry before DNS update
        current_entry = entry

        async def update_single_dns():
            try:
                dns_name = entry.dns_name
                
                # Resolve the DNS name
                resolution = await self.dns_service.resolve_entry_async(dns_name)
                
                # Apply resolution results to entry fields
                entry.last_resolved = resolution.resolved_at
                entry.dns_resolution_status = resolution.status.value
                
                if resolution.is_success():
                    # Update both resolved_ip and ip_address for the hosts file
                    entry.ip_address = resolution.resolved_ip
                    entry.resolved_ip = resolution.resolved_ip
                    
                    # Save hosts file with updated DNS information
                    save_success, save_message = self.manager.save_hosts_file(self.hosts_file)
                    if not save_success:
                        self.update_status(f"❌ DNS resolution completed but save failed: {save_message}")
                        return
                    
                    # Update the UI and restore cursor position
                    self.table_handler.populate_entries_table()
                    self.table_handler.restore_cursor_position(current_entry)
                    self.details_handler.update_entry_details()
                    
                    self.update_status(f"✅ DNS updated: {dns_name} → {resolution.resolved_ip}")
                else:
                    # Resolution failed, save the status update
                    save_success, save_message = self.manager.save_hosts_file(self.hosts_file)
                    if save_success:
                        # Update the UI to show failed status and restore cursor position
                        self.table_handler.populate_entries_table()
                        self.table_handler.restore_cursor_position(current_entry)
                        self.details_handler.update_entry_details()
                    
                    error_msg = resolution.error_message or "Unknown error"
                    self.update_status(f"❌ DNS resolution failed for {dns_name}: {error_msg}")
                    
            except Exception as e:
                self.update_status(f"❌ DNS resolution error: {e}")

        # Run DNS resolution in background
        self.run_worker(update_single_dns(), exclusive=False)
        self.update_status(f"🔄 Resolving DNS for {entry.dns_name}...")

    def action_show_filters(self) -> None:
        """Show advanced filtering modal."""
        def handle_filter_result(filter_options: FilterOptions) -> None:
            if filter_options is None:
                # User cancelled
                self.update_status("Filtering cancelled")
                return

            # Apply the new filter options
            self.current_filter_options = filter_options
            
            # Update the search term from filter if it has one
            if filter_options.search_term:
                self.search_term = filter_options.search_term
                # Update the search input to reflect the filter search term
                try:
                    search_input = self.query_one("#search-input", Input)
                    search_input.value = filter_options.search_term
                except Exception:
                    pass  # Search input not ready
            else:
                # Clear search term if no search in filter
                self.search_term = ""
                try:
                    search_input = self.query_one("#search-input", Input)
                    search_input.value = ""
                except Exception:
                    pass

            # Refresh the table with new filtering
            self.table_handler.populate_entries_table()
            self.details_handler.update_entry_details()
            
            # Get filter statistics for status message
            counts = self.entry_filter.count_filtered_entries(self.hosts_file.entries, filter_options)
            preset_info = f" (preset: {filter_options.preset_name})" if filter_options.preset_name else ""
            self.update_status(f"✅ Filter applied: showing {counts['filtered']} of {counts['total']} entries{preset_info}")

        # Show the filter modal with current options and entries for preview
        self.push_screen(
            FilterModal(
                initial_options=self.current_filter_options,
                entries=self.hosts_file.entries,
                entry_filter=self.entry_filter
            ),
            handle_filter_result
        )

    def _resolve_new_dns_entry(self, entry) -> None:
        """Trigger DNS resolution for a newly added DNS entry."""
        if not hasattr(entry, 'dns_name') or not entry.dns_name:
            return
            
        async def resolve_and_activate():
            try:
                # Resolve the DNS name
                resolution = await self.dns_service.resolve_entry_async(entry.dns_name)
                
                if resolution.is_success():
                    # Find the entry in the hosts file and update it
                    for hosts_entry in self.hosts_file.entries:
                        if (hasattr(hosts_entry, 'dns_name') and 
                            hosts_entry.dns_name == entry.dns_name and
                            hosts_entry.hostnames == entry.hostnames):
                            
                            # Update the entry with resolved IP
                            hosts_entry.ip_address = resolution.resolved_ip
                            hosts_entry.resolved_ip = resolution.resolved_ip
                            hosts_entry.last_resolved = resolution.resolved_at
                            hosts_entry.dns_resolution_status = resolution.status.value
                            hosts_entry.is_active = True  # Activate the entry
                            
                            # Save the updated hosts file
                            save_success, save_message = self.manager.save_hosts_file(self.hosts_file)
                            if save_success:
                                # Update UI - use direct calls since we're in the same async context
                                self.table_handler.populate_entries_table()
                                self.details_handler.update_entry_details()
                                self.update_status(f"✅ DNS resolved: {entry.dns_name} → {resolution.resolved_ip} (entry activated)")
                            else:
                                self.update_status(f"❌ DNS resolved but save failed: {save_message}")
                            break
                else:
                    # Resolution failed, update status but keep entry inactive
                    for hosts_entry in self.hosts_file.entries:
                        if (hasattr(hosts_entry, 'dns_name') and 
                            hosts_entry.dns_name == entry.dns_name and
                            hosts_entry.hostnames == entry.hostnames):
                            
                            hosts_entry.dns_resolution_status = resolution.status.value
                            hosts_entry.last_resolved = resolution.resolved_at
                            break
                            
                    self.update_status(f"❌ DNS resolution failed for {entry.dns_name}: {resolution.error_message or 'Unknown error'}")
                    
            except Exception as e:
                self.update_status(f"❌ DNS resolution error for {entry.dns_name}: {str(e)}")
        
        # Start the resolution in background
        self.run_worker(resolve_and_activate(), exclusive=False)

    async def on_shutdown(self) -> None:
        """Clean up resources when the app is shutting down."""
        # No DNS service cleanup needed for manual-only resolution
        pass

    # Delegated methods for backward compatibility with tests
    def has_entry_changes(self) -> bool:
        """Check if the current entry has been modified from its original values."""
        return self.edit_handler.has_entry_changes()

    def exit_edit_entry_mode(self) -> None:
        """Helper method to exit entry edit mode and clean up."""
        self.edit_handler.exit_edit_entry_mode()

    def populate_entries_table(self) -> None:
        """Populate the left pane with hosts entries using DataTable."""
        self.table_handler.populate_entries_table()

    def restore_cursor_position(self, previous_entry) -> None:
        """Restore cursor position after reload, maintaining selection if possible."""
        self.table_handler.restore_cursor_position(previous_entry)

    def get_visible_entries(self) -> list:
        """Get the list of entries that are visible in the table (after filtering)."""
        return self.table_handler.get_visible_entries()

    def display_index_to_actual_index(self, display_index: int) -> int:
        """Convert a display table index to the actual hosts file entry index."""
        return self.table_handler.display_index_to_actual_index(display_index)

    def actual_index_to_display_index(self, actual_index: int) -> int:
        """Convert an actual hosts file entry index to a display table index."""
        return self.table_handler.actual_index_to_display_index(actual_index)

    def update_entry_details(self) -> None:
        """Update the right pane with selected entry details."""
        self.details_handler.update_entry_details()

    def update_details_display(self) -> None:
        """Update the static details display."""
        self.details_handler.update_details_display()

    def update_edit_form(self) -> None:
        """Update the edit form with current entry values."""
        self.details_handler.update_edit_form()

    def watch_entry_edit_mode(self, entry_edit_mode: bool) -> None:
        """Update the right pane border title when entry edit mode changes."""
        try:
            right_pane = self.query_one(".right-pane")
            if entry_edit_mode:
                right_pane.border_title = "Edit Entry"
            else:
                right_pane.border_title = "Entry Details"
        except Exception:
            # App not fully initialized yet, ignore
            pass
