#!/usr/bin/env python3
"""Optimized display handling with partial screen updates."""

import curses
import logging
import os
import time
from collections import OrderedDict
from pathlib import Path
from typing import Any, Dict, List, Optional, Tuple

from ..application.configuration import load_themes
from ..infrastructure.file_config_source import FileConfigSource
from .color_manager import ColorManager
from .layout import LayoutCalculator


class Display:
    """Optimized terminal UI display with differential updates and caching."""

    _ROLE_ALIASES = {
        "muted": "footer",
        "accent": "info",
        "chip": "header",
        "chip_active": "selected",
        "label": "footer",
    }
    _METRIC_ROLE_FALLBACKS = {
        "critical": ("metric_critical", "critical"),
        "warning": ("metric_warning", "warning"),
        "normal": ("metric_normal", "accent", "normal"),
    }

    def __init__(self, stdscr: Any, theme_file: Optional[Path] = None) -> None:
        """Initialize the display with optimization support.

        Args:
            stdscr: curses window object
            theme_file: Optional path to theme configuration file
        """
        self.stdscr = stdscr
        self.logger = logging.getLogger(self.__class__.__name__)
        self._config_source = FileConfigSource()

        # Initialize curses settings
        curses.start_color()
        curses.use_default_colors()
        curses.cbreak()
        curses.noecho()

        # Set non-blocking mode with minimal delay
        self.stdscr.nodelay(1)  # Make getch() non-blocking
        self.stdscr.timeout(0)  # Set timeout to 0ms

        # Advanced configuration
        self.stdscr.keypad(True)  # Enable keypad
        curses.use_env(True)  # Enable terminal size detection

        # Save original terminal state
        try:
            curses.def_prog_mode()  # Save current terminal mode
        except curses.error:
            pass

        # Load themes
        self.themes = self._load_themes(theme_file)
        self.current_theme = self._detect_terminal_theme()

        # Set minimum required dimensions
        self.min_height = 24
        self.min_width = 80

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

        # Calculate actual dimensions
        self.actual_height = max(self.height, self.min_height)
        self.actual_width = max(self.width, self.min_width)

        # Create pad with actual dimensions
        self.pad = curses.newpad(self.actual_height, self.actual_width)
        self.pad.keypad(True)

        # Save visible area info
        self.visible_height = min(self.height, self.actual_height)
        self.visible_width = min(self.width, self.actual_width)

        # Initialize color manager
        self.color_manager = ColorManager()

        # Hide cursor
        self.original_cursor: Optional[int] = None
        try:
            self.original_cursor = curses.curs_set(0)
        except Exception:
            # Cursor control may not be available on all terminals
            self.original_cursor = None

        # Setup colors
        self.setup_colors()

        # Initialize screen content tracking for differential updates with LRU
        self._screen_state: "OrderedDict[tuple[int, int], tuple[str, int]]" = (
            OrderedDict()
        )
        self._screen_state_max = 10000  # Prevent unbounded growth
        self._force_redraw = True  # First draw is always full
        self._frame_active = False
        self._frame_touched: set[tuple[int, int]] = set()
        self._frame_touched_spans: dict[int, list[tuple[int, int]]] = {}

        # String rendering cache with LRU eviction
        self._cache_size = 1024
        self._string_cache: "OrderedDict[str, str]" = OrderedDict()
        self._bar_cache: "OrderedDict[str, Tuple[str, int]]" = OrderedDict()

        # Performance stats
        self._perf_stats: dict[str, float] = {
            "redraws": 0.0,
            "partial_updates": 0.0,
            "strings_rendered": 0.0,
            "cache_hits": 0.0,
            "last_render_time": 0.0,
        }

        # Initialize layout calculator
        self.layout = LayoutCalculator(self)

        self.logger.debug(f"Display initialized: {self.width}x{self.height}")

    def _load_themes(
        self, theme_file: Optional[Path] = None
    ) -> dict[str, dict[str, Any]]:
        """Load theme configurations from JSON file efficiently."""
        try:
            themes = load_themes(theme_file, source=self._config_source)
            self.logger.debug(f"Loaded {len(themes)} themes")
            return themes
        except (RuntimeError, FileNotFoundError, ValueError, KeyError) as e:
            self.logger.error(f"Theme loading error: {str(e)}")
            # Return minimal default theme if file loading fails
            return {
                "default": {
                    "name": "Default Terminal",
                    "description": "Standard theme based on htop colors",
                    "colors": {
                        "normal": {"fg": "GREEN", "bg": -1, "attrs": []},
                        "selected": {"fg": "BLACK", "bg": "GREEN", "attrs": []},
                        "warning": {"fg": "YELLOW", "bg": -1, "attrs": []},
                        "critical": {"fg": "RED", "bg": -1, "attrs": []},
                        "info": {"fg": "CYAN", "bg": -1, "attrs": []},
                        "ai_process": {"fg": "MAGENTA", "bg": -1, "attrs": []},
                        "header": {"fg": "BLUE", "bg": -1, "attrs": []},
                        "footer": {"fg": "WHITE", "bg": -1, "attrs": []},
                    },
                    "progress_bar": {
                        "filled": "█",
                        "empty": "░",
                        "thresholds": {"critical": 80, "warning": 60},
                    },
                }
            }

    def _detect_terminal_theme(self) -> str:
        """Detect appropriate terminal theme based on environment."""
        # Check for environment variable override
        env_theme = os.environ.get("AITOP_THEME")
        if env_theme and env_theme in self.themes:
            self.logger.debug(f"Using theme from environment: {env_theme}")
            return env_theme

        # Check terminal type from environment
        term = os.environ.get("TERM", "").lower()
        colorterm = os.environ.get("COLORTERM", "").lower()

        # Detect color support level
        has_truecolor = "truecolor" in colorterm or "24bit" in colorterm
        has_256color = "256color" in term

        # Check if terminal actually supports color palette modification
        # Modern terminals (cosmic-term, alacritty, etc.) report truecolor
        # but don't support can_change_color(), so hex colors get approximated
        can_change = (
            curses.can_change_color() if hasattr(curses, "can_change_color") else False
        )

        # Choose appropriate theme based on terminal capabilities
        if has_truecolor and can_change:
            # Terminal supports true color WITH palette modification
            # Use hex-based themes (rare - mainly xterm)
            for theme_name in [
                "graphite_modern",
                "monokai_pro",
                "nord",
                "cyberpunk_neon",
            ]:
                if theme_name in self.themes:
                    self.logger.debug(f"Selected truecolor theme: {theme_name}")
                    return theme_name

        elif has_truecolor or has_256color:
            # Terminal has high color support but no palette modification
            # Use 256-color themes designed for approximation
            # This covers cosmic-term, alacritty, gnome-terminal, etc.
            for theme_name in ["graphite_modern", "solarized_dark", "material_ocean"]:
                if theme_name in self.themes:
                    self.logger.debug(
                        f"Selected 256-color theme: {theme_name} "
                        f"(truecolor={has_truecolor}, can_change={can_change})"
                    )
                    return theme_name

        # Default fallback for basic 8-color terminals
        self.logger.debug("Using default theme for basic terminal")
        return "default"

    def setup_colors(self) -> None:
        """Initialize color pairs based on current theme with better error handling."""
        try:
            theme = self.themes.get(self.current_theme, self.themes["default"])
            color_defs = theme.get("colors", {})

            # Initialize default pair first
            curses.init_pair(1, curses.COLOR_GREEN, -1)

            # Store attribute mappings for quick lookup
            self.theme_attrs = {}

            # Process each color definition in the theme
            for role, color_def in color_defs.items():
                try:
                    fg = color_def.get("fg", "WHITE")
                    bg = color_def.get("bg", -1)
                    attrs = color_def.get("attrs", [])

                    # Get color pair using enhanced color manager
                    color_pair = self.color_manager.get_color_pair(fg, bg)

                    # Combine with attributes
                    attr = color_pair
                    for attr_name in attrs:
                        if hasattr(curses, f"A_{attr_name}"):
                            attr |= getattr(curses, f"A_{attr_name}")

                    # Store for later use
                    self.theme_attrs[role] = attr

                except Exception as e:
                    self.logger.warning(f"Failed to set up color for {role}: {e}")
                    # Use default as fallback
                    self.theme_attrs[role] = curses.color_pair(1)

            self.logger.debug(f"Initialized colors for theme: {self.current_theme}")

        except Exception as e:
            self.logger.error(f"Color setup error: {e}")

    def get_dimensions(self) -> Tuple[int, int]:
        """Get current terminal dimensions."""
        self.height, self.width = self.stdscr.getmaxyx()
        return self.height, self.width

    def _get_cached_bar(self, value: float, width: int) -> Tuple[str, int]:
        """Get a cached progress bar or create a new one with LRU eviction."""
        # Generate cache key
        key = f"{value:.1f}_{width}"

        if key in self._bar_cache:
            # Mark as recently used
            self._bar_cache.move_to_end(key)
            return self._bar_cache[key]

        # Create new bar
        bar, color = self._create_bar_raw(value, width)

        # LRU eviction if at capacity
        if len(self._bar_cache) >= 100:
            self._bar_cache.popitem(last=False)  # Remove least recently used

        self._bar_cache[key] = (bar, color)
        return bar, color

    def _create_bar_raw(self, value: float, width: int) -> Tuple[str, int]:
        """Create a progress bar without caching."""
        # Ensure value is within bounds
        value = max(0, min(100, value))

        # Get theme characters and color
        theme = self.themes.get(self.current_theme, self.themes["default"])
        bar_chars = theme["progress_bar"]
        filled_char = str(bar_chars.get("filled", "█"))
        empty_char = str(bar_chars.get("empty", "░"))
        # Improve contrast for graphite-style rectangle bars by leaving unfilled
        # segments blank while preserving fixed width.
        if filled_char == "▮" and empty_char == "▯":
            empty_char = " "
        color = self.get_color(value)

        # Calculate available width
        available_width = min(width, self.width)
        if available_width <= 0:
            return "", color

        # Calculate filled portion
        filled_width = int((value * available_width) / 100)
        empty_width = available_width - filled_width

        # Create bar with exact width (optimized string creation)
        if filled_width == 0:
            bar = empty_char * empty_width
        elif empty_width == 0:
            bar = filled_char * filled_width
        else:
            bar = filled_char * filled_width + empty_char * empty_width

        return bar, color

    def create_bar(self, value: float, width: int) -> Tuple[str, int]:
        """Create a progress bar with caching for performance."""
        return self._get_cached_bar(value, width)

    def _resolve_theme_attr(self, role_candidates: Tuple[str, ...]) -> Optional[int]:
        """Resolve first available role from the active theme."""
        theme = self.themes.get(self.current_theme, self.themes["default"])
        color_defs = theme.get("colors", {})

        for role in role_candidates:
            resolved_role = self._ROLE_ALIASES.get(role, role)
            if resolved_role in color_defs or role in color_defs:
                return self.get_theme_attr(role)
        return None

    def get_color(self, value: float) -> int:
        """Get emphasized color for primary metric indicators."""
        theme = self.themes.get(self.current_theme, self.themes["default"])
        thresholds = theme.get("progress_bar", {}).get("thresholds", {})
        warning_threshold = float(thresholds.get("warning", 60))
        critical_threshold = float(thresholds.get("critical", 80))
        bold_attr = int(getattr(curses, "A_BOLD", 0))

        try:
            if value >= critical_threshold:
                role_key = "critical"
            elif value >= warning_threshold:
                role_key = "warning"
            else:
                role_key = "normal"

            role_candidates = self._METRIC_ROLE_FALLBACKS[role_key]
            resolved_attr = self._resolve_theme_attr(role_candidates)
            if resolved_attr is None:
                resolved_attr = self.get_theme_attr(role_key)
            return int(resolved_attr) | bold_attr
        except Exception:
            return curses.A_NORMAL

    def get_theme_attr(self, role: str) -> int:
        """Get color and attributes for a theme role with fallback handling."""
        requested_role = role
        role = self._ROLE_ALIASES.get(role, role)

        # Get from cached attributes if available
        if hasattr(self, "theme_attrs") and role in self.theme_attrs:
            return self.theme_attrs[role]

        # Fallback for direct attribute lookup
        try:
            theme = self.themes.get(self.current_theme, self.themes["default"])
            color_def = theme.get("colors", {}).get(role, {})
            if not color_def and requested_role != role:
                # Keep alias behavior resilient even on sparse themes.
                color_def = theme.get("colors", {}).get(requested_role, {})

            fg = color_def.get("fg", "WHITE")
            bg = color_def.get("bg", -1)
            attrs = color_def.get("attrs", [])

            # Get color pair using enhanced color manager
            color_pair = self.color_manager.get_color_pair(fg, bg)

            # Combine with attributes
            attr = color_pair
            for attr_name in attrs:
                if hasattr(curses, f"A_{attr_name}"):
                    attr |= getattr(curses, f"A_{attr_name}")

            # Store for future use
            if not hasattr(self, "theme_attrs"):
                self.theme_attrs = {}
            self.theme_attrs[role] = attr

            return attr
        except Exception as e:
            self.logger.warning(f"Failed to get theme attribute for {role}: {e}")
            return curses.A_NORMAL

    @staticmethod
    def _get_cell_key(y: int, x: int) -> Tuple[int, int]:
        """Generate a unique key for a screen cell."""
        return (y, x)

    def _register_frame_touch(self, y: int, x: int, text_len: int) -> None:
        """Record cell keys and horizontal spans rendered in this frame."""
        if not self._frame_active:
            return

        key = self._get_cell_key(y, x)
        self._frame_touched.add(key)

        if text_len <= 0:
            return
        span_end = x + text_len
        spans = self._frame_touched_spans.setdefault(y, [])
        spans.append((x, span_end))

    def _should_update_cell(self, y: int, x: int, text: str, attr: int) -> bool:
        """Determine if a cell needs updating based on current frame state."""
        key = self._get_cell_key(y, x)

        # Force redraw always updates
        if self._force_redraw:
            return True

        cached = self._screen_state.get(key)
        if cached is None:
            return True

        cached_text, cached_attr = cached
        return cached_text != text or cached_attr != attr

    def safe_addstr(
        self, y: int, x: int, text: str, attr: Optional[int] = None
    ) -> bool:
        """Safely add a string to the screen with caching and differential updates.

        Returns:
            bool: True if string was actually rendered, False if skipped
        """
        try:
            # Get default attribute if none provided
            if attr is None:
                attr = self.get_theme_attr("normal")

            # Get current dimensions
            visible_height = self.visible_height
            visible_width = self.visible_width

            # Validate coordinates - reject negative values
            if y < 0 or x < 0:
                return False

            # Skip if position is completely outside visible area
            if y >= visible_height or x >= visible_width:
                return False

            # Calculate available space
            remaining_width = visible_width - x
            if remaining_width <= 0:
                return False

            # Truncate text if needed
            if len(text) > remaining_width:
                text = text[:remaining_width]

            self._register_frame_touch(y, x, len(text))
            key = self._get_cell_key(y, x)
            cached = self._screen_state.get(key)

            # Check if this cell needs updating (differential update)
            if not self._should_update_cell(y, x, text, attr):
                return False

            render_text = text
            if cached is not None:
                previous_text, _previous_attr = cached
                if len(previous_text) > len(text):
                    padding = " " * (len(previous_text) - len(text))
                    render_text = f"{text}{padding}"
                    if len(render_text) > remaining_width:
                        render_text = render_text[:remaining_width]

            # Render the string
            try:
                self.pad.addstr(y, x, render_text, attr)
                self._perf_stats["strings_rendered"] += 1
                self._screen_state[key] = (text, attr)
                self._screen_state.move_to_end(key)
                if len(self._screen_state) > self._screen_state_max:
                    self._screen_state.popitem(last=False)
                return True
            except curses.error:
                # Handle specific error cases
                if y == visible_height - 1 and x + len(render_text) == visible_width:
                    # Special case: writing to bottom-right corner
                    if len(render_text) > 0:
                        self.pad.addstr(y, x, render_text[:-1], attr)
                        self._screen_state[key] = (text, attr)
                        self._screen_state.move_to_end(key)
                        if len(self._screen_state) > self._screen_state_max:
                            self._screen_state.popitem(last=False)
                        return True
            return False
        except curses.error:
            return False

    def begin_frame(self) -> None:
        """Start a differential render frame."""
        self._frame_active = True
        self._frame_touched.clear()
        self._frame_touched_spans.clear()

    @staticmethod
    def _subtract_covered_ranges(
        start: int, end: int, covered_ranges: List[Tuple[int, int]]
    ) -> List[Tuple[int, int]]:
        """Return uncovered segments from [start, end) after removing covered ranges."""
        if start >= end:
            return []
        if not covered_ranges:
            return [(start, end)]

        normalized: List[Tuple[int, int]] = []
        for cov_start, cov_end in sorted(covered_ranges, key=lambda pair: pair[0]):
            if cov_end <= start or cov_start >= end:
                continue
            bounded_start = max(start, cov_start)
            bounded_end = min(end, cov_end)
            if bounded_start >= bounded_end:
                continue
            if not normalized or bounded_start > normalized[-1][1]:
                normalized.append((bounded_start, bounded_end))
            else:
                prev_start, prev_end = normalized[-1]
                normalized[-1] = (prev_start, max(prev_end, bounded_end))

        if not normalized:
            return [(start, end)]

        segments: List[Tuple[int, int]] = []
        cursor = start
        for cov_start, cov_end in normalized:
            if cursor < cov_start:
                segments.append((cursor, cov_start))
            cursor = max(cursor, cov_end)
        if cursor < end:
            segments.append((cursor, end))
        return segments

    def end_frame(self) -> None:
        """Finalize a differential frame and clear cells not rendered this frame."""
        if not self._frame_active:
            return

        stale_keys = [
            key
            for key in list(self._screen_state.keys())
            if key not in self._frame_touched
        ]
        clear_attr = self.get_theme_attr("normal")

        for y, x in stale_keys:
            cached = self._screen_state.pop((y, x), None)
            if cached is None:
                continue

            old_text, _old_attr = cached
            if not old_text:
                continue
            if y < 0 or x < 0 or y >= self.visible_height or x >= self.visible_width:
                continue

            old_start = x
            old_end = x + len(old_text)
            covered = self._frame_touched_spans.get(y, [])
            clear_segments = self._subtract_covered_ranges(old_start, old_end, covered)

            for clear_start, clear_end in clear_segments:
                if clear_start >= self.visible_width:
                    continue
                clipped_end = min(clear_end, self.visible_width)
                clear_len = clipped_end - clear_start
                if clear_len <= 0:
                    continue
                try:
                    self.pad.addstr(y, clear_start, " " * clear_len, clear_attr)
                except curses.error:
                    continue

        self._frame_active = False
        self._frame_touched.clear()
        self._frame_touched_spans.clear()

    def clear(self) -> None:
        """Clear the screen and reset screen state."""
        self.pad.clear()
        self._screen_state.clear()
        self._frame_touched.clear()
        self._frame_touched_spans.clear()
        self._frame_active = False
        self._force_redraw = True
        self.logger.debug("Screen cleared, forcing full redraw")

    def handle_resize(self) -> None:
        """Handle terminal resize event efficiently."""
        try:
            # Get current dimensions
            self.height, self.width = self.stdscr.getmaxyx()

            # Calculate new actual dimensions
            new_height = max(self.height, self.min_height)
            new_width = max(self.width, self.min_width)

            # Check if dimensions changed enough to require new pad
            if new_height > self.actual_height or new_width > self.actual_width:
                # Create new pad with new dimensions
                new_pad = curses.newpad(new_height, new_width)
                new_pad.keypad(True)

                # Update dimensions
                self.actual_height = new_height
                self.actual_width = new_width
                self.pad = new_pad

                # Force full redraw
                self._force_redraw = True
                self._screen_state.clear()

                self.logger.debug(
                    f"Terminal resized to {self.width}x{self.height}, created new pad"
                )

            # Update visible area
            self.visible_height = min(self.height, self.actual_height)
            self.visible_width = min(self.width, self.actual_width)

            # Invalidate layout cache to trigger recalculation
            self.layout.invalidate_cache()

            self.logger.debug(
                f"Visible area: {self.visible_width}x{self.visible_height}"
            )

        except curses.error as e:
            self.logger.error(f"Resize error: {str(e)}")

    def refresh(self) -> None:
        """Refresh the screen using double buffering with performance tracking."""
        start_time = time.time()
        try:
            # Update current dimensions
            self.height, self.width = self.stdscr.getmaxyx()

            # Update visible area
            self.visible_height = min(self.height, self.actual_height)
            self.visible_width = min(self.width, self.actual_width)

            if self.visible_height <= 0 or self.visible_width <= 0:
                self._perf_stats["last_render_time"] = time.time() - start_time
                return

            # Refresh the visible portion using double buffering
            self.pad.noutrefresh(
                0, 0, 0, 0, self.visible_height - 1, self.visible_width - 1
            )
            curses.doupdate()

            # Update performance stats
            if self._force_redraw:
                self._perf_stats["redraws"] += 1
            else:
                self._perf_stats["partial_updates"] += 1

            self._perf_stats["last_render_time"] = time.time() - start_time

            # Reset force redraw flag
            self._force_redraw = False

        except curses.error as e:
            self.logger.error(f"Refresh error: {str(e)}")

    def force_redraw(self) -> None:
        """Force a complete screen redraw on next refresh."""
        self._force_redraw = True
        self._screen_state.clear()

    def truncate_text(self, text: str, max_length: int, ellipsis: str = "...") -> str:
        """Efficiently truncate text to specified length with LRU caching."""
        # Check cache first
        cache_key = f"trunc_{text}_{max_length}"
        if cache_key in self._string_cache:
            self._perf_stats["cache_hits"] += 1
            # Mark as recently used
            self._string_cache.move_to_end(cache_key)
            return self._string_cache[cache_key]

        # Skip unnecessary processing
        if len(text) <= max_length:
            return text

        # Truncate with ellipsis
        result = text[: max_length - len(ellipsis)] + ellipsis

        # LRU eviction if at capacity
        if len(self._string_cache) >= self._cache_size:
            self._string_cache.popitem(last=False)  # Remove least recently used

        self._string_cache[cache_key] = result
        return result

    def center_text(self, text: str, width: Optional[int] = None) -> str:
        """Center text efficiently with caching."""
        # Generate cache key
        if width is None:
            width = self.width
        cache_key = f"center_{text}_{width}"

        # Check cache
        if cache_key in self._string_cache:
            self._perf_stats["cache_hits"] += 1
            return self._string_cache[cache_key]

        # Calculate centered text
        target_width = max(0, width)

        if not text or target_width == 0:
            return ""

        if len(text) > target_width:
            return self.truncate_text(text, target_width)

        padding = target_width - len(text)
        left_pad = padding // 2
        right_pad = padding - left_pad

        result = " " * left_pad + text + " " * right_pad

        # Cache result
        if len(self._string_cache) >= self._cache_size:
            # Clear some entries when cache is full
            self._string_cache.pop(next(iter(self._string_cache)), None)

        self._string_cache[cache_key] = result
        return result

    def get_performance_stats(self) -> Dict[str, Any]:
        """Get rendering performance statistics."""
        return self._perf_stats.copy()

    def clear_caches(self) -> None:
        """Clear all rendering caches."""
        self._string_cache.clear()
        self._bar_cache.clear()
        self.logger.debug("Display caches cleared")

    def reload_theme(self, theme_name: Optional[str] = None) -> bool:
        """Reload theme and clear all color caches.

        Args:
            theme_name: Theme to load, or None to re-detect

        Returns:
            bool: True if theme changed successfully
        """
        try:
            # Clear color manager caches
            self.color_manager.color_cache.clear()
            self.color_manager.pair_pool.clear()
            self.color_manager.next_pair_index = 1

            # Clear screen state to force full redraw
            self._screen_state.clear()
            self._force_redraw = True

            # Clear display caches
            self.clear_caches()

            # Update theme
            if theme_name and theme_name in self.themes:
                self.current_theme = theme_name
                self.logger.debug(f"Manually selected theme: {theme_name}")
            else:
                self.current_theme = self._detect_terminal_theme()
                self.logger.debug(f"Auto-detected theme: {self.current_theme}")

            # Reinitialize colors
            self.setup_colors()

            self.logger.info(f"Theme reloaded: {self.current_theme}")
            return True

        except Exception as e:
            self.logger.error(f"Theme reload error: {e}")
            return False

    def cleanup(self) -> None:
        """Clean up display resources and restore terminal state."""
        try:
            # Restore original cursor state
            if self.original_cursor is not None:
                try:
                    curses.curs_set(self.original_cursor)
                except curses.error:
                    pass

            # Clear pad and screen
            try:
                self.pad.clear()
                self.pad.refresh()
            except curses.error:
                pass

            # Reset terminal mode
            try:
                curses.reset_prog_mode()
            except curses.error:
                pass

            # Final screen refresh
            try:
                self.stdscr.clear()
                self.stdscr.refresh()
            except curses.error:
                pass

            self.logger.debug("Display cleanup completed")

        except Exception as e:
            self.logger.error(f"Cleanup error: {str(e)}")
