"""Midnight Commander-style TUI for disk management."""

import curses
import socket
from typing import List, Optional, Tuple
from jusfltuls.models import Disk, Partition
#from p20260130_12_kilo01.models import Disk, Partition


class Panel:
    """Represents a panel in the MC-style interface."""

    def __init__(self, name: str):
        self.name = name
        self.items: List[str] = []
        self.selected_index = 0
        self.is_active = False

    def set_items(self, items: List[str]) -> None:
        """Set the items to display in the panel."""
        self.items = items
        # Ensure selected index is within bounds
        if self.selected_index >= len(self.items):
            self.selected_index = max(0, len(self.items) - 1)

    def move_up(self) -> None:
        """Move selection up."""
        if self.selected_index > 0:
            self.selected_index -= 1

    def move_down(self) -> None:
        """Move selection down."""
        if self.selected_index < len(self.items) - 1:
            self.selected_index += 1

    def get_selected_item(self) -> Optional[str]:
        """Get the currently selected item."""
        if 0 <= self.selected_index < len(self.items):
            return self.items[self.selected_index]
        return None


class DiskManagerUI:
    """Midnight Commander-style disk management UI."""

    # Color pairs
    COLOR_NORMAL = 1
    COLOR_SELECTED = 2
    COLOR_HEADER = 3
    COLOR_BORDER = 4
    COLOR_FOOTER = 5
    COLOR_OK = 6    # Green for snapper OK status
    COLOR_WARN = 7  # Red for stale scrub (>600h)

    def __init__(self, stdscr):
        self.stdscr = stdscr
        self.left_panel = Panel("Disks")
        self.right_panel = Panel("Partitions")
        self.active_panel = "left"  # "left" or "right"
        self.disks: List[Disk] = []
        self.error_message: Optional[str] = None
        self.status_message: Optional[str] = None
        self.snapper_managed_devices: set = set()  # Devices managed by snapper

        # Initialize curses
        curses.curs_set(0)  # Hide cursor
        self.stdscr.nodelay(False)  # Blocking input (wait for key press)
        self._init_colors()

        # Get terminal dimensions
        self.height, self.width = self.stdscr.getmaxyx()

    def _init_colors(self) -> None:
        """Initialize color pairs."""
        if curses.has_colors():
            curses.start_color()
            try:
                curses.use_default_colors()
                bg = -1
            except:
                bg = curses.COLOR_BLACK

            # Define color pairs
            curses.init_pair(self.COLOR_NORMAL, curses.COLOR_WHITE, bg)
            curses.init_pair(self.COLOR_SELECTED, curses.COLOR_BLACK, curses.COLOR_CYAN)
            curses.init_pair(self.COLOR_HEADER, curses.COLOR_WHITE, curses.COLOR_BLUE)
            curses.init_pair(self.COLOR_BORDER, curses.COLOR_CYAN, bg)
            curses.init_pair(self.COLOR_FOOTER, curses.COLOR_BLACK, curses.COLOR_WHITE)
            curses.init_pair(self.COLOR_OK, curses.COLOR_GREEN, bg)
            curses.init_pair(self.COLOR_WARN, curses.COLOR_RED, bg)

    def update_disks(self, disks: List[Disk]) -> None:
        """Update the disk list and refresh panels.

        Args:
            disks: List of Disk objects to display
        """
        self.disks = disks
        # Load snapper managed devices
        from jusfltuls.snapper import get_snapper_managed_devices
        self.snapper_managed_devices = get_snapper_managed_devices()
        self._update_panel_contents()

    def _update_panel_contents(self) -> None:
        """Update the contents of both panels based on current disk selection."""
        # Update left panel (disks) with health info if available
        self._update_left_panel()

        # Update right panel with ALL partitions from ALL disks
        self._update_all_partitions_panel()

    def _update_left_panel(self) -> None:
        """Update left panel with disk information including health status."""
        disk_items = []
        for disk in self.disks:
            # Basic info: name, size, type
            base_text = f"{disk.name:10} {disk.size:>8} {disk.disk_type:5}"

            # Add health info if available
            if disk.has_health_info():
                health_str = disk.health_status or "?"
                poh_str = f"{disk.power_on_hours}h" if disk.power_on_hours else "?"
                any_in_progress = disk.short_test_in_progress or disk.long_test_in_progress
                # Show short test status
                if disk.short_test_in_progress and disk.short_test_remaining_percent is not None:
                    short_str = f"{disk.short_test_remaining_percent}%"
                elif disk.last_short_test_hours_ago is not None:
                    sfx = "!" if disk.last_short_test_failed else "h"
                    short_str = f"{disk.last_short_test_hours_ago}{sfx}"
                elif any_in_progress:
                    short_str = "-"
                else:
                    short_str = "?"
                # Show long test status
                if disk.long_test_in_progress and disk.long_test_remaining_percent is not None:
                    long_str = f"{disk.long_test_remaining_percent}%"
                elif disk.last_long_test_hours_ago is not None:
                    sfx = "!" if disk.last_long_test_failed else "h"
                    long_str = f"{disk.last_long_test_hours_ago}{sfx}"
                elif any_in_progress:
                    long_str = "-"
                else:
                    long_str = "?"
                item_text = f"{base_text} {health_str:6} {poh_str:>6} {short_str:>6} {long_str:>6}"
            elif disk.has_test_info():
                # Show test info even if health status not loaded yet
                poh_str = f"{disk.power_on_hours}h" if disk.power_on_hours else "?"
                any_in_progress = disk.short_test_in_progress or disk.long_test_in_progress
                if disk.short_test_in_progress and disk.short_test_remaining_percent is not None:
                    short_str = f"{disk.short_test_remaining_percent}%"
                elif disk.last_short_test_hours_ago is not None:
                    sfx = "!" if disk.last_short_test_failed else "h"
                    short_str = f"{disk.last_short_test_hours_ago}{sfx}"
                elif any_in_progress:
                    short_str = "-"
                else:
                    short_str = "?"
                if disk.long_test_in_progress and disk.long_test_remaining_percent is not None:
                    long_str = f"{disk.long_test_remaining_percent}%"
                elif disk.last_long_test_hours_ago is not None:
                    sfx = "!" if disk.last_long_test_failed else "h"
                    long_str = f"{disk.last_long_test_hours_ago}{sfx}"
                elif any_in_progress:
                    long_str = "-"
                else:
                    long_str = "?"
                item_text = f"{base_text} {'?':6} {poh_str:>6} {short_str:>6} {long_str:>6}"
            else:
                item_text = base_text

            disk_items.append(item_text)
        self.left_panel.set_items(disk_items)

    def _update_all_partitions_panel(self) -> None:
        """Update right panel with all partitions from all disks."""
        partition_items = []
        for disk in self.disks:
            # Add disk header
            partition_items.append(f"-- {disk.name} ({disk.size}) --")
            # Add partitions for this disk
            if disk.partitions:
                for part in disk.partitions:
                    # Build status indicators
                    snapper_indicator = ""  # "SN" if snapper managed
                    snapshot_count = ""  # Number of snapshots
                    btrfs_error_indicator = ""
                    btrfs_scrub_indicator = ""
                    mountpoint_display = ""

                    if part.filesystem == "btrfs":
                        # Show snapper indicator
                        if part.snapper_managed:
                            snapper_indicator = "SN"
                        # Show snapshot count
                        if part.snapper_snapshot_count > 0:
                            snapshot_count = str(part.snapper_snapshot_count)
                        # Show btrfs device stats: OK or ERR
                        if part.btrfs_errors:
                            btrfs_error_indicator = part.btrfs_errors
                        # Show scrub status: percentage if running
                        if part.btrfs_scrub_status:
                            btrfs_scrub_indicator = part.btrfs_scrub_status
                        # Show mountpoint
                        if part.mountpoint:
                            mountpoint_display = part.mountpoint
                    else:
                        # For non-btrfs, just show mountpoint if available
                        if part.mountpoint:
                            mountpoint_display = part.mountpoint

                    # Scrub age column — raw hours embedded for colour detection in _draw_panel
                    if part.filesystem == "btrfs" and part.btrfs_last_scrub_hours is not None:
                        scrub_age = f"SCRUB:{part.btrfs_last_scrub_hours}h"
                    else:
                        scrub_age = ""

                    # Format: partition size fs snapper snapshots btrfs_errors btrfs_scrub scrub_age mountpoint
                    item_text = f"  {part.name:12} {part.size:>8} {part.filesystem:8} {snapper_indicator:3} {snapshot_count:4} {btrfs_error_indicator:4} {btrfs_scrub_indicator:9} {scrub_age:12} {mountpoint_display}"
                    partition_items.append(item_text)
            else:
                partition_items.append("  (no partitions)")
        self.right_panel.set_items(partition_items)

    def load_disk_health(self, disk_idx: int) -> bool:
        """Load health information for the disk at the given index.

        Args:
            disk_idx: Index of the disk in self.disks

        Returns:
            True if health info was loaded/updated, False otherwise
        """
        if 0 <= disk_idx < len(self.disks):
            disk = self.disks[disk_idx]
            from jusfltuls.smartctl import SmartHealthMonitor

            monitor = SmartHealthMonitor()
            health = monitor.get_disk_health(disk.device_path)

            disk.health_status = health.overall_health
            disk.power_on_hours = health.power_on_hours
            # Short test info
            disk.last_short_test_hours_ago = health.last_short_test_hours_ago
            disk.last_short_test_failed = health.last_short_test_failed
            disk.short_test_in_progress = health.short_test_in_progress
            disk.short_test_remaining_percent = health.short_test_remaining_percent
            # Long test info
            disk.last_long_test_hours_ago = health.last_long_test_hours_ago
            disk.last_long_test_failed = health.last_long_test_failed
            disk.long_test_in_progress = health.long_test_in_progress
            disk.long_test_remaining_percent = health.long_test_remaining_percent

            return True
        return False

    def start_short_test(self, disk_idx: int) -> Tuple[bool, str]:
        """Start a short self-test on the disk at the given index.

        Args:
            disk_idx: Index of the disk in self.disks

        Returns:
            Tuple of (success, message)
        """
        if 0 <= disk_idx < len(self.disks):
            disk = self.disks[disk_idx]
            from jusfltuls.smartctl import SmartHealthMonitor

            # Show confirmation dialog
            if not self._show_confirm_dialog(
                "Start Short Self-Test",
                f"Start short self-test on {disk.name}?",
                f"Device: {disk.device_path}\nModel: {disk.disk_type} - {disk.size}\n\nShort test typically takes 1-2 minutes."
            ):
                return False, "Test cancelled"

            monitor = SmartHealthMonitor()
            success, message = monitor.start_short_test(disk.device_path)

            if success:
                # Mark short test as in progress
                disk.short_test_in_progress = True
                disk.short_test_remaining_percent = 100

            return success, message
        return False, "Invalid disk index"

    def start_long_test(self, disk_idx: int) -> Tuple[bool, str]:
        """Start a long self-test on the disk at the given index.

        Args:
            disk_idx: Index of the disk in self.disks

        Returns:
            Tuple of (success, message)
        """
        if 0 <= disk_idx < len(self.disks):
            disk = self.disks[disk_idx]
            from jusfltuls.smartctl import SmartHealthMonitor

            # Show confirmation dialog
            if not self._show_confirm_dialog(
                "Start Long Self-Test",
                f"Start long self-test on {disk.name}?",
                f"Device: {disk.device_path}\nModel: {disk.disk_type} - {disk.size}\n\nLong test can take several hours depending on disk size. Continue?"
            ):
                return False, "Test cancelled"

            monitor = SmartHealthMonitor()
            success, message = monitor.start_long_test(disk.device_path)

            if success:
                # Mark long test as in progress
                disk.long_test_in_progress = True
                disk.long_test_remaining_percent = 100

            return success, message
        return False, "Invalid disk index"

    def start_btrfs_scrub(self, partition_idx: int) -> Tuple[bool, str]:
        """Start a btrfs scrub on the partition at the given index in the right panel.

        Args:
            partition_idx: Index in the right panel (includes disk headers)

        Returns:
            Tuple of (success, message)
        """
        # Find the partition corresponding to this right panel index
        current_idx = 0
        for disk in self.disks:
            # Skip disk header
            current_idx += 1
            for part in disk.partitions:
                if current_idx == partition_idx:
                    # Found the partition
                    if part.filesystem != "btrfs":
                        return False, "Not a btrfs partition"
                    if not part.mountpoint:
                        return False, "Partition not mounted"

                    # Show confirmation dialog
                    if not self._show_confirm_dialog(
                        "Start Btrfs Scrub",
                        f"Start scrub on {part.name}?",
                        f"Mountpoint: {part.mountpoint}\nFilesystem: {part.filesystem}\nSize: {part.size}\n\nScrub will verify all data checksums and can take a long time."
                    ):
                        return False, "Scrub cancelled"

                    import subprocess
                    import sys
                    from jusfltuls.shell_calls import build_sudo_btrfs_scrub_start, get_debug_mode

                    cmd = build_sudo_btrfs_scrub_start(part.mountpoint)
                    cmd_str = ' '.join(cmd)
                    # Debug: print command being executed
                    if get_debug_mode():
                        print(f"\n[DEBUG] >>> Executing: {cmd_str}", file=sys.stderr, flush=True)
                    # Launch scrub in background and return immediately
                    try:
                        subprocess.Popen(
                            cmd,
                            stdout=subprocess.DEVNULL,
                            stderr=subprocess.DEVNULL,
                            start_new_session=True
                        )
                        # Mark scrub as starting - will show percentage on refresh
                        part.btrfs_scrub_status = "0%"
                        return True, f"Scrub started on {part.name}"
                    except Exception as e:
                        return False, f"Failed to start scrub: {e}"
                current_idx += 1
            # If no partitions, there's a "(no partitions)" line
            if not disk.partitions:
                current_idx += 1

        return False, "Invalid partition index"

    def update_scrub_status(self) -> None:
        """Update scrub status for all btrfs partitions.

        This should be called periodically to refresh the scrub progress.
        """
        from jusfltuls.disk_scanner import get_btrfs_scrub_status

        for disk in self.disks:
            for part in disk.partitions:
                if part.filesystem == "btrfs" and part.mountpoint:
                    part.btrfs_scrub_status = get_btrfs_scrub_status(part.mountpoint)

    def get_selected_partition(self) -> Optional[Tuple[int, int]]:
        """Get the disk and partition indices for the currently selected item in right panel.

        Returns:
            Tuple of (disk_idx, partition_idx) or None if not a partition
        """
        if self.active_panel != "right":
            return None

        right_idx = self.right_panel.selected_index
        current_idx = 0

        for disk_idx, disk in enumerate(self.disks):
            # Skip disk header
            if current_idx == right_idx:
                return None  # Selected a disk header
            current_idx += 1

            for part_idx, part in enumerate(disk.partitions):
                if current_idx == right_idx:
                    return (disk_idx, part_idx)
                current_idx += 1

            # If no partitions, there's a "(no partitions)" line
            if not disk.partitions:
                if current_idx == right_idx:
                    return None
                current_idx += 1

        return None

    def _get_partition_indices_for_disk(self, disk_idx: int) -> Tuple[int, int]:
        """Get the start and end indices in the right panel for a given disk's partitions.

        Returns:
            Tuple of (start_index, end_index) for the disk's partitions in the right panel.
        """
        start_idx = 0
        for i, disk in enumerate(self.disks):
            # Each disk has a header line
            if i == disk_idx:
                # Start after the header
                return start_idx + 1, start_idx + 1 + len(disk.partitions)
            # Header + partitions for this disk (or just header + 1 if no partitions)
            start_idx += 1 + max(1, len(disk.partitions))
        return 0, 0

    def switch_panel(self) -> None:
        """Switch between left and right panels."""
        if self.active_panel == "left":
            self.active_panel = "right"
            self.left_panel.is_active = False
            self.right_panel.is_active = True
            # Jump to the partition of the currently selected disk
            disk_idx = self.left_panel.selected_index
            start_idx, end_idx = self._get_partition_indices_for_disk(disk_idx)
            if start_idx < end_idx:
                self.right_panel.selected_index = start_idx
        else:
            self.active_panel = "left"
            self.left_panel.is_active = True
            self.right_panel.is_active = False

    def move_up(self) -> None:
        """Move selection up in active panel."""
        if self.active_panel == "left":
            self.left_panel.move_up()
        else:
            self.right_panel.move_up()

    def move_down(self) -> None:
        """Move selection down in active panel."""
        if self.active_panel == "left":
            self.left_panel.move_down()
        else:
            self.right_panel.move_down()

    def _safe_addstr(self, y: int, x: int, text: str, attr: int = 0) -> None:
        """Safely add a string to the screen, handling boundary issues."""
        try:
            if x < self.width and y < self.height:
                max_len = self.width - x
                if max_len > 0:
                    self.stdscr.addnstr(y, x, text, max_len, attr)
        except curses.error:
            pass

    def draw(self) -> None:
        """Draw the complete UI."""
        self.stdscr.clear()
        self.height, self.width = self.stdscr.getmaxyx()

        # Calculate panel dimensions
        panel_width = (self.width - 3) // 2
        panel_height = self.height - 4  # Leave room for header and footer

        # Draw header
        self._draw_header()

        # Draw left panel (disks) with updated column headers
        self._draw_panel(
            self.left_panel,
            1, 0,
            panel_height, panel_width,
            "Disks",
            ["Device", "Size", "Type", "Health", "POH", "Short", "Long"]
        )

        # Draw separator line between panels
        for y in range(1, self.height - 2):
            self._safe_addstr(y, panel_width + 1, "│", curses.color_pair(self.COLOR_BORDER))

        # Draw right panel (partitions)
        self._draw_panel(
            self.right_panel,
            1, panel_width + 2,
            panel_height, panel_width,
            "Partitions",
            ["Partition", "Size", "FS", "SN", "#Snp", "Err", "Scrub", "Scr.Age", "Mount"]
        )

        # Draw footer
        self._draw_footer()

        self.stdscr.refresh()

    def _draw_header(self) -> None:
        """Draw the top header bar."""
        hostname = socket.gethostname()
        title = f" Disk Manager - {hostname} "

        # Center the title
        start_x = max(0, (self.width - len(title)) // 2)

        header_text = title.center(self.width)
        self._safe_addstr(0, 0, header_text, curses.color_pair(self.COLOR_HEADER) | curses.A_BOLD)

    def _draw_panel(self, panel: Panel, y: int, x: int, height: int, width: int,
                    title: str, headers: List[str]) -> None:
        """Draw a panel with its contents."""
        # Draw panel border
        border_attr = curses.color_pair(self.COLOR_BORDER)
        if panel.is_active:
            border_attr |= curses.A_BOLD

        # Top border with title
        title_str = f" {title} "
        self._safe_addstr(y, x, "+" + "-" * (width - 2) + "+", border_attr)

        # Header row
        header_y = y + 1
        header_text = ""
        col_widths = [11, 9, 6, 7, 7, 7, 7]  # Device, Size, Type, Health, POH, Short, Long
        for i, h in enumerate(headers):
            w = col_widths[i] if i < len(col_widths) else 10
            header_text += f"{h:<{w}}"

        header_line = f"|{header_text[:width-2]:<{width-2}}|"
        self._safe_addstr(header_y, x, header_line, curses.color_pair(self.COLOR_HEADER))

        # Separator line
        sep_y = header_y + 1
        self._safe_addstr(sep_y, x, "+" + "-" * (width - 2) + "+", border_attr)

        # Content area
        content_start_y = sep_y + 1
        content_height = height - 4  # Account for borders and header

        # Calculate visible range
        total_items = len(panel.items)
        visible_start = 0
        if total_items > content_height:
            # Scroll to keep selected item visible
            if panel.selected_index >= content_height:
                visible_start = panel.selected_index - content_height + 1

        for i in range(content_height):
            item_idx = visible_start + i
            row_y = content_start_y + i

            if row_y >= y + height - 1:
                break

            if item_idx < total_items:
                item_text = panel.items[item_idx]
                is_selected = (item_idx == panel.selected_index) and panel.is_active

                if is_selected:
                    attr = curses.color_pair(self.COLOR_SELECTED) | curses.A_BOLD
                else:
                    attr = curses.color_pair(self.COLOR_NORMAL)

                # Detect SCRUB:NNNh token anywhere in the line and colour it.
                # The token is a fixed-width column; text after it is the mountpoint.
                import re as _re
                scrub_match = _re.search(r'(SCRUB:(\d+)h)', item_text) if not is_selected else None
                if scrub_match:
                    scrub_hours = int(scrub_match.group(2))
                    scrub_token = scrub_match.group(1)       # e.g. "SCRUB:700h"
                    before = item_text[:scrub_match.start()] # text up to token
                    after  = item_text[scrub_match.end():]   # text after token
                    # Draw everything before the token normally
                    display_before = before[:width-2]
                    self._safe_addstr(row_y, x, f"|{display_before}", attr)
                    token_x = x + 1 + len(display_before)
                    scrub_attr = (curses.color_pair(self.COLOR_WARN) | curses.A_BOLD
                                  if scrub_hours > 600
                                  else curses.color_pair(self.COLOR_OK))
                    if token_x < x + width - 1:
                        self._safe_addstr(row_y, token_x, scrub_token, scrub_attr)
                    # Draw everything after the token (mountpoint) normally
                    after_x = token_x + len(scrub_token)
                    remaining_width = x + width - 1 - after_x
                    if remaining_width > 0:
                        self._safe_addstr(row_y, after_x, f"{after[:remaining_width]:<{remaining_width}}|", attr)
                elif " btrfs " in item_text and item_text.rstrip().endswith(" OK") and not is_selected:
                    # Draw the line with green OK
                    ok_pos = item_text.rfind(" OK")
                    main_part = item_text[:ok_pos]
                    display_main = main_part[:width-2]
                    self._safe_addstr(row_y, x, f"|{display_main}", attr)
                    ok_x = x + 1 + len(display_main)
                    if ok_x < x + width - 1:
                        self._safe_addstr(row_y, ok_x, " OK",
                                          curses.color_pair(self.COLOR_OK) | curses.A_BOLD)
                    rest_x = ok_x + 3
                    if rest_x < x + width:
                        self._safe_addstr(row_y, rest_x, " " * (x + width - rest_x - 1) + "|",
                                          curses.color_pair(self.COLOR_BORDER))
                else:
                    # Truncate or pad to fit
                    display_text = item_text[:width-2]
                    line = f"|{display_text:<{width-2}}|"
                    self._safe_addstr(row_y, x, line, attr)
            else:
                # Empty line
                line = f"|{' ' * (width-2)}|"
                self._safe_addstr(row_y, x, line, curses.color_pair(self.COLOR_NORMAL))

        # Bottom border
        bottom_y = y + height - 1
        if bottom_y < self.height - 2:
            self._safe_addstr(bottom_y, x, "+" + "-" * (width - 2) + "+", border_attr)

    def _show_confirm_dialog(self, title: str, question: str, details: str = "") -> bool:
        """Show a confirmation dialog and return True if user confirms.

        Args:
            title: Dialog title
            question: Main question text
            details: Additional details to show

        Returns:
            True if user confirmed, False otherwise
        """
        # Calculate dialog dimensions
        dialog_height = 10 + (details.count('\n') if details else 0)
        dialog_width = min(60, self.width - 4)
        dialog_y = (self.height - dialog_height) // 2
        dialog_x = (self.width - dialog_width) // 2

        # Save current screen
        self.stdscr.nodelay(False)  # Switch to blocking for dialog

        # Draw dialog box
        dialog = curses.newwin(dialog_height, dialog_width, dialog_y, dialog_x)
        dialog.box()

        # Draw title
        title_x = (dialog_width - len(title)) // 2
        dialog.addstr(0, title_x, f" {title} ", curses.A_BOLD)

        # Draw question
        dialog.addstr(2, 2, question, curses.A_BOLD)

        # Draw details
        if details:
            line_num = 4
            for line in details.split('\n'):
                if line_num < dialog_height - 3:
                    dialog.addstr(line_num, 2, line[:dialog_width-4])
                    line_num += 1

        # Draw buttons
        button_y = dialog_height - 2
        yes_x = dialog_width // 4
        no_x = (dialog_width * 3) // 4 - 6
        dialog.addstr(button_y, yes_x, "[ Yes ]", curses.A_REVERSE)
        dialog.addstr(button_y, no_x, "[ No ]")

        dialog.refresh()

        # Handle input
        selected = 0  # 0 = Yes, 1 = No
        while True:
            key = dialog.getch()

            if key in (ord('\t'), curses.KEY_RIGHT, curses.KEY_LEFT):
                # Toggle selection
                selected = 1 - selected
                if selected == 0:
                    dialog.addstr(button_y, yes_x, "[ Yes ]", curses.A_REVERSE)
                    dialog.addstr(button_y, no_x, "[ No ]")
                else:
                    dialog.addstr(button_y, yes_x, "[ Yes ]")
                    dialog.addstr(button_y, no_x, "[ No ]", curses.A_REVERSE)
                dialog.refresh()
            elif key in (10, 13, ord(' ')):  # Enter or Space
                result = (selected == 0)
                break
            elif key in (ord('y'), ord('Y')):
                result = True
                break
            elif key in (ord('n'), ord('N'), 27):  # N or Escape
                result = False
                break

        # Restore non-blocking mode
        self.stdscr.nodelay(True)

        # Clear dialog area
        for y in range(dialog_y, dialog_y + dialog_height):
            self.stdscr.move(y, dialog_x)
            self.stdscr.clrtoeol()
        self.stdscr.refresh()

        return result

    def _draw_footer(self) -> None:
        """Draw the bottom footer bar."""
        footer_y = self.height - 2

        # Build footer text based on active panel
        if self.active_panel == "left":
            keys = [
                ("↑↓", "Navigate"),
                ("Tab", "Switch panel"),
                ("t", "Short Test"),
                ("l", "Long Test"),
                ("q", "Quit"),
                ("r", "Refresh"),
            ]
        else:
            keys = [
                ("↑↓", "Navigate"),
                ("Tab", "Switch panel"),
                ("s", "Scrub"),
                ("q", "Quit"),
                ("r", "Refresh"),
            ]

        footer_parts = []
        for key, action in keys:
            footer_parts.append(f" {key}:{action} ")

        footer_text = "".join(footer_parts)

        # Add status/error message if present
        if self.status_message:
            footer_text += f" | {self.status_message}"
        elif self.error_message:
            footer_text += f" | ERROR: {self.error_message[:30]}"

        # Pad or truncate to fit width
        if len(footer_text) < self.width:
            footer_text = footer_text.ljust(self.width)
        else:
            footer_text = footer_text[:self.width]

        self._safe_addstr(footer_y, 0, footer_text, curses.color_pair(self.COLOR_FOOTER))

        # Second footer line with hint
        hint_y = self.height - 1
        if self.active_panel == "left":
            hint_text = " t=short test, l=long test, Tab=switch panel, q=quit "
        else:
            hint_text = " s=start scrub, Tab=switch to left, q=quit "
        hint_text = hint_text.center(self.width)
        self._safe_addstr(hint_y, 0, hint_text, curses.color_pair(self.COLOR_NORMAL))

    def set_status(self, message: str) -> None:
        """Set a status message to display in the footer."""
        self.status_message = message
        self.error_message = None

    def set_error(self, message: str) -> None:
        """Set an error message to display in the footer."""
        self.error_message = message
        self.status_message = None

    def clear_messages(self) -> None:
        """Clear any status or error messages."""
        self.status_message = None
        self.error_message = None

    def get_selected_disk(self) -> Optional[Disk]:
        """Get the currently selected disk."""
        idx = self.left_panel.selected_index
        if 0 <= idx < len(self.disks):
            return self.disks[idx]
        return None

    def get_selected_partition(self) -> Optional[Partition]:
        """Get the currently selected partition."""
        disk = self.get_selected_disk()
        if disk:
            idx = self.right_panel.selected_index
            if 0 <= idx < len(disk.partitions):
                return disk.partitions[idx]
        return None
