sql_cli/
app_state_container.rs

1use crate::api_client::QueryResponse;
2use crate::buffer::{AppMode, BufferAPI, BufferManager, SortOrder};
3use crate::debug_service::DebugLevel;
4use crate::help_widget::HelpWidget;
5use crate::history::CommandHistory;
6use crate::history_widget::HistoryWidget;
7use crate::search_modes_widget::SearchModesWidget;
8use crate::stats_widget::StatsWidget;
9// TODO: Add DebugWidget when it implements DebugInfoProvider
10// use crate::debug_widget::DebugWidget;
11use crate::widget_traits::DebugInfoProvider;
12use anyhow::{anyhow, Result};
13use arboard::Clipboard;
14use chrono::{DateTime, Local};
15use crossterm::event::{KeyCode, KeyEvent, KeyModifiers};
16use std::cell::RefCell;
17use std::collections::{HashMap, VecDeque};
18use std::fmt;
19use std::time::{Duration, Instant};
20use tracing::{info, trace};
21
22/// Platform type for key handling
23#[derive(Debug, Clone, PartialEq)]
24pub enum Platform {
25    Windows,
26    Linux,
27    MacOS,
28    Unknown,
29}
30
31impl Platform {
32    #[must_use]
33    pub fn detect() -> Self {
34        if cfg!(target_os = "windows") {
35            Platform::Windows
36        } else if cfg!(target_os = "linux") {
37            Platform::Linux
38        } else if cfg!(target_os = "macos") {
39            Platform::MacOS
40        } else {
41            Platform::Unknown
42        }
43    }
44}
45
46/// Represents a single key press with all metadata
47#[derive(Debug, Clone)]
48pub struct KeyPressEntry {
49    /// The raw key event from crossterm
50    pub raw_event: KeyEvent,
51    /// First timestamp when this key was pressed
52    pub first_timestamp: DateTime<Local>,
53    /// Last timestamp when this key was pressed (for repeats)
54    pub last_timestamp: DateTime<Local>,
55    /// Number of times this key was pressed consecutively
56    pub repeat_count: usize,
57    /// The platform where the key was pressed
58    pub platform: Platform,
59    /// The interpreted action (if any) from the key dispatcher
60    pub interpreted_action: Option<String>,
61    /// The mode the app was in when the key was pressed
62    pub app_mode: AppMode,
63    /// Formatted display string for the key
64    pub display_string: String,
65}
66
67impl KeyPressEntry {
68    #[must_use]
69    pub fn new(key: KeyEvent, mode: AppMode, action: Option<String>) -> Self {
70        let display_string = Self::format_key(&key);
71        let now = Local::now();
72        Self {
73            raw_event: key,
74            first_timestamp: now,
75            last_timestamp: now,
76            repeat_count: 1,
77            platform: Platform::detect(),
78            interpreted_action: action,
79            app_mode: mode,
80            display_string,
81        }
82    }
83
84    /// Check if this entry represents the same key press (for coalescing)
85    pub fn is_same_key(&self, key: &KeyEvent, mode: &AppMode) -> bool {
86        // Compare only the key code and modifiers, not the entire event
87        // (KeyEvent might have other fields that differ between presses)
88        // Also check that keys are pressed within 1 second of each other for coalescing
89        let time_window = chrono::Duration::seconds(1);
90        let now = Local::now();
91        let time_diff = now - self.last_timestamp;
92
93        let code_match = self.raw_event.code == key.code;
94        let modifier_match = self.raw_event.modifiers == key.modifiers;
95        let mode_match = self.app_mode == *mode;
96        let time_match = time_diff < time_window;
97
98        tracing::trace!(
99            "is_same_key: code_match={}, modifier_match={}, mode_match={}, time_match={} ({}ms < 1000ms)",
100            code_match, modifier_match, mode_match, time_match, time_diff.num_milliseconds()
101        );
102
103        code_match && modifier_match && mode_match && time_match
104    }
105
106    /// Add a repeat to this entry
107    pub fn add_repeat(&mut self) {
108        self.repeat_count += 1;
109        self.last_timestamp = Local::now();
110    }
111
112    /// Get display string with repeat count in vim-style notation
113    #[must_use]
114    pub fn display_with_count(&self) -> String {
115        if self.repeat_count > 1 {
116            // Use vim-style notation: 5j instead of j x5
117            // For special keys, keep the x notation for clarity
118            match self.display_string.as_str() {
119                // Single letter keys get vim notation
120                s if s.len() == 1 => format!("{}{}", self.repeat_count, s),
121                // Arrow keys get compact notation
122                "↑" | "↓" | "←" | "→" => {
123                    format!("{}{}", self.repeat_count, self.display_string)
124                }
125                // Multi-char keys keep x notation for clarity
126                _ => format!("{} x{}", self.display_string, self.repeat_count),
127            }
128        } else {
129            self.display_string.clone()
130        }
131    }
132
133    /// Format a key event for display
134    fn format_key(key: &KeyEvent) -> String {
135        let mut result = String::new();
136
137        // Add modifiers
138        if key.modifiers.contains(KeyModifiers::CONTROL) {
139            result.push_str("Ctrl+");
140        }
141        if key.modifiers.contains(KeyModifiers::ALT) {
142            result.push_str("Alt+");
143        }
144        if key.modifiers.contains(KeyModifiers::SHIFT) {
145            result.push_str("Shift+");
146        }
147
148        // Add key code
149        match key.code {
150            KeyCode::Char(c) => result.push(c),
151            KeyCode::Enter => result.push_str("Enter"),
152            KeyCode::Esc => result.push_str("Esc"),
153            KeyCode::Backspace => result.push_str("Backspace"),
154            KeyCode::Tab => result.push_str("Tab"),
155            KeyCode::Delete => result.push_str("Del"),
156            KeyCode::Insert => result.push_str("Ins"),
157            KeyCode::F(n) => result.push_str(&format!("F{n}")),
158            KeyCode::Left => result.push('←'),
159            KeyCode::Right => result.push('→'),
160            KeyCode::Up => result.push('↑'),
161            KeyCode::Down => result.push('↓'),
162            KeyCode::Home => result.push_str("Home"),
163            KeyCode::End => result.push_str("End"),
164            KeyCode::PageUp => result.push_str("PgUp"),
165            KeyCode::PageDown => result.push_str("PgDn"),
166            _ => result.push('?'),
167        }
168
169        result
170    }
171
172    /// Get a detailed debug string for this key press
173    #[must_use]
174    pub fn debug_string(&self) -> String {
175        let modifiers = if self.raw_event.modifiers.is_empty() {
176            String::new()
177        } else {
178            format!(" ({})", self.format_modifiers())
179        };
180
181        let action = self
182            .interpreted_action
183            .as_ref()
184            .map(|a| format!(" → {a}"))
185            .unwrap_or_default();
186
187        let repeat_info = if self.repeat_count > 1 {
188            format!(" x{}", self.repeat_count)
189        } else {
190            String::new()
191        };
192
193        format!(
194            "[{}] {}{}{} [{:?}]{}",
195            self.last_timestamp.format("%H:%M:%S.%3f"),
196            self.display_string,
197            repeat_info,
198            modifiers,
199            self.platform,
200            action
201        )
202    }
203
204    fn format_modifiers(&self) -> String {
205        let mut parts = Vec::new();
206        if self.raw_event.modifiers.contains(KeyModifiers::CONTROL) {
207            parts.push("Ctrl");
208        }
209        if self.raw_event.modifiers.contains(KeyModifiers::ALT) {
210            parts.push("Alt");
211        }
212        if self.raw_event.modifiers.contains(KeyModifiers::SHIFT) {
213            parts.push("Shift");
214        }
215        parts.join("+")
216    }
217}
218
219/// Manages key press history with a ring buffer and smart coalescing
220#[derive(Debug, Clone)]
221pub struct KeyPressHistory {
222    /// Ring buffer of key presses
223    entries: VecDeque<KeyPressEntry>,
224    /// Maximum number of entries to keep
225    max_size: usize,
226}
227
228impl KeyPressHistory {
229    #[must_use]
230    pub fn new(max_size: usize) -> Self {
231        Self {
232            entries: VecDeque::with_capacity(max_size),
233            max_size,
234        }
235    }
236
237    /// Check if a key is considered a navigation key
238    fn is_navigation_key(key: &KeyEvent) -> bool {
239        matches!(
240            key.code,
241            KeyCode::Up
242                | KeyCode::Down
243                | KeyCode::Left
244                | KeyCode::Right
245                | KeyCode::PageUp
246                | KeyCode::PageDown
247                | KeyCode::Home
248                | KeyCode::End
249        )
250    }
251
252    /// Add a new key press to the history with smart coalescing
253    pub fn add(&mut self, entry: KeyPressEntry) {
254        // Check if we can coalesce with the last entry
255        if let Some(last_entry) = self.entries.back_mut() {
256            // Debug logging to understand why coalescing might fail
257            let time_diff = Local::now() - last_entry.last_timestamp;
258            tracing::debug!(
259                "Key coalesce check: last_key={:?}/{:?}, new_key={:?}/{:?}, mode_match={}, time_diff={}ms",
260                last_entry.raw_event.code,
261                last_entry.raw_event.modifiers,
262                entry.raw_event.code,
263                entry.raw_event.modifiers,
264                last_entry.app_mode == entry.app_mode,
265                time_diff.num_milliseconds()
266            );
267
268            if last_entry.is_same_key(&entry.raw_event, &entry.app_mode) {
269                // Same key pressed again in same mode, just increment counter
270                tracing::debug!("Key coalesced! Count now: {}", last_entry.repeat_count + 1);
271                last_entry.add_repeat();
272                // Update the action in case it changed
273                if entry.interpreted_action != last_entry.interpreted_action {
274                    last_entry.interpreted_action = entry.interpreted_action;
275                }
276                return;
277            }
278            tracing::debug!("Key NOT coalesced - adding new entry");
279        }
280
281        // Not a repeat, need to add new entry
282        // But first check if buffer is full
283        if self.entries.len() >= self.max_size {
284            // Smart removal strategy:
285            // 1. First try to remove old navigation key entries with low repeat counts
286            // 2. Then remove any old entry with low repeat count
287            // 3. Finally just remove the oldest
288
289            let mut removed = false;
290
291            // Try to remove single-press navigation keys
292            for i in 0..self.entries.len() {
293                if Self::is_navigation_key(&self.entries[i].raw_event)
294                    && self.entries[i].repeat_count == 1
295                {
296                    self.entries.remove(i);
297                    removed = true;
298                    break;
299                }
300            }
301
302            // If no single navigation keys, remove any single-press entry from first half
303            if !removed {
304                let half = self.entries.len() / 2;
305                for i in 0..half {
306                    if self.entries[i].repeat_count == 1 {
307                        self.entries.remove(i);
308                        removed = true;
309                        break;
310                    }
311                }
312            }
313
314            // Last resort: remove oldest
315            if !removed {
316                self.entries.pop_front();
317            }
318        }
319
320        self.entries.push_back(entry);
321    }
322
323    /// Get all entries
324    #[must_use]
325    pub fn entries(&self) -> &VecDeque<KeyPressEntry> {
326        &self.entries
327    }
328
329    /// Clear the history
330    pub fn clear(&mut self) {
331        self.entries.clear();
332    }
333
334    /// Get formatted history for display
335    #[must_use]
336    pub fn format_history(&self) -> String {
337        let mut output = String::new();
338        output.push_str("========== KEY PRESS HISTORY ==========\n");
339        output.push_str(&format!(
340            "(Most recent at bottom, {} unique entries, max {})\n",
341            self.entries.len(),
342            self.max_size
343        ));
344
345        // Count total key presses including repeats
346        let total_presses: usize = self.entries.iter().map(|e| e.repeat_count).sum();
347        output.push_str(&format!(
348            "Total key presses (with repeats): {total_presses}\n"
349        ));
350
351        for entry in &self.entries {
352            output.push_str(&format!(
353                "[{}] {}",
354                entry.last_timestamp.format("%H:%M:%S.%3f"),
355                entry.display_with_count()
356            ));
357
358            if !entry.raw_event.modifiers.is_empty() {
359                output.push_str(&format!(" ({})", entry.format_modifiers()));
360            }
361
362            output.push('\n');
363        }
364
365        output.push_str("========================================\n");
366        output
367    }
368
369    /// Get detailed debug history with platform info and actions
370    #[must_use]
371    pub fn format_debug_history(&self) -> String {
372        let mut output = String::new();
373        output.push_str("========== DETAILED KEY HISTORY ==========\n");
374        output.push_str(&format!("Platform: {:?}\n", Platform::detect()));
375        output.push_str(&format!(
376            "(Most recent at bottom, last {} keys)\n",
377            self.max_size
378        ));
379
380        for entry in &self.entries {
381            output.push_str(&entry.debug_string());
382            output.push('\n');
383        }
384
385        output.push_str("==========================================\n");
386        output
387    }
388}
389
390/// Represents input state for command editing
391#[derive(Debug, Clone)]
392pub struct InputState {
393    pub text: String,
394    pub cursor_position: usize,
395    pub last_executed_query: String,
396}
397
398impl Default for InputState {
399    fn default() -> Self {
400        Self::new()
401    }
402}
403
404impl InputState {
405    #[must_use]
406    pub fn new() -> Self {
407        Self {
408            text: String::new(),
409            cursor_position: 0,
410            last_executed_query: String::new(),
411        }
412    }
413
414    pub fn clear(&mut self) {
415        let _old_text = self.text.clone();
416        self.text.clear();
417        self.cursor_position = 0;
418        // Note: This is on InputState, so we don't have access to debug_service here
419        // Logging will need to be done at the AppStateContainer level
420    }
421
422    pub fn set_text(&mut self, text: String) {
423        let _old_text = self.text.clone();
424        // TODO: Add logging when log crate is available
425        // info!(target: "state", "InputState::set_text() - '{}' -> '{}'", old_text, text);
426        self.cursor_position = text.len();
427        self.text = text;
428    }
429
430    pub fn set_text_with_cursor(&mut self, text: String, cursor: usize) {
431        let _old_text = self.text.clone();
432        let _old_cursor = self.cursor_position;
433        // TODO: Add logging when log crate is available
434        // info!(target: "state", "InputState::set_text_with_cursor() - text: '{}' -> '{}', cursor: {} -> {}",
435        //       old_text, text, old_cursor, cursor);
436        self.text = text;
437        self.cursor_position = cursor;
438    }
439}
440
441/// Search operation types for tracking
442#[derive(Debug, Clone)]
443pub enum SearchOperation {
444    StartSearch(String),
445    UpdatePattern(String, String), // old, new
446    MatchesFound(usize),
447    NavigateToMatch(usize),
448    ClearSearch,
449    NoMatchesFound,
450}
451
452/// Search history entry
453#[derive(Debug, Clone)]
454pub struct SearchHistoryEntry {
455    pub pattern: String,
456    pub match_count: usize,
457    pub timestamp: DateTime<Local>,
458    pub duration_ms: Option<u64>,
459}
460
461/// Search state for regular search
462#[derive(Debug, Clone)]
463pub struct SearchState {
464    pub pattern: String,
465    pub matches: Vec<(usize, usize, usize, usize)>, // (row_start, col_start, row_end, col_end)
466    pub current_match: usize,
467    pub is_active: bool,
468    pub history: VecDeque<SearchHistoryEntry>,
469    pub last_search_time: Option<std::time::Instant>,
470}
471
472impl Default for SearchState {
473    fn default() -> Self {
474        Self::new()
475    }
476}
477
478impl SearchState {
479    #[must_use]
480    pub fn new() -> Self {
481        Self {
482            pattern: String::new(),
483            matches: Vec::new(),
484            current_match: 0,
485            is_active: false,
486            history: VecDeque::with_capacity(20), // Keep last 20 searches
487            last_search_time: None,
488        }
489    }
490
491    pub fn clear(&mut self) {
492        // Save to history if we had an active search
493        if self.is_active && !self.pattern.is_empty() {
494            let duration_ms = self
495                .last_search_time
496                .map(|t| t.elapsed().as_millis() as u64);
497            let entry = SearchHistoryEntry {
498                pattern: self.pattern.clone(),
499                match_count: self.matches.len(),
500                timestamp: Local::now(),
501                duration_ms,
502            };
503
504            // Keep history size limited
505            if self.history.len() >= 20 {
506                self.history.pop_front();
507            }
508            self.history.push_back(entry);
509        }
510
511        self.pattern.clear();
512        self.matches.clear();
513        self.current_match = 0;
514        self.is_active = false;
515        self.last_search_time = None;
516    }
517}
518
519/// Filter state for filtering results
520#[derive(Debug, Clone)]
521pub struct FilterState {
522    pub pattern: String,
523    pub filtered_indices: Vec<usize>,
524    pub filtered_data: Option<Vec<Vec<String>>>,
525    pub is_active: bool,
526    pub case_insensitive: bool,
527    pub total_filters: usize,
528    pub last_filter_time: Option<Instant>,
529    pub history: VecDeque<FilterHistoryEntry>,
530    pub max_history: usize,
531}
532
533#[derive(Debug, Clone)]
534pub struct FilterHistoryEntry {
535    pub pattern: String,
536    pub match_count: usize,
537    pub timestamp: chrono::DateTime<chrono::Local>,
538    pub duration_ms: Option<u64>,
539}
540
541impl Default for FilterState {
542    fn default() -> Self {
543        Self::new()
544    }
545}
546
547impl FilterState {
548    #[must_use]
549    pub fn new() -> Self {
550        Self {
551            pattern: String::new(),
552            filtered_indices: Vec::new(),
553            filtered_data: None,
554            is_active: false,
555            case_insensitive: true,
556            total_filters: 0,
557            last_filter_time: None,
558            history: VecDeque::with_capacity(20),
559            max_history: 20,
560        }
561    }
562
563    pub fn clear(&mut self) {
564        info!(target: "filter", "FilterState::clear() - had {} filtered rows for pattern '{}'",
565              self.filtered_indices.len(), self.pattern);
566
567        // Add to history before clearing
568        if !self.pattern.is_empty() && self.is_active {
569            let duration_ms = self
570                .last_filter_time
571                .as_ref()
572                .map(|t| t.elapsed().as_millis() as u64);
573            let entry = FilterHistoryEntry {
574                pattern: self.pattern.clone(),
575                match_count: self.filtered_indices.len(),
576                timestamp: chrono::Local::now(),
577                duration_ms,
578            };
579            self.history.push_front(entry);
580            if self.history.len() > self.max_history {
581                self.history.pop_back();
582            }
583        }
584
585        self.pattern.clear();
586        self.filtered_indices.clear();
587        self.filtered_data = None;
588        self.is_active = false;
589        self.last_filter_time = None;
590    }
591
592    /// Set filter pattern and mark as active
593    pub fn set_pattern(&mut self, pattern: String) {
594        info!(target: "filter", "FilterState::set_pattern('{}') - was '{}'", pattern, self.pattern);
595        self.pattern = pattern;
596        if self.pattern.is_empty() {
597            self.is_active = false;
598        } else {
599            self.is_active = true;
600            self.total_filters += 1;
601            self.last_filter_time = Some(Instant::now());
602        }
603    }
604
605    /// Set filtered indices from filter operation
606    pub fn set_filtered_indices(&mut self, indices: Vec<usize>) {
607        info!(target: "filter", "FilterState::set_filtered_indices - {} rows match pattern '{}'", 
608              indices.len(), self.pattern);
609        self.filtered_indices = indices;
610    }
611
612    /// Set filtered data from filter operation
613    pub fn set_filtered_data(&mut self, data: Option<Vec<Vec<String>>>) {
614        let count = data.as_ref().map_or(0, std::vec::Vec::len);
615        info!(target: "filter", "FilterState::set_filtered_data - {} rows", count);
616        self.filtered_data = data;
617    }
618
619    /// Get filter statistics
620    #[must_use]
621    pub fn get_stats(&self) -> String {
622        format!(
623            "Total filters: {}, History items: {}, Current matches: {}",
624            self.total_filters,
625            self.history.len(),
626            self.filtered_indices.len()
627        )
628    }
629}
630
631/// Column search state management
632#[derive(Debug, Clone)]
633pub struct ColumnSearchState {
634    /// Current search pattern
635    pub pattern: String,
636
637    /// Matching columns (index, `column_name`)
638    pub matching_columns: Vec<(usize, String)>,
639
640    /// Current match index (index into `matching_columns`)
641    pub current_match: usize,
642
643    /// Whether column search is active
644    pub is_active: bool,
645
646    /// Search history
647    pub history: VecDeque<ColumnSearchHistoryEntry>,
648
649    /// Total searches performed
650    pub total_searches: usize,
651
652    /// Last search time
653    pub last_search_time: Option<Instant>,
654}
655
656#[derive(Debug, Clone)]
657pub struct ColumnSearchHistoryEntry {
658    /// Search pattern
659    pub pattern: String,
660
661    /// Number of matching columns
662    pub match_count: usize,
663
664    /// Column names that matched
665    pub matched_columns: Vec<String>,
666
667    /// When this search was performed
668    pub timestamp: DateTime<Local>,
669
670    /// How long the search took
671    pub duration_ms: Option<u64>,
672}
673
674/// State for tab completion functionality
675#[derive(Clone, Debug)]
676pub struct CompletionState {
677    pub suggestions: Vec<String>,
678    pub current_index: usize,
679    pub last_query: String,
680    pub last_cursor_pos: usize,
681    pub is_active: bool,
682    // Statistics
683    pub total_completions: usize,
684    pub last_completion_time: Option<std::time::Instant>,
685}
686
687impl Default for CompletionState {
688    fn default() -> Self {
689        Self::new()
690    }
691}
692
693impl CompletionState {
694    #[must_use]
695    pub fn new() -> Self {
696        Self {
697            suggestions: Vec::new(),
698            current_index: 0,
699            last_query: String::new(),
700            last_cursor_pos: 0,
701            is_active: false,
702            total_completions: 0,
703            last_completion_time: None,
704        }
705    }
706
707    /// Clear the completion state
708    pub fn clear(&mut self) {
709        self.suggestions.clear();
710        self.current_index = 0;
711        self.is_active = false;
712        // Keep last_query and last_cursor_pos for context
713    }
714
715    /// Set new suggestions
716    pub fn set_suggestions(&mut self, suggestions: Vec<String>) {
717        self.is_active = !suggestions.is_empty();
718        self.suggestions = suggestions;
719        self.current_index = 0;
720        if self.is_active {
721            self.last_completion_time = Some(std::time::Instant::now());
722            self.total_completions += 1;
723        }
724    }
725
726    /// Cycle to next suggestion
727    pub fn next_suggestion(&mut self) {
728        if !self.suggestions.is_empty() {
729            self.current_index = (self.current_index + 1) % self.suggestions.len();
730        }
731    }
732
733    /// Get current suggestion
734    #[must_use]
735    pub fn current_suggestion(&self) -> Option<&String> {
736        if self.is_active && !self.suggestions.is_empty() {
737            self.suggestions.get(self.current_index)
738        } else {
739            None
740        }
741    }
742
743    /// Check if we're in the same completion context
744    #[must_use]
745    pub fn is_same_context(&self, query: &str, cursor_pos: usize) -> bool {
746        query == self.last_query && cursor_pos == self.last_cursor_pos
747    }
748
749    /// Update context for next completion
750    pub fn update_context(&mut self, query: String, cursor_pos: usize) {
751        self.last_query = query;
752        self.last_cursor_pos = cursor_pos;
753    }
754}
755
756impl Default for ColumnSearchState {
757    fn default() -> Self {
758        Self::new()
759    }
760}
761
762impl ColumnSearchState {
763    #[must_use]
764    pub fn new() -> Self {
765        Self {
766            pattern: String::new(),
767            matching_columns: Vec::new(),
768            current_match: 0,
769            is_active: false,
770            history: VecDeque::with_capacity(20),
771            total_searches: 0,
772            last_search_time: None,
773        }
774    }
775
776    /// Clear the column search state
777    pub fn clear(&mut self) {
778        // Save to history if we had an active search
779        if self.is_active && !self.pattern.is_empty() {
780            let duration_ms = self
781                .last_search_time
782                .map(|t| t.elapsed().as_millis() as u64);
783            let entry = ColumnSearchHistoryEntry {
784                pattern: self.pattern.clone(),
785                match_count: self.matching_columns.len(),
786                matched_columns: self
787                    .matching_columns
788                    .iter()
789                    .map(|(_, name)| name.clone())
790                    .collect(),
791                timestamp: Local::now(),
792                duration_ms,
793            };
794            self.history.push_front(entry);
795
796            // Trim history
797            while self.history.len() > 20 {
798                self.history.pop_back();
799            }
800        }
801
802        self.pattern.clear();
803        self.matching_columns.clear();
804        self.current_match = 0;
805        self.is_active = false;
806        self.last_search_time = None;
807    }
808
809    /// Set search results
810    pub fn set_matches(&mut self, matches: Vec<(usize, String)>) {
811        self.matching_columns = matches;
812        self.current_match = 0;
813        self.total_searches += 1;
814        self.last_search_time = Some(Instant::now());
815    }
816
817    /// Navigate to next match
818    pub fn next_match(&mut self) -> Option<(usize, String)> {
819        if self.matching_columns.is_empty() {
820            return None;
821        }
822
823        self.current_match = (self.current_match + 1) % self.matching_columns.len();
824        Some(self.matching_columns[self.current_match].clone())
825    }
826
827    /// Navigate to previous match
828    pub fn prev_match(&mut self) -> Option<(usize, String)> {
829        if self.matching_columns.is_empty() {
830            return None;
831        }
832
833        self.current_match = if self.current_match == 0 {
834            self.matching_columns.len() - 1
835        } else {
836            self.current_match - 1
837        };
838        Some(self.matching_columns[self.current_match].clone())
839    }
840
841    /// Get current match
842    #[must_use]
843    pub fn current_match(&self) -> Option<(usize, String)> {
844        if self.matching_columns.is_empty() {
845            None
846        } else {
847            Some(self.matching_columns[self.current_match].clone())
848        }
849    }
850
851    /// Get search statistics
852    #[must_use]
853    pub fn get_stats(&self) -> String {
854        format!(
855            "Total searches: {}, History items: {}, Current matches: {}",
856            self.total_searches,
857            self.history.len(),
858            self.matching_columns.len()
859        )
860    }
861}
862
863/// Cache list state
864#[derive(Debug, Clone)]
865pub struct CacheListState {
866    pub selected_index: usize,
867    pub cache_names: Vec<String>,
868}
869
870impl Default for CacheListState {
871    fn default() -> Self {
872        Self::new()
873    }
874}
875
876impl CacheListState {
877    #[must_use]
878    pub fn new() -> Self {
879        Self {
880            selected_index: 0,
881            cache_names: Vec::new(),
882        }
883    }
884}
885
886/// Column stats state
887#[derive(Debug, Clone)]
888pub struct ColumnStatsState {
889    pub column_index: usize,
890    pub is_visible: bool,
891}
892
893impl Default for ColumnStatsState {
894    fn default() -> Self {
895        Self::new()
896    }
897}
898
899impl ColumnStatsState {
900    #[must_use]
901    pub fn new() -> Self {
902        Self {
903            column_index: 0,
904            is_visible: false,
905        }
906    }
907}
908
909/// Jump to row state
910#[derive(Debug, Clone)]
911pub struct JumpToRowState {
912    pub input: String,
913    pub is_active: bool,
914}
915
916/// Navigation and viewport state
917#[derive(Debug, Clone)]
918pub struct NavigationState {
919    pub selected_row: usize,
920    pub selected_column: usize,
921    pub scroll_offset: (usize, usize), // (row, col)
922    pub viewport_rows: usize,
923    pub viewport_columns: usize,
924    pub total_rows: usize,
925    pub total_columns: usize,
926    pub last_visible_rows: usize,
927    pub viewport_lock: bool, // Lock viewport position (cursor moves within)
928    pub viewport_lock_row: Option<usize>,
929    pub cursor_lock: bool, // Lock cursor at visual position (data scrolls)
930    pub cursor_lock_position: Option<usize>, // Visual position to lock cursor at
931    pub selection_history: VecDeque<(usize, usize)>, // Track navigation history
932}
933
934impl Default for NavigationState {
935    fn default() -> Self {
936        Self::new()
937    }
938}
939
940impl NavigationState {
941    #[must_use]
942    pub fn new() -> Self {
943        Self {
944            selected_row: 0,
945            selected_column: 0,
946            scroll_offset: (0, 0),
947            viewport_rows: 30,
948            viewport_columns: 10,
949            total_rows: 0,
950            total_columns: 0,
951            last_visible_rows: 0,
952            viewport_lock: false,
953            viewport_lock_row: None,
954            cursor_lock: false,
955            cursor_lock_position: None,
956            selection_history: VecDeque::with_capacity(50), // Keep last 50 positions
957        }
958    }
959
960    /// Reset navigation state to initial values (used when executing new queries)
961    pub fn reset(&mut self) {
962        self.selected_row = 0;
963        self.selected_column = 0;
964        self.scroll_offset = (0, 0);
965        // Keep viewport dimensions as they are (terminal size dependent)
966        // Reset totals will be updated when new data arrives
967        self.total_rows = 0;
968        self.total_columns = 0;
969        self.last_visible_rows = 0;
970        self.viewport_lock = false;
971        self.viewport_lock_row = None;
972        self.cursor_lock = false;
973        self.cursor_lock_position = None;
974        self.selection_history.clear();
975    }
976
977    pub fn update_totals(&mut self, rows: usize, columns: usize) {
978        info!(target: "navigation", "NavigationState::update_totals - rows: {} -> {}, columns: {} -> {}", 
979              self.total_rows, rows, self.total_columns, columns);
980
981        self.total_rows = rows;
982        self.total_columns = columns;
983
984        // Adjust selected position if it's out of bounds
985        if self.selected_row >= rows && rows > 0 {
986            let old_row = self.selected_row;
987            self.selected_row = rows - 1;
988            info!(target: "navigation", "Adjusted selected_row from {} to {} (out of bounds)", old_row, self.selected_row);
989        }
990        if self.selected_column >= columns && columns > 0 {
991            let old_col = self.selected_column;
992            self.selected_column = columns - 1;
993            info!(target: "navigation", "Adjusted selected_column from {} to {} (out of bounds)", old_col, self.selected_column);
994        }
995    }
996
997    pub fn set_viewport_size(&mut self, rows: usize, columns: usize) {
998        info!(target: "navigation", "NavigationState::set_viewport_size - rows: {} -> {}, columns: {} -> {}", 
999              self.viewport_rows, rows, self.viewport_columns, columns);
1000        self.viewport_rows = rows;
1001        self.viewport_columns = columns;
1002    }
1003
1004    /// Move to next row
1005    pub fn next_row(&mut self) -> bool {
1006        if self.cursor_lock {
1007            // In cursor lock mode, scroll the data instead of moving cursor
1008            if let Some(lock_position) = self.cursor_lock_position {
1009                // Check if we can scroll down
1010                let max_scroll = self.total_rows.saturating_sub(self.viewport_rows);
1011                if self.scroll_offset.0 < max_scroll {
1012                    self.scroll_offset.0 += 1;
1013                    // Keep cursor at the locked visual position
1014                    let new_data_row = self.scroll_offset.0 + lock_position;
1015                    if new_data_row < self.total_rows {
1016                        self.selected_row = new_data_row;
1017                        self.add_to_history(self.selected_row, self.selected_column);
1018                        info!(target: "navigation", "NavigationState::next_row (cursor locked) - scrolled to offset {}, cursor at row {}", 
1019                              self.scroll_offset.0, self.selected_row);
1020                        return true;
1021                    }
1022                }
1023                return false;
1024            }
1025        }
1026
1027        // Check viewport lock boundaries
1028        if self.viewport_lock {
1029            // In viewport lock mode, don't allow cursor to leave visible area
1030            let viewport_bottom = self.scroll_offset.0 + self.viewport_rows - 1;
1031            if self.selected_row >= viewport_bottom {
1032                info!(target: "navigation", "NavigationState::next_row - at viewport bottom (row {}), viewport locked", self.selected_row);
1033                return false; // Already at bottom of viewport
1034            }
1035        }
1036
1037        // Normal navigation (with viewport lock boundary check)
1038        if self.selected_row < self.total_rows.saturating_sub(1) {
1039            self.selected_row += 1;
1040            self.add_to_history(self.selected_row, self.selected_column);
1041            self.ensure_visible(self.selected_row, self.selected_column);
1042            info!(target: "navigation", "NavigationState::next_row - moved to row {}", self.selected_row);
1043            true
1044        } else {
1045            false
1046        }
1047    }
1048
1049    /// Move to previous row
1050    pub fn previous_row(&mut self) -> bool {
1051        if self.cursor_lock {
1052            // In cursor lock mode, scroll the data instead of moving cursor
1053            if let Some(lock_position) = self.cursor_lock_position {
1054                // Check if we can scroll up
1055                if self.scroll_offset.0 > 0 {
1056                    self.scroll_offset.0 -= 1;
1057                    // Keep cursor at the locked visual position
1058                    let new_data_row = self.scroll_offset.0 + lock_position;
1059                    self.selected_row = new_data_row;
1060                    self.add_to_history(self.selected_row, self.selected_column);
1061                    info!(target: "navigation", "NavigationState::previous_row (cursor locked) - scrolled to offset {}, cursor at row {}", 
1062                          self.scroll_offset.0, self.selected_row);
1063                    return true;
1064                }
1065                return false;
1066            }
1067        }
1068
1069        // Check viewport lock boundaries
1070        if self.viewport_lock {
1071            // In viewport lock mode, don't allow cursor to leave visible area
1072            let viewport_top = self.scroll_offset.0;
1073            if self.selected_row <= viewport_top {
1074                info!(target: "navigation", "NavigationState::previous_row - at viewport top (row {}), viewport locked", self.selected_row);
1075                return false; // Already at top of viewport
1076            }
1077        }
1078
1079        // Normal navigation (with viewport lock boundary check)
1080        if self.selected_row > 0 {
1081            self.selected_row -= 1;
1082            self.add_to_history(self.selected_row, self.selected_column);
1083            self.ensure_visible(self.selected_row, self.selected_column);
1084            info!(target: "navigation", "NavigationState::previous_row - moved to row {}", self.selected_row);
1085            true
1086        } else {
1087            false
1088        }
1089    }
1090
1091    /// Move to next column
1092    pub fn next_column(&mut self) -> bool {
1093        if self.selected_column < self.total_columns.saturating_sub(1) {
1094            self.selected_column += 1;
1095            self.add_to_history(self.selected_row, self.selected_column);
1096            self.ensure_visible(self.selected_row, self.selected_column);
1097            info!(target: "navigation", "NavigationState::next_column - moved to column {}", self.selected_column);
1098            true
1099        } else {
1100            false
1101        }
1102    }
1103
1104    /// Move to previous column
1105    pub fn previous_column(&mut self) -> bool {
1106        if self.selected_column > 0 {
1107            self.selected_column -= 1;
1108            self.add_to_history(self.selected_row, self.selected_column);
1109            self.ensure_visible(self.selected_row, self.selected_column);
1110            info!(target: "navigation", "NavigationState::previous_column - moved to column {}", self.selected_column);
1111            true
1112        } else {
1113            false
1114        }
1115    }
1116
1117    /// Jump to specific row
1118    pub fn jump_to_row(&mut self, row: usize) {
1119        let target_row = row.min(self.total_rows.saturating_sub(1));
1120        info!(target: "navigation", "NavigationState::jump_to_row - from {} to {}", self.selected_row, target_row);
1121        self.selected_row = target_row;
1122        self.add_to_history(self.selected_row, self.selected_column);
1123        self.ensure_visible(self.selected_row, self.selected_column);
1124    }
1125
1126    /// Jump to first row
1127    pub fn jump_to_first_row(&mut self) {
1128        info!(target: "navigation", "NavigationState::jump_to_first_row - from row {}", self.selected_row);
1129        self.selected_row = 0;
1130        self.add_to_history(self.selected_row, self.selected_column);
1131        self.ensure_visible(self.selected_row, self.selected_column);
1132    }
1133
1134    /// Jump to last row
1135    pub fn jump_to_last_row(&mut self) {
1136        let last_row = self.total_rows.saturating_sub(1);
1137        info!(target: "navigation", "NavigationState::jump_to_last_row - from {} to {}", self.selected_row, last_row);
1138        self.selected_row = last_row;
1139        self.add_to_history(self.selected_row, self.selected_column);
1140        self.ensure_visible(self.selected_row, self.selected_column);
1141    }
1142
1143    /// Set selected position
1144    pub fn set_position(&mut self, row: usize, column: usize) {
1145        info!(target: "navigation", "NavigationState::set_position - ({}, {}) -> ({}, {})", 
1146              self.selected_row, self.selected_column, row, column);
1147        self.selected_row = row.min(self.total_rows.saturating_sub(1));
1148        self.selected_column = column.min(self.total_columns.saturating_sub(1));
1149        self.add_to_history(self.selected_row, self.selected_column);
1150        self.ensure_visible(self.selected_row, self.selected_column);
1151    }
1152
1153    /// Page down
1154    pub fn page_down(&mut self) {
1155        if self.cursor_lock {
1156            // In cursor lock mode, scroll the data by a page
1157            if let Some(lock_position) = self.cursor_lock_position {
1158                let max_scroll = self.total_rows.saturating_sub(self.viewport_rows);
1159                let new_scroll = (self.scroll_offset.0 + self.viewport_rows).min(max_scroll);
1160                if new_scroll != self.scroll_offset.0 {
1161                    self.scroll_offset.0 = new_scroll;
1162                    // Keep cursor at the locked visual position
1163                    let new_data_row = self.scroll_offset.0 + lock_position;
1164                    if new_data_row < self.total_rows {
1165                        self.selected_row = new_data_row;
1166                        self.add_to_history(self.selected_row, self.selected_column);
1167                        info!(target: "navigation", "NavigationState::page_down (cursor locked) - scrolled to offset {}, cursor at row {}", 
1168                              self.scroll_offset.0, self.selected_row);
1169                    }
1170                }
1171                return;
1172            }
1173        }
1174
1175        // Normal page down when not locked
1176        let old_row = self.selected_row;
1177        self.selected_row =
1178            (self.selected_row + self.viewport_rows).min(self.total_rows.saturating_sub(1));
1179        if self.selected_row != old_row {
1180            info!(target: "navigation", "NavigationState::page_down - from {} to {}", old_row, self.selected_row);
1181            self.add_to_history(self.selected_row, self.selected_column);
1182            self.ensure_visible(self.selected_row, self.selected_column);
1183        }
1184    }
1185
1186    /// Page up
1187    pub fn page_up(&mut self) {
1188        if self.cursor_lock {
1189            // In cursor lock mode, scroll the data by a page
1190            if let Some(lock_position) = self.cursor_lock_position {
1191                let new_scroll = self.scroll_offset.0.saturating_sub(self.viewport_rows);
1192                if new_scroll != self.scroll_offset.0 {
1193                    self.scroll_offset.0 = new_scroll;
1194                    // Keep cursor at the locked visual position
1195                    let new_data_row = self.scroll_offset.0 + lock_position;
1196                    self.selected_row = new_data_row;
1197                    self.add_to_history(self.selected_row, self.selected_column);
1198                    info!(target: "navigation", "NavigationState::page_up (cursor locked) - scrolled to offset {}, cursor at row {}", 
1199                          self.scroll_offset.0, self.selected_row);
1200                }
1201                return;
1202            }
1203        }
1204
1205        // Normal page up when not locked
1206        let old_row = self.selected_row;
1207        self.selected_row = self.selected_row.saturating_sub(self.viewport_rows);
1208        if self.selected_row != old_row {
1209            info!(target: "navigation", "NavigationState::page_up - from {} to {}", old_row, self.selected_row);
1210            self.add_to_history(self.selected_row, self.selected_column);
1211            self.ensure_visible(self.selected_row, self.selected_column);
1212        }
1213    }
1214
1215    /// Jump to top of viewport (H in vim)
1216    pub fn jump_to_viewport_top(&mut self) {
1217        let target_row = self.scroll_offset.0;
1218        if target_row != self.selected_row && target_row < self.total_rows {
1219            info!(target: "navigation", "NavigationState::jump_to_viewport_top - from {} to {} (viewport top)", 
1220                  self.selected_row, target_row);
1221            self.selected_row = target_row;
1222            self.add_to_history(self.selected_row, self.selected_column);
1223            // No need to ensure_visible since we're jumping to a visible position
1224        }
1225    }
1226
1227    /// Jump to middle of viewport (M in vim)
1228    pub fn jump_to_viewport_middle(&mut self) {
1229        let viewport_start = self.scroll_offset.0;
1230        let viewport_end = (viewport_start + self.viewport_rows).min(self.total_rows);
1231        let target_row = viewport_start + (viewport_end - viewport_start) / 2;
1232
1233        if target_row != self.selected_row && target_row < self.total_rows {
1234            info!(target: "navigation", "NavigationState::jump_to_viewport_middle - from {} to {} (viewport middle)", 
1235                  self.selected_row, target_row);
1236            self.selected_row = target_row;
1237            self.add_to_history(self.selected_row, self.selected_column);
1238            // No need to ensure_visible since we're jumping to a visible position
1239        }
1240    }
1241
1242    /// Jump to bottom of viewport (L in vim)
1243    pub fn jump_to_viewport_bottom(&mut self) {
1244        let viewport_start = self.scroll_offset.0;
1245        let viewport_end = (viewport_start + self.viewport_rows).min(self.total_rows);
1246        let target_row = viewport_end.saturating_sub(1);
1247
1248        if target_row != self.selected_row && target_row < self.total_rows {
1249            info!(target: "navigation", "NavigationState::jump_to_viewport_bottom - from {} to {} (viewport bottom)", 
1250                  self.selected_row, target_row);
1251            self.selected_row = target_row;
1252            self.add_to_history(self.selected_row, self.selected_column);
1253            // No need to ensure_visible since we're jumping to a visible position
1254        }
1255    }
1256
1257    #[must_use]
1258    pub fn is_position_visible(&self, row: usize, col: usize) -> bool {
1259        let (scroll_row, scroll_col) = self.scroll_offset;
1260        row >= scroll_row
1261            && row < scroll_row + self.viewport_rows
1262            && col >= scroll_col
1263            && col < scroll_col + self.viewport_columns
1264    }
1265
1266    pub fn ensure_visible(&mut self, row: usize, col: usize) {
1267        // If viewport is locked, don't adjust scroll offset
1268        if self.viewport_lock {
1269            info!(target: "navigation", "NavigationState::ensure_visible - viewport locked, not adjusting scroll");
1270            return;
1271        }
1272
1273        let (mut scroll_row, mut scroll_col) = self.scroll_offset;
1274
1275        // Adjust row scrolling
1276        if row < scroll_row {
1277            scroll_row = row;
1278        } else if row >= scroll_row + self.viewport_rows {
1279            scroll_row = row.saturating_sub(self.viewport_rows - 1);
1280        }
1281
1282        // Adjust column scrolling
1283        if col < scroll_col {
1284            scroll_col = col;
1285        } else if col >= scroll_col + self.viewport_columns {
1286            scroll_col = col.saturating_sub(self.viewport_columns - 1);
1287        }
1288
1289        if self.scroll_offset != (scroll_row, scroll_col) {
1290            info!(target: "navigation", "NavigationState::ensure_visible - scroll_offset: {:?} -> {:?}", 
1291                  self.scroll_offset, (scroll_row, scroll_col));
1292            self.scroll_offset = (scroll_row, scroll_col);
1293        }
1294    }
1295
1296    /// Check if cursor is at top of viewport
1297    #[must_use]
1298    pub fn is_at_viewport_top(&self) -> bool {
1299        self.selected_row == self.scroll_offset.0
1300    }
1301
1302    /// Check if cursor is at bottom of viewport
1303    #[must_use]
1304    pub fn is_at_viewport_bottom(&self) -> bool {
1305        self.selected_row == self.scroll_offset.0 + self.viewport_rows - 1
1306    }
1307
1308    /// Get position description for status
1309    #[must_use]
1310    pub fn get_position_status(&self) -> String {
1311        if self.viewport_lock {
1312            if self.is_at_viewport_top() {
1313                " (at viewport top)".to_string()
1314            } else if self.is_at_viewport_bottom() {
1315                " (at viewport bottom)".to_string()
1316            } else {
1317                String::new()
1318            }
1319        } else {
1320            String::new()
1321        }
1322    }
1323
1324    pub fn add_to_history(&mut self, row: usize, col: usize) {
1325        // Don't add if it's the same as the last position
1326        if let Some(&(last_row, last_col)) = self.selection_history.back() {
1327            if last_row == row && last_col == col {
1328                return;
1329            }
1330        }
1331
1332        if self.selection_history.len() >= 50 {
1333            self.selection_history.pop_front();
1334        }
1335        self.selection_history.push_back((row, col));
1336    }
1337}
1338
1339impl Default for JumpToRowState {
1340    fn default() -> Self {
1341        Self::new()
1342    }
1343}
1344
1345impl JumpToRowState {
1346    #[must_use]
1347    pub fn new() -> Self {
1348        Self {
1349            input: String::new(),
1350            is_active: false,
1351        }
1352    }
1353}
1354
1355/// State for column sorting
1356#[derive(Debug, Clone)]
1357pub struct SortState {
1358    /// Currently sorted column index
1359    pub column: Option<usize>,
1360    /// Column name (for display)
1361    pub column_name: Option<String>,
1362    /// Sort order (Ascending, Descending, None)
1363    pub order: SortOrder,
1364    /// History of sort operations
1365    pub history: VecDeque<SortHistoryEntry>,
1366    /// Maximum history size
1367    pub max_history: usize,
1368    /// Total sorts performed
1369    pub total_sorts: usize,
1370    /// Last sort time
1371    pub last_sort_time: Option<Instant>,
1372}
1373
1374#[derive(Debug, Clone)]
1375pub struct SortHistoryEntry {
1376    /// Column that was sorted
1377    pub column_index: usize,
1378    /// Column name
1379    pub column_name: String,
1380    /// Sort order applied
1381    pub order: SortOrder,
1382    /// When the sort was performed
1383    pub sorted_at: Instant,
1384    /// Number of rows sorted
1385    pub row_count: usize,
1386}
1387
1388impl Default for SortState {
1389    fn default() -> Self {
1390        Self::new()
1391    }
1392}
1393
1394impl SortState {
1395    #[must_use]
1396    pub fn new() -> Self {
1397        Self {
1398            column: None,
1399            column_name: None,
1400            order: SortOrder::None,
1401            history: VecDeque::with_capacity(20),
1402            max_history: 20,
1403            total_sorts: 0,
1404            last_sort_time: None,
1405        }
1406    }
1407
1408    /// Set sort column and order
1409    pub fn set_sort(
1410        &mut self,
1411        column_index: usize,
1412        column_name: String,
1413        order: SortOrder,
1414        row_count: usize,
1415    ) {
1416        // Add to history
1417        if self.history.len() >= self.max_history {
1418            self.history.pop_front();
1419        }
1420
1421        self.history.push_back(SortHistoryEntry {
1422            column_index,
1423            column_name: column_name.clone(),
1424            order,
1425            sorted_at: Instant::now(),
1426            row_count,
1427        });
1428
1429        // Update current state
1430        self.column = Some(column_index);
1431        self.column_name = Some(column_name);
1432        self.order = order;
1433        self.total_sorts += 1;
1434        self.last_sort_time = Some(Instant::now());
1435    }
1436
1437    /// Clear sort (return to original order)
1438    pub fn clear_sort(&mut self) {
1439        self.column = None;
1440        self.column_name = None;
1441        self.order = SortOrder::None;
1442        self.last_sort_time = Some(Instant::now());
1443    }
1444
1445    /// Get the next sort order for a column
1446    #[must_use]
1447    pub fn get_next_order(&self, column_index: usize) -> SortOrder {
1448        // Debug: GET_NEXT_ORDER calculation
1449        if let Some(current_col) = self.column {
1450            if current_col == column_index {
1451                // Same column - cycle through orders
1452                match self.order {
1453                    SortOrder::None => SortOrder::Ascending,
1454                    SortOrder::Ascending => SortOrder::Descending,
1455                    SortOrder::Descending => SortOrder::None,
1456                }
1457            } else {
1458                // Different column - start with ascending
1459                SortOrder::Ascending
1460            }
1461        } else {
1462            // No column sorted - start with ascending
1463            SortOrder::Ascending
1464        }
1465    }
1466
1467    /// Advance the sort state for the given column
1468    pub fn advance_sort_state(
1469        &mut self,
1470        column_index: usize,
1471        column_name: Option<String>,
1472        new_order: SortOrder,
1473    ) {
1474        // Update history before changing state
1475        if let (Some(col), Some(name)) = (self.column, &self.column_name) {
1476            self.history.push_back(SortHistoryEntry {
1477                column_index: col,
1478                column_name: name.clone(),
1479                order: self.order,
1480                sorted_at: std::time::Instant::now(),
1481                row_count: 0, // We don't track row count here, could be added later
1482            });
1483        }
1484
1485        // Update statistics
1486        self.total_sorts += 1;
1487
1488        // Update current state
1489        if new_order == SortOrder::None {
1490            self.column = None;
1491            self.column_name = None;
1492        } else {
1493            self.column = Some(column_index);
1494            self.column_name = column_name;
1495        }
1496        self.order = new_order;
1497        self.last_sort_time = Some(std::time::Instant::now());
1498    }
1499
1500    /// Get sort statistics
1501    #[must_use]
1502    pub fn get_stats(&self) -> String {
1503        let current = if let (Some(col), Some(name)) = (self.column, &self.column_name) {
1504            format!(
1505                "Column {} ({}) {}",
1506                col,
1507                name,
1508                match self.order {
1509                    SortOrder::Ascending => "↑",
1510                    SortOrder::Descending => "↓",
1511                    SortOrder::None => "-",
1512                }
1513            )
1514        } else {
1515            "None".to_string()
1516        };
1517
1518        format!(
1519            "Current: {}, Total sorts: {}, History items: {}",
1520            current,
1521            self.total_sorts,
1522            self.history.len()
1523        )
1524    }
1525}
1526
1527/// Selection mode for results view
1528#[derive(Debug, Clone, PartialEq)]
1529pub enum SelectionMode {
1530    Row,
1531    Cell,
1532    Column,
1533}
1534
1535/// Selection state for managing row/cell/column selections
1536#[derive(Debug, Clone)]
1537pub struct SelectionState {
1538    /// Current selection mode
1539    pub mode: SelectionMode,
1540    /// Currently selected row (for table navigation)
1541    pub selected_row: Option<usize>,
1542    /// Currently selected column (always tracked)
1543    pub selected_column: usize,
1544    /// Selected cells for multi-cell operations
1545    pub selected_cells: Vec<(usize, usize)>,
1546    /// Selection anchor for range selections
1547    pub selection_anchor: Option<(usize, usize)>,
1548    /// Selection history for undo
1549    pub history: VecDeque<SelectionHistoryEntry>,
1550    /// Maximum history size
1551    pub max_history: usize,
1552    /// Total selections made
1553    pub total_selections: usize,
1554    /// Last selection time
1555    pub last_selection_time: Option<Instant>,
1556}
1557
1558#[derive(Debug, Clone)]
1559pub struct SelectionHistoryEntry {
1560    pub mode: SelectionMode,
1561    pub row: Option<usize>,
1562    pub column: usize,
1563    pub cells: Vec<(usize, usize)>,
1564    pub timestamp: chrono::DateTime<chrono::Local>,
1565}
1566
1567impl Default for SelectionState {
1568    fn default() -> Self {
1569        Self::new()
1570    }
1571}
1572
1573impl SelectionState {
1574    #[must_use]
1575    pub fn new() -> Self {
1576        Self {
1577            mode: SelectionMode::Row,
1578            selected_row: None,
1579            selected_column: 0,
1580            selected_cells: Vec::new(),
1581            selection_anchor: None,
1582            history: VecDeque::new(),
1583            max_history: 50,
1584            total_selections: 0,
1585            last_selection_time: None,
1586        }
1587    }
1588
1589    /// Set selection mode
1590    pub fn set_mode(&mut self, mode: SelectionMode) {
1591        if self.mode != mode {
1592            // Save to history before changing
1593            self.save_to_history();
1594            self.mode = mode;
1595            // Clear multi-cell selections when changing modes
1596            self.selected_cells.clear();
1597            self.selection_anchor = None;
1598        }
1599    }
1600
1601    /// Select a row
1602    pub fn select_row(&mut self, row: Option<usize>) {
1603        if self.selected_row != row {
1604            self.save_to_history();
1605            self.selected_row = row;
1606            self.total_selections += 1;
1607            self.last_selection_time = Some(Instant::now());
1608        }
1609    }
1610
1611    /// Select a column
1612    pub fn select_column(&mut self, column: usize) {
1613        if self.selected_column != column {
1614            self.save_to_history();
1615            self.selected_column = column;
1616            self.total_selections += 1;
1617            self.last_selection_time = Some(Instant::now());
1618        }
1619    }
1620
1621    /// Select a cell
1622    pub fn select_cell(&mut self, row: usize, column: usize) {
1623        self.save_to_history();
1624        self.selected_row = Some(row);
1625        self.selected_column = column;
1626        self.total_selections += 1;
1627        self.last_selection_time = Some(Instant::now());
1628    }
1629
1630    /// Add cell to multi-selection
1631    pub fn add_cell_to_selection(&mut self, row: usize, column: usize) {
1632        let cell = (row, column);
1633        if !self.selected_cells.contains(&cell) {
1634            self.selected_cells.push(cell);
1635            self.total_selections += 1;
1636            self.last_selection_time = Some(Instant::now());
1637        }
1638    }
1639
1640    /// Clear all selections
1641    pub fn clear_selections(&mut self) {
1642        self.save_to_history();
1643        self.selected_cells.clear();
1644        self.selection_anchor = None;
1645    }
1646
1647    /// Save current state to history
1648    fn save_to_history(&mut self) {
1649        let entry = SelectionHistoryEntry {
1650            mode: self.mode.clone(),
1651            row: self.selected_row,
1652            column: self.selected_column,
1653            cells: self.selected_cells.clone(),
1654            timestamp: chrono::Local::now(),
1655        };
1656
1657        if self.history.len() >= self.max_history {
1658            self.history.pop_front();
1659        }
1660        self.history.push_back(entry);
1661    }
1662
1663    /// Get selection statistics
1664    #[must_use]
1665    pub fn get_stats(&self) -> String {
1666        let mode_str = match self.mode {
1667            SelectionMode::Row => "Row",
1668            SelectionMode::Cell => "Cell",
1669            SelectionMode::Column => "Column",
1670        };
1671
1672        let selection_str = match (self.selected_row, self.selected_cells.len()) {
1673            (Some(row), 0) => format!("Row {}, Col {}", row, self.selected_column),
1674            (_, n) if n > 0 => format!("{n} cells selected"),
1675            _ => format!("Col {}", self.selected_column),
1676        };
1677
1678        format!(
1679            "Mode: {}, Selection: {}, Total: {}",
1680            mode_str, selection_str, self.total_selections
1681        )
1682    }
1683}
1684
1685// ========== Proxy Structures for Buffer ViewState ==========
1686// These proxies allow AppStateContainer to access navigation and selection state
1687// directly from the current buffer's ViewState, eliminating state duplication
1688
1689/// Read-only proxy for navigation state
1690pub struct NavigationProxy<'a> {
1691    buffer: Option<&'a crate::buffer::Buffer>,
1692}
1693
1694impl<'a> NavigationProxy<'a> {
1695    #[must_use]
1696    pub fn new(buffer: Option<&'a crate::buffer::Buffer>) -> Self {
1697        Self { buffer }
1698    }
1699
1700    #[must_use]
1701    pub fn selected_row(&self) -> usize {
1702        self.buffer.map_or(0, |b| b.view_state.crosshair_row)
1703    }
1704
1705    #[must_use]
1706    pub fn selected_column(&self) -> usize {
1707        self.buffer.map_or(0, |b| b.view_state.crosshair_col)
1708    }
1709
1710    #[must_use]
1711    pub fn scroll_offset(&self) -> (usize, usize) {
1712        self.buffer.map_or((0, 0), |b| b.view_state.scroll_offset)
1713    }
1714
1715    #[must_use]
1716    pub fn viewport_lock(&self) -> bool {
1717        self.buffer.is_some_and(|b| b.view_state.viewport_lock)
1718    }
1719
1720    #[must_use]
1721    pub fn cursor_lock(&self) -> bool {
1722        self.buffer.is_some_and(|b| b.view_state.cursor_lock)
1723    }
1724
1725    #[must_use]
1726    pub fn total_rows(&self) -> usize {
1727        self.buffer.map_or(0, |b| b.view_state.total_rows)
1728    }
1729
1730    #[must_use]
1731    pub fn total_columns(&self) -> usize {
1732        self.buffer.map_or(0, |b| b.view_state.total_columns)
1733    }
1734}
1735
1736/// Mutable proxy for navigation state
1737pub struct NavigationProxyMut<'a> {
1738    buffer: Option<&'a mut crate::buffer::Buffer>,
1739}
1740
1741impl<'a> NavigationProxyMut<'a> {
1742    #[must_use]
1743    pub fn new(buffer: Option<&'a mut crate::buffer::Buffer>) -> Self {
1744        Self { buffer }
1745    }
1746
1747    pub fn set_selected_row(&mut self, row: usize) {
1748        if let Some(buffer) = &mut self.buffer {
1749            buffer.view_state.crosshair_row = row;
1750        }
1751    }
1752
1753    pub fn set_selected_column(&mut self, col: usize) {
1754        if let Some(buffer) = &mut self.buffer {
1755            buffer.view_state.crosshair_col = col;
1756        }
1757    }
1758
1759    pub fn set_scroll_offset(&mut self, offset: (usize, usize)) {
1760        if let Some(buffer) = &mut self.buffer {
1761            buffer.view_state.scroll_offset = offset;
1762        }
1763    }
1764
1765    pub fn set_viewport_lock(&mut self, locked: bool) {
1766        if let Some(buffer) = &mut self.buffer {
1767            buffer.view_state.viewport_lock = locked;
1768        }
1769    }
1770
1771    pub fn set_cursor_lock(&mut self, locked: bool) {
1772        if let Some(buffer) = &mut self.buffer {
1773            buffer.view_state.cursor_lock = locked;
1774        }
1775    }
1776
1777    pub fn update_totals(&mut self, rows: usize, columns: usize) {
1778        if let Some(buffer) = &mut self.buffer {
1779            buffer.view_state.total_rows = rows;
1780            buffer.view_state.total_columns = columns;
1781        }
1782    }
1783}
1784
1785/// Read-only proxy for selection state
1786pub struct SelectionProxy<'a> {
1787    buffer: Option<&'a crate::buffer::Buffer>,
1788}
1789
1790impl<'a> SelectionProxy<'a> {
1791    #[must_use]
1792    pub fn new(buffer: Option<&'a crate::buffer::Buffer>) -> Self {
1793        Self { buffer }
1794    }
1795
1796    #[must_use]
1797    pub fn mode(&self) -> crate::buffer::SelectionMode {
1798        self.buffer.map_or(crate::buffer::SelectionMode::Row, |b| {
1799            b.view_state.selection_mode.clone()
1800        })
1801    }
1802
1803    #[must_use]
1804    pub fn selected_cells(&self) -> Vec<(usize, usize)> {
1805        self.buffer
1806            .map(|b| b.view_state.selected_cells.clone())
1807            .unwrap_or_default()
1808    }
1809
1810    #[must_use]
1811    pub fn selection_anchor(&self) -> Option<(usize, usize)> {
1812        self.buffer.and_then(|b| b.view_state.selection_anchor)
1813    }
1814}
1815
1816/// Mutable proxy for selection state
1817pub struct SelectionProxyMut<'a> {
1818    buffer: Option<&'a mut crate::buffer::Buffer>,
1819}
1820
1821impl<'a> SelectionProxyMut<'a> {
1822    #[must_use]
1823    pub fn new(buffer: Option<&'a mut crate::buffer::Buffer>) -> Self {
1824        Self { buffer }
1825    }
1826
1827    pub fn set_mode(&mut self, mode: crate::buffer::SelectionMode) {
1828        if let Some(buffer) = &mut self.buffer {
1829            buffer.view_state.selection_mode = mode;
1830        }
1831    }
1832
1833    pub fn add_selected_cell(&mut self, cell: (usize, usize)) {
1834        if let Some(buffer) = &mut self.buffer {
1835            buffer.view_state.selected_cells.push(cell);
1836        }
1837    }
1838
1839    pub fn clear_selections(&mut self) {
1840        if let Some(buffer) = &mut self.buffer {
1841            buffer.view_state.selected_cells.clear();
1842            buffer.view_state.selection_anchor = None;
1843        }
1844    }
1845
1846    pub fn set_selection_anchor(&mut self, anchor: Option<(usize, usize)>) {
1847        if let Some(buffer) = &mut self.buffer {
1848            buffer.view_state.selection_anchor = anchor;
1849        }
1850    }
1851}
1852
1853/// History search state (for Ctrl+R functionality)
1854#[derive(Debug, Clone)]
1855pub struct HistorySearchState {
1856    pub query: String,
1857    pub matches: Vec<crate::history::HistoryMatch>,
1858    pub selected_index: usize,
1859    pub is_active: bool,
1860    pub original_input: String,
1861}
1862
1863impl Default for HistorySearchState {
1864    fn default() -> Self {
1865        Self::new()
1866    }
1867}
1868
1869impl HistorySearchState {
1870    #[must_use]
1871    pub fn new() -> Self {
1872        Self {
1873            query: String::new(),
1874            matches: Vec::new(),
1875            selected_index: 0,
1876            is_active: false,
1877            original_input: String::new(),
1878        }
1879    }
1880
1881    pub fn clear(&mut self) {
1882        self.query.clear();
1883        self.matches.clear();
1884        self.selected_index = 0;
1885        self.is_active = false;
1886        self.original_input.clear();
1887    }
1888}
1889
1890/// Help widget state management
1891#[derive(Debug, Clone)]
1892pub struct HelpState {
1893    /// Whether help is visible
1894    pub is_visible: bool,
1895
1896    /// Vertical scroll offset
1897    pub scroll_offset: u16,
1898
1899    /// Maximum scroll based on content height
1900    pub max_scroll: u16,
1901
1902    /// Number of times help was opened
1903    pub open_count: usize,
1904
1905    /// Last time help was opened
1906    pub last_opened: Option<Instant>,
1907}
1908
1909impl Default for HelpState {
1910    fn default() -> Self {
1911        Self::new()
1912    }
1913}
1914
1915impl HelpState {
1916    #[must_use]
1917    pub fn new() -> Self {
1918        Self {
1919            is_visible: false,
1920            scroll_offset: 0,
1921            max_scroll: 0,
1922            open_count: 0,
1923            last_opened: None,
1924        }
1925    }
1926
1927    /// Show help and reset scroll
1928    pub fn show(&mut self) {
1929        self.is_visible = true;
1930        self.scroll_offset = 0;
1931        self.open_count += 1;
1932        self.last_opened = Some(Instant::now());
1933    }
1934
1935    /// Hide help
1936    pub fn hide(&mut self) {
1937        self.is_visible = false;
1938    }
1939
1940    /// Toggle help visibility
1941    pub fn toggle(&mut self) {
1942        if self.is_visible {
1943            self.hide();
1944        } else {
1945            self.show();
1946        }
1947    }
1948
1949    /// Scroll down by amount
1950    pub fn scroll_down(&mut self, amount: u16) {
1951        self.scroll_offset = (self.scroll_offset + amount).min(self.max_scroll);
1952    }
1953
1954    /// Scroll up by amount
1955    pub fn scroll_up(&mut self, amount: u16) {
1956        self.scroll_offset = self.scroll_offset.saturating_sub(amount);
1957    }
1958
1959    /// Set maximum scroll based on content height and viewport
1960    pub fn set_max_scroll(&mut self, content_lines: usize, viewport_height: usize) {
1961        self.max_scroll = content_lines.saturating_sub(viewport_height) as u16;
1962    }
1963}
1964
1965/// State for undo/redo operations
1966#[derive(Debug, Clone)]
1967pub struct UndoRedoState {
1968    /// Undo stack storing (text, `cursor_position`)
1969    pub undo_stack: Vec<(String, usize)>,
1970    /// Redo stack storing (text, `cursor_position`)
1971    pub redo_stack: Vec<(String, usize)>,
1972    /// Maximum number of undo entries to keep
1973    pub max_undo_entries: usize,
1974}
1975
1976impl Default for UndoRedoState {
1977    fn default() -> Self {
1978        Self {
1979            undo_stack: Vec::new(),
1980            redo_stack: Vec::new(),
1981            max_undo_entries: 100,
1982        }
1983    }
1984}
1985
1986impl UndoRedoState {
1987    /// Push a state to the undo stack
1988    pub fn push_undo(&mut self, text: String, cursor: usize) {
1989        self.undo_stack.push((text, cursor));
1990        if self.undo_stack.len() > self.max_undo_entries {
1991            self.undo_stack.remove(0);
1992        }
1993        // Clear redo stack when new action is performed
1994        self.redo_stack.clear();
1995    }
1996
1997    /// Pop from undo stack
1998    pub fn pop_undo(&mut self) -> Option<(String, usize)> {
1999        self.undo_stack.pop()
2000    }
2001
2002    /// Push to redo stack
2003    pub fn push_redo(&mut self, text: String, cursor: usize) {
2004        self.redo_stack.push((text, cursor));
2005        if self.redo_stack.len() > self.max_undo_entries {
2006            self.redo_stack.remove(0);
2007        }
2008    }
2009
2010    /// Pop from redo stack
2011    pub fn pop_redo(&mut self) -> Option<(String, usize)> {
2012        self.redo_stack.pop()
2013    }
2014}
2015
2016/// State for UI scrolling and viewport
2017#[derive(Debug, Clone, Default)]
2018pub struct ScrollState {
2019    /// Help widget scroll offset
2020    pub help_scroll: u16,
2021    /// Input field horizontal scroll offset
2022    pub input_scroll_offset: u16,
2023    /// Main viewport scroll offset (row, column)
2024    pub viewport_scroll_offset: (usize, usize),
2025    /// Last calculated visible rows (viewport height)
2026    pub last_visible_rows: usize,
2027}
2028
2029/// State for managing key chord sequences
2030#[derive(Debug, Clone)]
2031pub struct ChordState {
2032    /// Current chord sequence being built
2033    pub current_chord: Vec<String>, // Store as strings for simplicity
2034    /// Time when current chord started (as timestamp)
2035    pub chord_start: Option<std::time::SystemTime>,
2036    /// Whether chord mode is active
2037    pub is_active: bool,
2038    /// Description of current chord mode (e.g., "Yank mode")
2039    pub description: Option<String>,
2040    /// Registered chord mappings (chord -> action)
2041    pub registered_chords: std::collections::HashMap<String, String>,
2042    /// History of chord completions
2043    pub history: Vec<(String, String, std::time::SystemTime)>, // (chord, action, timestamp)
2044}
2045
2046impl Default for ChordState {
2047    fn default() -> Self {
2048        let mut registered_chords = std::collections::HashMap::new();
2049        // Register default yank chords
2050        registered_chords.insert("yy".to_string(), "yank_row".to_string());
2051        registered_chords.insert("yr".to_string(), "yank_row".to_string());
2052        registered_chords.insert("yc".to_string(), "yank_column".to_string());
2053        registered_chords.insert("ya".to_string(), "yank_all".to_string());
2054        registered_chords.insert("yv".to_string(), "yank_cell".to_string());
2055
2056        Self {
2057            current_chord: Vec::new(),
2058            chord_start: None,
2059            is_active: false,
2060            description: None,
2061            registered_chords,
2062            history: Vec::new(),
2063        }
2064    }
2065}
2066
2067impl ChordState {
2068    /// Clear the current chord sequence
2069    pub fn clear(&mut self) {
2070        self.current_chord.clear();
2071        self.chord_start = None;
2072        self.is_active = false;
2073        self.description = None;
2074    }
2075
2076    /// Add a key to the current chord
2077    pub fn add_key(&mut self, key: String) {
2078        if self.current_chord.is_empty() {
2079            self.chord_start = Some(std::time::SystemTime::now());
2080        }
2081        self.current_chord.push(key);
2082        self.is_active = true;
2083    }
2084
2085    /// Get the current chord as a string
2086    #[must_use]
2087    pub fn get_chord_string(&self) -> String {
2088        self.current_chord.join("")
2089    }
2090
2091    /// Check if current chord matches a registered chord
2092    #[must_use]
2093    pub fn check_match(&self) -> Option<String> {
2094        let chord = self.get_chord_string();
2095        self.registered_chords.get(&chord).cloned()
2096    }
2097
2098    /// Check if current chord is a partial match
2099    #[must_use]
2100    pub fn is_partial_match(&self) -> bool {
2101        let current = self.get_chord_string();
2102        self.registered_chords
2103            .keys()
2104            .any(|chord| chord.starts_with(&current) && chord.len() > current.len())
2105    }
2106
2107    /// Record a completed chord
2108    pub fn record_completion(&mut self, chord: String, action: String) {
2109        self.history
2110            .push((chord, action, std::time::SystemTime::now()));
2111        // Keep only last 50 completions
2112        if self.history.len() > 50 {
2113            self.history.remove(0);
2114        }
2115    }
2116}
2117
2118/// Container for all widget states
2119pub struct WidgetStates {
2120    pub search_modes: SearchModesWidget,
2121    pub history: Option<HistoryWidget>, // Will be initialized with CommandHistory later
2122    pub help: HelpWidget,
2123    pub stats: StatsWidget,
2124    // pub debug: DebugWidget, // TODO: Add when DebugInfoProvider is implemented
2125}
2126
2127impl Default for WidgetStates {
2128    fn default() -> Self {
2129        Self::new()
2130    }
2131}
2132
2133impl WidgetStates {
2134    #[must_use]
2135    pub fn new() -> Self {
2136        Self {
2137            search_modes: SearchModesWidget::new(),
2138            history: None, // Will be set when CommandHistory is available
2139            help: HelpWidget::new(),
2140            stats: StatsWidget::new(),
2141            // debug: DebugWidget::new(), // TODO: Add when available
2142        }
2143    }
2144
2145    pub fn set_history(&mut self, history: HistoryWidget) {
2146        self.history = Some(history);
2147    }
2148}
2149
2150/// Centralized query results state management
2151#[derive(Debug, Clone)]
2152pub struct ResultsState {
2153    /// Current query results for active buffer
2154    pub current_results: Option<QueryResponse>,
2155
2156    /// Results cache with LRU behavior
2157    pub results_cache: HashMap<String, CachedResult>,
2158
2159    /// Maximum cache size (number of queries)
2160    pub max_cache_size: usize,
2161
2162    /// Memory usage tracking
2163    pub total_memory_usage: usize,
2164
2165    /// Memory limit in bytes
2166    pub memory_limit: usize,
2167
2168    /// Last query executed
2169    pub last_query: String,
2170
2171    /// Last query execution time
2172    pub last_execution_time: Duration,
2173
2174    /// Query history for performance analysis
2175    pub query_performance_history: VecDeque<QueryPerformance>,
2176
2177    /// Whether results are from cache
2178    pub from_cache: bool,
2179
2180    /// Last modification timestamp
2181    pub last_modified: Instant,
2182}
2183
2184#[derive(Debug, Clone)]
2185pub struct CachedResult {
2186    /// The actual query response
2187    pub response: QueryResponse,
2188
2189    /// When this result was cached
2190    pub cached_at: Instant,
2191
2192    /// How often this result was accessed (for LRU)
2193    pub access_count: u32,
2194
2195    /// Last access time (for LRU)
2196    pub last_access: Instant,
2197
2198    /// Memory size of this result
2199    pub memory_size: usize,
2200}
2201
2202#[derive(Debug, Clone)]
2203pub struct QueryPerformance {
2204    /// The query that was executed
2205    pub query: String,
2206
2207    /// Execution time
2208    pub execution_time: Duration,
2209
2210    /// Number of rows returned
2211    pub row_count: usize,
2212
2213    /// Whether result came from cache
2214    pub from_cache: bool,
2215
2216    /// Memory usage
2217    pub memory_usage: usize,
2218
2219    /// Timestamp of execution
2220    pub executed_at: Instant,
2221}
2222
2223impl Default for ResultsState {
2224    fn default() -> Self {
2225        Self {
2226            current_results: None,
2227            results_cache: HashMap::new(),
2228            max_cache_size: 100, // Cache up to 100 queries
2229            total_memory_usage: 0,
2230            memory_limit: 512 * 1024 * 1024, // 512MB limit
2231            last_query: String::new(),
2232            last_execution_time: Duration::from_millis(0),
2233            query_performance_history: VecDeque::with_capacity(1000),
2234            from_cache: false,
2235            last_modified: Instant::now(),
2236        }
2237    }
2238}
2239
2240/// Clipboard/Yank state management
2241#[derive(Debug, Clone)]
2242pub struct ClipboardState {
2243    /// Last yanked item (description, `full_value`, preview)
2244    pub last_yanked: Option<YankedItem>,
2245
2246    /// History of yanked items
2247    pub yank_history: VecDeque<YankedItem>,
2248
2249    /// Maximum history size
2250    pub max_history: usize,
2251
2252    /// Current yank register (for multi-register support in future)
2253    pub current_register: char,
2254
2255    /// Statistics
2256    pub total_yanks: usize,
2257    pub last_yank_time: Option<Instant>,
2258}
2259
2260#[derive(Debug, Clone)]
2261pub struct YankedItem {
2262    /// Description of what was yanked (e.g., "cell at [2,3]", "row 5", "column 'name'")
2263    pub description: String,
2264
2265    /// The full value that was yanked
2266    pub full_value: String,
2267
2268    /// Preview of the value (truncated for display)
2269    pub preview: String,
2270
2271    /// Type of yank operation
2272    pub yank_type: YankType,
2273
2274    /// When this was yanked
2275    pub yanked_at: DateTime<Local>,
2276
2277    /// Size in bytes
2278    pub size_bytes: usize,
2279}
2280
2281#[derive(Debug, Clone, PartialEq)]
2282pub enum YankType {
2283    Cell {
2284        row: usize,
2285        column: usize,
2286    },
2287    Row {
2288        row: usize,
2289    },
2290    Column {
2291        name: String,
2292        index: usize,
2293    },
2294    All,
2295    Selection {
2296        start: (usize, usize),
2297        end: (usize, usize),
2298    },
2299    Query,
2300    TestCase,
2301    DebugContext,
2302}
2303
2304impl Default for ClipboardState {
2305    fn default() -> Self {
2306        Self {
2307            last_yanked: None,
2308            yank_history: VecDeque::with_capacity(50),
2309            max_history: 50,
2310            current_register: '"', // Default register like vim
2311            total_yanks: 0,
2312            last_yank_time: None,
2313        }
2314    }
2315}
2316
2317impl ClipboardState {
2318    #[must_use]
2319    pub fn new() -> Self {
2320        Self::default()
2321    }
2322
2323    /// Add a new yanked item
2324    pub fn add_yank(&mut self, item: YankedItem) {
2325        // Add to history
2326        self.yank_history.push_front(item.clone());
2327
2328        // Trim history if needed
2329        while self.yank_history.len() > self.max_history {
2330            self.yank_history.pop_back();
2331        }
2332
2333        // Update current
2334        self.last_yanked = Some(item);
2335        self.total_yanks += 1;
2336        self.last_yank_time = Some(Instant::now());
2337    }
2338
2339    /// Clear clipboard
2340    pub fn clear(&mut self) {
2341        self.last_yanked = None;
2342    }
2343
2344    /// Clear all history
2345    pub fn clear_history(&mut self) {
2346        self.yank_history.clear();
2347        self.last_yanked = None;
2348    }
2349
2350    /// Get clipboard statistics
2351    #[must_use]
2352    pub fn get_stats(&self) -> String {
2353        format!(
2354            "Total yanks: {}, History items: {}, Last yank: {}",
2355            self.total_yanks,
2356            self.yank_history.len(),
2357            self.last_yank_time
2358                .map_or_else(|| "never".to_string(), |t| format!("{:?} ago", t.elapsed()))
2359        )
2360    }
2361}
2362
2363impl ResultsState {
2364    #[must_use]
2365    pub fn new() -> Self {
2366        Self::default()
2367    }
2368
2369    /// Set current query results with performance tracking
2370    pub fn set_results(
2371        &mut self,
2372        results: QueryResponse,
2373        execution_time: Duration,
2374        from_cache: bool,
2375    ) -> Result<()> {
2376        let row_count = results.count;
2377        let memory_usage = self.estimate_memory_usage(&results);
2378
2379        // Record performance metrics
2380        let performance = QueryPerformance {
2381            query: results.query.select.join(", "),
2382            execution_time,
2383            row_count,
2384            from_cache,
2385            memory_usage,
2386            executed_at: Instant::now(),
2387        };
2388
2389        // Add to performance history (keep last 1000)
2390        self.query_performance_history.push_back(performance);
2391        if self.query_performance_history.len() > 1000 {
2392            self.query_performance_history.pop_front();
2393        }
2394
2395        // Update state
2396        self.current_results = Some(results);
2397        self.last_execution_time = execution_time;
2398        self.from_cache = from_cache;
2399        self.last_modified = Instant::now();
2400
2401        Ok(())
2402    }
2403
2404    /// Get current results
2405    #[must_use]
2406    pub fn get_results(&self) -> Option<&QueryResponse> {
2407        self.current_results.as_ref()
2408    }
2409
2410    /// Cache query results with LRU management
2411    pub fn cache_results(&mut self, query_key: String, results: QueryResponse) -> Result<()> {
2412        let memory_usage = self.estimate_memory_usage(&results);
2413
2414        // Check memory limit
2415        if self.total_memory_usage + memory_usage > self.memory_limit {
2416            self.evict_to_fit(memory_usage)?;
2417        }
2418
2419        // Create cached result
2420        let cached_result = CachedResult {
2421            response: results,
2422            cached_at: Instant::now(),
2423            access_count: 1,
2424            last_access: Instant::now(),
2425            memory_size: memory_usage,
2426        };
2427
2428        // Remove oldest if at capacity
2429        if self.results_cache.len() >= self.max_cache_size {
2430            self.evict_oldest()?;
2431        }
2432
2433        self.results_cache.insert(query_key, cached_result);
2434        self.total_memory_usage += memory_usage;
2435
2436        Ok(())
2437    }
2438
2439    /// Get cached results
2440    pub fn get_cached_results(&mut self, query_key: &str) -> Option<&QueryResponse> {
2441        if let Some(cached) = self.results_cache.get_mut(query_key) {
2442            cached.access_count += 1;
2443            cached.last_access = Instant::now();
2444            Some(&cached.response)
2445        } else {
2446            None
2447        }
2448    }
2449
2450    /// Clear all cached results
2451    pub fn clear_cache(&mut self) {
2452        self.results_cache.clear();
2453        self.total_memory_usage = 0;
2454    }
2455
2456    /// Get cache statistics
2457    #[must_use]
2458    pub fn get_cache_stats(&self) -> CacheStats {
2459        CacheStats {
2460            entry_count: self.results_cache.len(),
2461            memory_usage: self.total_memory_usage,
2462            memory_limit: self.memory_limit,
2463            hit_rate: self.calculate_hit_rate(),
2464        }
2465    }
2466
2467    /// Get performance statistics
2468    #[must_use]
2469    pub fn get_performance_stats(&self) -> PerformanceStats {
2470        let total_queries = self.query_performance_history.len();
2471        let cached_queries = self
2472            .query_performance_history
2473            .iter()
2474            .filter(|q| q.from_cache)
2475            .count();
2476        let avg_execution_time = if total_queries > 0 {
2477            self.query_performance_history
2478                .iter()
2479                .map(|q| q.execution_time.as_millis() as f64)
2480                .sum::<f64>()
2481                / total_queries as f64
2482        } else {
2483            0.0
2484        };
2485
2486        PerformanceStats {
2487            total_queries,
2488            cached_queries,
2489            cache_hit_rate: if total_queries > 0 {
2490                cached_queries as f64 / total_queries as f64
2491            } else {
2492                0.0
2493            },
2494            average_execution_time_ms: avg_execution_time,
2495            last_execution_time: self.last_execution_time,
2496        }
2497    }
2498
2499    // Private helper methods
2500
2501    fn estimate_memory_usage(&self, results: &QueryResponse) -> usize {
2502        // Rough estimation of memory usage
2503        let data_size = results
2504            .data
2505            .iter()
2506            .map(|row| serde_json::to_string(row).unwrap_or_default().len())
2507            .sum::<usize>();
2508
2509        // Add overhead for structure
2510        data_size + std::mem::size_of::<QueryResponse>() + 1024 // Extra overhead
2511    }
2512
2513    fn evict_to_fit(&mut self, needed_space: usize) -> Result<()> {
2514        // Evict least recently used items until we have enough space
2515        while self.total_memory_usage + needed_space > self.memory_limit
2516            && !self.results_cache.is_empty()
2517        {
2518            self.evict_oldest()?;
2519        }
2520        Ok(())
2521    }
2522
2523    fn evict_oldest(&mut self) -> Result<()> {
2524        if let Some((key, cached)) = self
2525            .results_cache
2526            .iter()
2527            .min_by_key(|(_, cached)| cached.last_access)
2528            .map(|(k, v)| (k.clone(), v.memory_size))
2529        {
2530            self.results_cache.remove(&key);
2531            self.total_memory_usage = self.total_memory_usage.saturating_sub(cached);
2532        }
2533        Ok(())
2534    }
2535
2536    fn calculate_hit_rate(&self) -> f64 {
2537        // Simple hit rate based on recent performance history
2538        let total = self.query_performance_history.len();
2539        if total == 0 {
2540            return 0.0;
2541        }
2542
2543        let hits = self
2544            .query_performance_history
2545            .iter()
2546            .filter(|q| q.from_cache)
2547            .count();
2548        hits as f64 / total as f64
2549    }
2550}
2551
2552#[derive(Debug, Clone)]
2553pub struct CacheStats {
2554    pub entry_count: usize,
2555    pub memory_usage: usize,
2556    pub memory_limit: usize,
2557    pub hit_rate: f64,
2558}
2559
2560#[derive(Debug, Clone)]
2561pub struct PerformanceStats {
2562    pub total_queries: usize,
2563    pub cached_queries: usize,
2564    pub cache_hit_rate: f64,
2565    pub average_execution_time_ms: f64,
2566    pub last_execution_time: Duration,
2567}
2568
2569/// Results cache for storing query results
2570#[derive(Debug, Clone)]
2571pub struct ResultsCache {
2572    cache: HashMap<String, Vec<Vec<String>>>,
2573    max_size: usize,
2574}
2575
2576impl ResultsCache {
2577    #[must_use]
2578    pub fn new(max_size: usize) -> Self {
2579        Self {
2580            cache: HashMap::new(),
2581            max_size,
2582        }
2583    }
2584
2585    #[must_use]
2586    pub fn get(&self, key: &str) -> Option<&Vec<Vec<String>>> {
2587        self.cache.get(key)
2588    }
2589
2590    pub fn insert(&mut self, key: String, value: Vec<Vec<String>>) {
2591        if self.cache.len() >= self.max_size {
2592            // Remove oldest entry (simplified - in practice use LRU)
2593            if let Some(first_key) = self.cache.keys().next().cloned() {
2594                self.cache.remove(&first_key);
2595            }
2596        }
2597        self.cache.insert(key, value);
2598    }
2599}
2600
2601/// Main application state container
2602pub struct AppStateContainer {
2603    // Document/Buffer state
2604    buffers: BufferManager,
2605    current_buffer_id: usize,
2606
2607    // Input state
2608    command_input: RefCell<InputState>,
2609
2610    // Search/Filter states
2611    search: RefCell<SearchState>,
2612    filter: RefCell<FilterState>,
2613    column_search: RefCell<ColumnSearchState>,
2614    history_search: RefCell<HistorySearchState>,
2615    sort: RefCell<SortState>,
2616    selection: RefCell<SelectionState>,
2617    // Completion state
2618    completion: RefCell<CompletionState>,
2619
2620    // Widget states
2621    widgets: WidgetStates,
2622
2623    // UI states
2624    cache_list: CacheListState,
2625    column_stats: ColumnStatsState,
2626    jump_to_row: JumpToRowState,
2627    navigation: RefCell<NavigationState>,
2628
2629    // History
2630    command_history: RefCell<CommandHistory>,
2631    key_press_history: RefCell<KeyPressHistory>,
2632
2633    // Results state (centralized query results management)
2634    results: RefCell<ResultsState>,
2635
2636    // Clipboard/Yank state
2637    clipboard: RefCell<ClipboardState>,
2638
2639    // Chord state
2640    chord: RefCell<ChordState>,
2641
2642    // Undo/Redo state
2643    undo_redo: RefCell<UndoRedoState>,
2644
2645    // Scroll and viewport state
2646    scroll: RefCell<ScrollState>,
2647
2648    // Legacy results cache (to be deprecated)
2649    results_cache: ResultsCache,
2650
2651    // Mode stack for nested modes
2652    mode_stack: Vec<AppMode>,
2653
2654    // Debug/logging
2655    debug_enabled: bool,
2656    debug_service: RefCell<Option<crate::debug_service::DebugService>>,
2657
2658    // Help state - manages visibility and scrolling
2659    help: RefCell<HelpState>,
2660}
2661
2662impl AppStateContainer {
2663    /// Format numbers in a compact way (1000 -> 1k, 1500000 -> 1.5M, etc.)
2664    #[must_use]
2665    pub fn format_number_compact(n: usize) -> String {
2666        if n < 1000 {
2667            n.to_string()
2668        } else if n < 1000000 {
2669            let k = n as f64 / 1000.0;
2670            if k.fract() == 0.0 {
2671                format!("{}k", k as usize)
2672            } else if k < 10.0 {
2673                format!("{k:.1}k")
2674            } else {
2675                format!("{}k", k as usize)
2676            }
2677        } else if n < 1000000000 {
2678            let m = n as f64 / 1000000.0;
2679            if m.fract() == 0.0 {
2680                format!("{}M", m as usize)
2681            } else if m < 10.0 {
2682                format!("{m:.1}M")
2683            } else {
2684                format!("{}M", m as usize)
2685            }
2686        } else {
2687            let b = n as f64 / 1000000000.0;
2688            if b.fract() == 0.0 {
2689                format!("{}B", b as usize)
2690            } else {
2691                format!("{b:.1}B")
2692            }
2693        }
2694    }
2695
2696    pub fn new(buffers: BufferManager) -> Result<Self> {
2697        let command_history = CommandHistory::new()?;
2698        let mut widgets = WidgetStates::new();
2699        widgets.set_history(HistoryWidget::new(command_history.clone()));
2700
2701        Ok(Self {
2702            buffers,
2703            current_buffer_id: 0,
2704            command_input: RefCell::new(InputState::new()),
2705            search: RefCell::new(SearchState::new()),
2706            filter: RefCell::new(FilterState::new()),
2707            column_search: RefCell::new(ColumnSearchState::new()),
2708            history_search: RefCell::new(HistorySearchState::new()),
2709            sort: RefCell::new(SortState::new()),
2710            selection: RefCell::new(SelectionState::new()),
2711            completion: RefCell::new(CompletionState::new()),
2712            widgets,
2713            cache_list: CacheListState::new(),
2714            column_stats: ColumnStatsState::new(),
2715            jump_to_row: JumpToRowState::new(),
2716            command_history: RefCell::new(command_history),
2717            key_press_history: RefCell::new(KeyPressHistory::new(50)), // Keep last 50 key presses
2718            results: RefCell::new(ResultsState::new()),
2719            clipboard: RefCell::new(ClipboardState::new()),
2720            chord: RefCell::new(ChordState::default()),
2721            undo_redo: RefCell::new(UndoRedoState::default()),
2722            scroll: RefCell::new(ScrollState::default()),
2723            navigation: RefCell::new(NavigationState::new()),
2724            results_cache: ResultsCache::new(100),
2725            mode_stack: vec![AppMode::Command],
2726            debug_enabled: false,
2727            debug_service: RefCell::new(None), // Will be set later via set_debug_service
2728            help: RefCell::new(HelpState::new()),
2729        })
2730    }
2731
2732    // Buffer access
2733    pub fn current_buffer(&self) -> Option<&crate::buffer::Buffer> {
2734        self.buffers.current()
2735    }
2736
2737    pub fn current_buffer_mut(&mut self) -> Option<&mut crate::buffer::Buffer> {
2738        self.buffers.current_mut()
2739    }
2740
2741    pub fn buffers(&self) -> &BufferManager {
2742        &self.buffers
2743    }
2744
2745    pub fn buffers_mut(&mut self) -> &mut BufferManager {
2746        &mut self.buffers
2747    }
2748
2749    // Input state access
2750    pub fn command_input(&self) -> std::cell::Ref<'_, InputState> {
2751        self.command_input.borrow()
2752    }
2753
2754    pub fn command_input_mut(&self) -> std::cell::RefMut<'_, InputState> {
2755        self.command_input.borrow_mut()
2756    }
2757
2758    // Helper methods to update input state (for use through Arc)
2759    pub fn set_input_text(&self, text: String) {
2760        let mut input = self.command_input.borrow_mut();
2761        input.text = text.clone();
2762        input.cursor_position = text.len();
2763    }
2764
2765    pub fn set_input_text_with_cursor(&self, text: String, cursor: usize) {
2766        let mut input = self.command_input.borrow_mut();
2767        input.text = text;
2768        input.cursor_position = cursor;
2769    }
2770
2771    pub fn set_last_executed_query(&self, query: String) {
2772        self.command_input.borrow_mut().last_executed_query = query;
2773    }
2774
2775    // Search/Filter state access
2776    pub fn search(&self) -> std::cell::Ref<'_, SearchState> {
2777        self.search.borrow()
2778    }
2779
2780    pub fn search_mut(&self) -> std::cell::RefMut<'_, SearchState> {
2781        self.search.borrow_mut()
2782    }
2783
2784    // Search operations with logging
2785
2786    /// Start a new search with the given pattern
2787    pub fn start_search(&self, pattern: String) -> usize {
2788        let mut search = self.search.borrow_mut();
2789        let old_pattern = search.pattern.clone();
2790        let old_active = search.is_active;
2791
2792        search.pattern = pattern.clone();
2793        search.is_active = true;
2794        search.last_search_time = Some(std::time::Instant::now());
2795
2796        if let Some(ref debug_service) = *self.debug_service.borrow() {
2797            debug_service.info(
2798                "Search",
2799                format!(
2800                    "Starting search: '{pattern}' (was: '{old_pattern}', active: {old_active})"
2801                ),
2802            );
2803        }
2804
2805        // Return match count (to be filled by caller for now)
2806        0
2807    }
2808
2809    /// Update search matches
2810    pub fn update_search_matches(&self, matches: Vec<(usize, usize, usize, usize)>) {
2811        let match_count = matches.len();
2812        let mut search = self.search.borrow_mut();
2813        let pattern = search.pattern.clone();
2814        search.matches = matches;
2815        search.current_match = if match_count > 0 { 0 } else { 0 };
2816
2817        if let Some(ref debug_service) = *self.debug_service.borrow() {
2818            debug_service.info(
2819                "Search",
2820                format!("Search found {match_count} matches for pattern '{pattern}'"),
2821            );
2822        }
2823
2824        // Record in history if this completes a search
2825        if !pattern.is_empty() {
2826            let duration_ms = search
2827                .last_search_time
2828                .map(|t| t.elapsed().as_millis() as u64);
2829
2830            let entry = SearchHistoryEntry {
2831                pattern: pattern.clone(),
2832                match_count,
2833                timestamp: Local::now(),
2834                duration_ms,
2835            };
2836
2837            if search.history.len() >= 20 {
2838                search.history.pop_front();
2839            }
2840            search.history.push_back(entry);
2841        }
2842    }
2843
2844    /// Navigate to next search match
2845    pub fn next_search_match(&self) -> Option<(usize, usize)> {
2846        let mut search = self.search.borrow_mut();
2847        if search.matches.is_empty() {
2848            return None;
2849        }
2850
2851        let old_match = search.current_match;
2852        search.current_match = (search.current_match + 1) % search.matches.len();
2853
2854        if let Some(ref debug_service) = *self.debug_service.borrow() {
2855            debug_service.info(
2856                "Search",
2857                format!(
2858                    "Navigate to next match: {} -> {} (of {})",
2859                    old_match,
2860                    search.current_match,
2861                    search.matches.len()
2862                ),
2863            );
2864        }
2865
2866        let match_pos = search.matches[search.current_match];
2867        Some((match_pos.0, match_pos.1))
2868    }
2869
2870    /// Navigate to previous search match
2871    pub fn previous_search_match(&self) -> Option<(usize, usize)> {
2872        let mut search = self.search.borrow_mut();
2873        if search.matches.is_empty() {
2874            return None;
2875        }
2876
2877        let old_match = search.current_match;
2878        search.current_match = if search.current_match == 0 {
2879            search.matches.len() - 1
2880        } else {
2881            search.current_match - 1
2882        };
2883
2884        if let Some(ref debug_service) = *self.debug_service.borrow() {
2885            debug_service.info(
2886                "Search",
2887                format!(
2888                    "Navigate to previous match: {} -> {} (of {})",
2889                    old_match,
2890                    search.current_match,
2891                    search.matches.len()
2892                ),
2893            );
2894        }
2895
2896        let match_pos = search.matches[search.current_match];
2897        Some((match_pos.0, match_pos.1))
2898    }
2899
2900    /// Clear current search
2901    pub fn clear_search(&self) {
2902        let mut search = self.search.borrow_mut();
2903        let had_matches = search.matches.len();
2904        let had_pattern = search.pattern.clone();
2905
2906        search.clear();
2907
2908        if let Some(ref debug_service) = *self.debug_service.borrow() {
2909            debug_service.info(
2910                "Search",
2911                format!("Cleared search (had pattern: '{had_pattern}', {had_matches} matches)"),
2912            );
2913        }
2914    }
2915
2916    /// Perform search on provided data
2917    /// Returns the search matches as a vector of (row, col, `row_end`, `col_end`) tuples
2918    pub fn perform_search(&self, data: &[Vec<String>]) -> Vec<(usize, usize, usize, usize)> {
2919        use regex::Regex;
2920
2921        let pattern = self.search.borrow().pattern.clone();
2922        if pattern.is_empty() {
2923            let mut search = self.search.borrow_mut();
2924            search.matches.clear();
2925            search.current_match = 0;
2926            return Vec::new();
2927        }
2928
2929        let start_time = std::time::Instant::now();
2930        let mut matches = Vec::new();
2931
2932        if let Some(ref debug_service) = *self.debug_service.borrow() {
2933            debug_service.info(
2934                "Search",
2935                format!(
2936                    "Performing search for pattern '{}' on {} rows",
2937                    pattern,
2938                    data.len()
2939                ),
2940            );
2941        }
2942
2943        // Try to compile regex pattern
2944        match Regex::new(&pattern) {
2945            Ok(regex) => {
2946                for (row_idx, row) in data.iter().enumerate() {
2947                    for (col_idx, cell) in row.iter().enumerate() {
2948                        if regex.is_match(cell) {
2949                            // For now, just store simple match positions
2950                            // In future, could store actual match spans
2951                            matches.push((row_idx, col_idx, row_idx, col_idx));
2952                        }
2953                    }
2954                }
2955            }
2956            Err(e) => {
2957                if let Some(ref debug_service) = *self.debug_service.borrow() {
2958                    debug_service.info("Search", format!("Invalid regex pattern '{pattern}': {e}"));
2959                }
2960                // Fall back to simple string contains search
2961                let pattern_lower = pattern.to_lowercase();
2962                for (row_idx, row) in data.iter().enumerate() {
2963                    for (col_idx, cell) in row.iter().enumerate() {
2964                        if cell.to_lowercase().contains(&pattern_lower) {
2965                            matches.push((row_idx, col_idx, row_idx, col_idx));
2966                        }
2967                    }
2968                }
2969            }
2970        }
2971
2972        let elapsed = start_time.elapsed();
2973        self.search.borrow_mut().last_search_time = Some(start_time);
2974
2975        // Update search state with matches
2976        self.update_search_matches(matches.clone());
2977
2978        if let Some(ref debug_service) = *self.debug_service.borrow() {
2979            debug_service.info(
2980                "Search",
2981                format!(
2982                    "Search completed in {:?}: found {} matches for '{}'",
2983                    elapsed,
2984                    matches.len(),
2985                    pattern
2986                ),
2987            );
2988        }
2989
2990        matches
2991    }
2992
2993    /// Get current search match position (for highlighting)
2994    pub fn get_current_match(&self) -> Option<(usize, usize)> {
2995        let search = self.search.borrow();
2996        if search.matches.is_empty() || !search.is_active {
2997            return None;
2998        }
2999
3000        let match_pos = search.matches[search.current_match];
3001        Some((match_pos.0, match_pos.1))
3002    }
3003
3004    pub fn filter(&self) -> std::cell::Ref<'_, FilterState> {
3005        self.filter.borrow()
3006    }
3007
3008    pub fn filter_mut(&self) -> std::cell::RefMut<'_, FilterState> {
3009        self.filter.borrow_mut()
3010    }
3011
3012    pub fn column_search(&self) -> std::cell::Ref<'_, ColumnSearchState> {
3013        self.column_search.borrow()
3014    }
3015
3016    pub fn column_search_mut(&self) -> std::cell::RefMut<'_, ColumnSearchState> {
3017        self.column_search.borrow_mut()
3018    }
3019
3020    // Column search operations with logging
3021
3022    /// Start column search with pattern
3023    pub fn start_column_search(&self, pattern: String) {
3024        let mut column_search = self.column_search.borrow_mut();
3025        let old_pattern = column_search.pattern.clone();
3026        let old_active = column_search.is_active;
3027
3028        column_search.pattern = pattern.clone();
3029        column_search.is_active = true;
3030        column_search.last_search_time = Some(Instant::now());
3031
3032        if let Some(ref debug_service) = *self.debug_service.borrow() {
3033            debug_service.info(
3034                "ColumnSearch",
3035                format!(
3036                    "Starting column search: '{pattern}' (was: '{old_pattern}', active: {old_active})"
3037                ),
3038            );
3039        }
3040    }
3041
3042    /// Update column search matches
3043    pub fn update_column_search_matches(
3044        &self,
3045        columns: &[(String, usize)],
3046        pattern: &str,
3047    ) -> Vec<(usize, String)> {
3048        let pattern_lower = pattern.to_lowercase();
3049        let mut matches = Vec::new();
3050
3051        for (name, index) in columns {
3052            if name.to_lowercase().contains(&pattern_lower) {
3053                matches.push((*index, name.clone()));
3054            }
3055        }
3056
3057        let mut column_search = self.column_search.borrow_mut();
3058        column_search.set_matches(matches.clone());
3059
3060        if let Some(ref debug_service) = *self.debug_service.borrow() {
3061            debug_service.info(
3062                "ColumnSearch",
3063                format!(
3064                    "Found {} columns matching '{}': {:?}",
3065                    matches.len(),
3066                    pattern,
3067                    matches.iter().map(|(_, name)| name).collect::<Vec<_>>()
3068                ),
3069            );
3070        }
3071
3072        matches
3073    }
3074
3075    /// Navigate to next column match
3076    pub fn next_column_match(&self) -> Option<(usize, String)> {
3077        let mut column_search = self.column_search.borrow_mut();
3078        if let Some((idx, name)) = column_search.next_match() {
3079            let current = column_search.current_match;
3080            let total = column_search.matching_columns.len();
3081
3082            if let Some(ref debug_service) = *self.debug_service.borrow() {
3083                debug_service.info(
3084                    "ColumnSearch",
3085                    format!(
3086                        "Navigate to next column: {}/{} - '{}' (index {})",
3087                        current + 1,
3088                        total,
3089                        name,
3090                        idx
3091                    ),
3092                );
3093            }
3094
3095            Some((idx, name))
3096        } else {
3097            None
3098        }
3099    }
3100
3101    /// Navigate to previous column match
3102    pub fn previous_column_match(&self) -> Option<(usize, String)> {
3103        let mut column_search = self.column_search.borrow_mut();
3104        if let Some((idx, name)) = column_search.prev_match() {
3105            let current = column_search.current_match;
3106            let total = column_search.matching_columns.len();
3107
3108            if let Some(ref debug_service) = *self.debug_service.borrow() {
3109                debug_service.info(
3110                    "ColumnSearch",
3111                    format!(
3112                        "Navigate to previous column: {}/{} - '{}' (index {})",
3113                        current + 1,
3114                        total,
3115                        name,
3116                        idx
3117                    ),
3118                );
3119            }
3120
3121            Some((idx, name))
3122        } else {
3123            None
3124        }
3125    }
3126
3127    /// Clear column search
3128    pub fn clear_column_search(&self) {
3129        let mut column_search = self.column_search.borrow_mut();
3130        let had_matches = column_search.matching_columns.len();
3131        let had_pattern = column_search.pattern.clone();
3132
3133        column_search.clear();
3134
3135        if let Some(ref debug_service) = *self.debug_service.borrow() {
3136            debug_service.info(
3137                "ColumnSearch",
3138                format!(
3139                    "Cleared column search (had pattern: '{had_pattern}', {had_matches} matches)"
3140                ),
3141            );
3142        }
3143    }
3144
3145    /// Accept current column match
3146    pub fn accept_column_match(&self) -> Option<(usize, String)> {
3147        let column_search = self.column_search.borrow();
3148        if let Some((idx, name)) = column_search.current_match() {
3149            if let Some(ref debug_service) = *self.debug_service.borrow() {
3150                debug_service.info(
3151                    "ColumnSearch",
3152                    format!("Accepted column: '{name}' at index {idx}"),
3153                );
3154            }
3155            Some((idx, name))
3156        } else {
3157            None
3158        }
3159    }
3160
3161    // Sort operations with logging
3162
3163    /// Sort by column
3164    pub fn sort_by_column(&self, column_index: usize, column_name: String, row_count: usize) {
3165        let mut sort_state = self.sort.borrow_mut();
3166
3167        // Get the next sort order for this column
3168        let new_order = sort_state.get_next_order(column_index);
3169
3170        let old_column = sort_state.column;
3171        let old_order = sort_state.order;
3172
3173        if new_order == SortOrder::None {
3174            // Clear sort - return to original order
3175            sort_state.clear_sort();
3176
3177            if let Some(ref debug_service) = *self.debug_service.borrow() {
3178                debug_service.info(
3179                    "Sort",
3180                    format!(
3181                        "Cleared sort on column {column_index} ({column_name}), returning to original order"
3182                    ),
3183                );
3184            }
3185        } else {
3186            // Apply sort
3187            sort_state.set_sort(column_index, column_name.clone(), new_order, row_count);
3188
3189            if let Some(ref debug_service) = *self.debug_service.borrow() {
3190                debug_service.info(
3191                    "Sort",
3192                    format!(
3193                        "Sorted column {} ({}) {}, {} rows (was: column {:?} {})",
3194                        column_index,
3195                        column_name,
3196                        match new_order {
3197                            SortOrder::Ascending => "ascending ↑",
3198                            SortOrder::Descending => "descending ↓",
3199                            SortOrder::None => "none",
3200                        },
3201                        row_count,
3202                        old_column,
3203                        match old_order {
3204                            SortOrder::Ascending => "↑",
3205                            SortOrder::Descending => "↓",
3206                            SortOrder::None => "-",
3207                        }
3208                    ),
3209                );
3210            }
3211        }
3212    }
3213
3214    /// Clear all sorting
3215    pub fn clear_sort(&self) {
3216        let mut sort_state = self.sort.borrow_mut();
3217        let had_sort = sort_state.column.is_some();
3218        let old_column = sort_state.column;
3219        let old_name = sort_state.column_name.clone();
3220
3221        sort_state.clear_sort();
3222
3223        if had_sort {
3224            if let Some(ref debug_service) = *self.debug_service.borrow() {
3225                debug_service.info(
3226                    "Sort",
3227                    format!(
3228                        "Cleared all sorting (was: column {:?} - {})",
3229                        old_column,
3230                        old_name.unwrap_or_else(|| "unknown".to_string())
3231                    ),
3232                );
3233            }
3234        }
3235    }
3236
3237    /// Get current sort state
3238    pub fn sort(&self) -> std::cell::Ref<SortState> {
3239        self.sort.borrow()
3240    }
3241
3242    /// Get next sort order for a column
3243    pub fn get_next_sort_order(&self, column_index: usize) -> SortOrder {
3244        self.sort.borrow().get_next_order(column_index)
3245    }
3246
3247    /// Advance the sort state for a column
3248    pub fn advance_sort_state(
3249        &self,
3250        column_index: usize,
3251        column_name: Option<String>,
3252        new_order: SortOrder,
3253    ) {
3254        self.sort
3255            .borrow_mut()
3256            .advance_sort_state(column_index, column_name, new_order);
3257    }
3258
3259    /// Get current selection state (read-only) - DEPRECATED: Will be removed after migration
3260    pub fn selection(&self) -> std::cell::Ref<SelectionState> {
3261        self.selection.borrow()
3262    }
3263
3264    /// Get current selection state (mutable) - DEPRECATED: Will be removed after migration
3265    pub fn selection_mut(&self) -> std::cell::RefMut<SelectionState> {
3266        self.selection.borrow_mut()
3267    }
3268
3269    /// NEW: Proxy-based selection access (single source of truth)
3270    pub fn selection_proxy(&self) -> SelectionProxy {
3271        SelectionProxy::new(self.buffers.current())
3272    }
3273
3274    pub fn selection_proxy_mut(&mut self) -> SelectionProxyMut {
3275        SelectionProxyMut::new(self.buffers.current_mut())
3276    }
3277
3278    /// Set selection mode
3279    pub fn set_selection_mode(&self, mode: SelectionMode) {
3280        let mut selection = self.selection.borrow_mut();
3281        let old_mode = selection.mode.clone();
3282        selection.set_mode(mode.clone());
3283
3284        if old_mode != mode {
3285            if let Some(ref debug_service) = *self.debug_service.borrow() {
3286                debug_service.info(
3287                    "Selection",
3288                    format!("Mode changed: {old_mode:?} → {mode:?}"),
3289                );
3290            }
3291        }
3292    }
3293
3294    /// Select a row
3295    pub fn select_row(&self, row: Option<usize>) {
3296        let mut selection = self.selection.borrow_mut();
3297        let old_row = selection.selected_row;
3298        selection.select_row(row);
3299
3300        if old_row != row {
3301            if let Some(ref debug_service) = *self.debug_service.borrow() {
3302                debug_service.info("Selection", format!("Row selection: {old_row:?} → {row:?}"));
3303            }
3304        }
3305    }
3306
3307    /// Select a column
3308    pub fn select_column(&self, column: usize) {
3309        let mut selection = self.selection.borrow_mut();
3310        let old_column = selection.selected_column;
3311        selection.select_column(column);
3312
3313        if old_column != column {
3314            if let Some(ref debug_service) = *self.debug_service.borrow() {
3315                debug_service.info(
3316                    "Selection",
3317                    format!("Column selection: {old_column} → {column}"),
3318                );
3319            }
3320        }
3321    }
3322
3323    /// Select a cell
3324    pub fn select_cell(&self, row: usize, column: usize) {
3325        self.selection.borrow_mut().select_cell(row, column);
3326
3327        if let Some(ref debug_service) = *self.debug_service.borrow() {
3328            debug_service.info("Selection", format!("Cell selected: [{row}, {column}]"));
3329        }
3330    }
3331
3332    /// Toggle selection mode between Row/Cell/Column
3333    pub fn toggle_selection_mode(&self) {
3334        let mut selection = self.selection.borrow_mut();
3335        let new_mode = match selection.mode {
3336            SelectionMode::Row => SelectionMode::Cell,
3337            SelectionMode::Cell => SelectionMode::Column,
3338            SelectionMode::Column => SelectionMode::Row,
3339        };
3340        let old_mode = selection.mode.clone();
3341        selection.set_mode(new_mode.clone());
3342
3343        if let Some(ref debug_service) = *self.debug_service.borrow() {
3344            debug_service.info(
3345                "Selection",
3346                format!("Mode toggled: {old_mode:?} → {new_mode:?}"),
3347            );
3348        }
3349    }
3350
3351    /// Clear all selections
3352    pub fn clear_selections(&self) {
3353        let mut selection = self.selection.borrow_mut();
3354        let had_selections = !selection.selected_cells.is_empty();
3355        selection.clear_selections();
3356
3357        if had_selections {
3358            if let Some(ref debug_service) = *self.debug_service.borrow() {
3359                debug_service.info("Selection", "Cleared all selections".to_string());
3360            }
3361        }
3362    }
3363
3364    /// Get current selection mode
3365    pub fn get_selection_mode(&self) -> SelectionMode {
3366        self.selection.borrow().mode.clone()
3367    }
3368
3369    /// Get selected row
3370    pub fn get_selected_row(&self) -> Option<usize> {
3371        self.selection.borrow().selected_row
3372    }
3373
3374    /// Get selected column
3375    pub fn get_selected_column(&self) -> usize {
3376        self.selection.borrow().selected_column
3377    }
3378
3379    /// Get the current selected position from `NavigationState`
3380    /// This is the primary source of truth for cursor position
3381    pub fn get_current_position(&self) -> (usize, usize) {
3382        let nav = self.navigation.borrow();
3383        (nav.selected_row, nav.selected_column)
3384    }
3385
3386    /// Sync selection state with navigation position
3387    /// Called when navigation changes to update selection tracking
3388    pub fn sync_selection_with_navigation(&self) {
3389        let nav = self.navigation.borrow();
3390        let mut selection = self.selection.borrow_mut();
3391
3392        // Update selection state to match navigation
3393        selection.selected_row = Some(nav.selected_row);
3394        selection.selected_column = nav.selected_column;
3395        selection.last_selection_time = Some(Instant::now());
3396        selection.total_selections += 1;
3397    }
3398
3399    /// Handle yank chord based on selection mode
3400    /// Returns the action taken for status messaging
3401    pub fn handle_yank_by_mode(&self) -> Option<String> {
3402        let mode = self.get_selection_mode();
3403        let (_row, _col) = self.get_current_position();
3404
3405        match mode {
3406            SelectionMode::Cell => {
3407                // In cell mode, single 'y' yanks the cell
3408                Some("yank_cell".to_string())
3409            }
3410            SelectionMode::Row => {
3411                // In row mode, this starts a chord sequence
3412                None // Chord handler will process
3413            }
3414            SelectionMode::Column => {
3415                // In column mode, single 'y' yanks the column
3416                Some("yank_column".to_string())
3417            }
3418        }
3419    }
3420
3421    /// Get the selected row for table widget (ratatui compatibility)
3422    pub fn get_table_selected_row(&self) -> Option<usize> {
3423        let nav = self.navigation.borrow();
3424        // Only return Some if we have actual data
3425        if nav.total_rows > 0 {
3426            Some(nav.selected_row)
3427        } else {
3428            // Debug logging to understand why this returns None
3429            tracing::debug!(
3430                "get_table_selected_row returning None: total_rows={}, selected_row={}",
3431                nav.total_rows,
3432                nav.selected_row
3433            );
3434            None
3435        }
3436    }
3437
3438    /// Set the selected row (updates navigation state)
3439    pub fn set_table_selected_row(&self, row: Option<usize>) {
3440        if let Some(row) = row {
3441            let mut nav = self.navigation.borrow_mut();
3442            if row < nav.total_rows {
3443                let old_row = nav.selected_row;
3444                let column = nav.selected_column;
3445                nav.selected_row = row;
3446                nav.add_to_history(row, column);
3447
3448                if let Some(ref debug_service) = *self.debug_service.borrow() {
3449                    debug_service.info(
3450                        "Navigation",
3451                        format!("Table row selected: {old_row} → {row}"),
3452                    );
3453                }
3454            }
3455        }
3456        // Sync selection state
3457        self.sync_selection_with_navigation();
3458    }
3459
3460    /// Get the current column index
3461    pub fn get_current_column(&self) -> usize {
3462        self.navigation.borrow().selected_column
3463    }
3464
3465    /// Set the current column index
3466    pub fn set_current_column(&self, column: usize) {
3467        let mut nav = self.navigation.borrow_mut();
3468        if column < nav.total_columns {
3469            let old_col = nav.selected_column;
3470            let row = nav.selected_row;
3471            nav.selected_column = column;
3472            nav.add_to_history(row, column);
3473
3474            // Ensure the column is visible in the viewport
3475            nav.ensure_visible(row, column);
3476
3477            if let Some(ref debug_service) = *self.debug_service.borrow() {
3478                debug_service.info(
3479                    "Navigation",
3480                    format!("Column selected: {old_col} → {column}"),
3481                );
3482            }
3483        }
3484        // Sync selection state
3485        drop(nav); // Release borrow before calling sync
3486        self.sync_selection_with_navigation();
3487    }
3488
3489    // Tab completion operations
3490    pub fn completion(&self) -> std::cell::Ref<'_, CompletionState> {
3491        self.completion.borrow()
3492    }
3493
3494    pub fn completion_mut(&self) -> std::cell::RefMut<'_, CompletionState> {
3495        self.completion.borrow_mut()
3496    }
3497
3498    pub fn clear_completion(&self) {
3499        let mut completion = self.completion.borrow_mut();
3500        let had_suggestions = completion.suggestions.len();
3501        completion.clear();
3502
3503        if had_suggestions > 0 {
3504            if let Some(ref debug_service) = *self.debug_service.borrow() {
3505                debug_service.info(
3506                    "Completion",
3507                    format!("Cleared {had_suggestions} suggestions"),
3508                );
3509            }
3510        }
3511    }
3512
3513    pub fn set_completion_suggestions(&self, suggestions: Vec<String>) {
3514        let mut completion = self.completion.borrow_mut();
3515        let count = suggestions.len();
3516        completion.set_suggestions(suggestions);
3517
3518        if count > 0 {
3519            if let Some(ref debug_service) = *self.debug_service.borrow() {
3520                debug_service.info("Completion", format!("Set {count} completion suggestions"));
3521            }
3522        }
3523    }
3524
3525    pub fn next_completion(&self) {
3526        let mut completion = self.completion.borrow_mut();
3527        if !completion.suggestions.is_empty() {
3528            completion.next_suggestion();
3529
3530            if let Some(ref debug_service) = *self.debug_service.borrow() {
3531                if let Some(current) = completion.current_suggestion() {
3532                    debug_service.info(
3533                        "Completion",
3534                        format!(
3535                            "Cycling to suggestion {}/{}: {}",
3536                            completion.current_index + 1,
3537                            completion.suggestions.len(),
3538                            current
3539                        ),
3540                    );
3541                }
3542            }
3543        }
3544    }
3545
3546    pub fn get_current_completion(&self) -> Option<String> {
3547        self.completion.borrow().current_suggestion().cloned()
3548    }
3549
3550    pub fn is_completion_active(&self) -> bool {
3551        self.completion.borrow().is_active
3552    }
3553
3554    pub fn update_completion_context(&self, query: String, cursor_pos: usize) {
3555        self.completion
3556            .borrow_mut()
3557            .update_context(query, cursor_pos);
3558    }
3559
3560    pub fn is_same_completion_context(&self, query: &str, cursor_pos: usize) -> bool {
3561        self.completion.borrow().is_same_context(query, cursor_pos)
3562    }
3563
3564    // History search operations (Ctrl+R)
3565    pub fn start_history_search(&self, original_input: String) {
3566        info!(
3567            target: "history",
3568            "Starting history search with original input: '{}'",
3569            original_input
3570        );
3571
3572        let mut history_search = self.history_search.borrow_mut();
3573        history_search.query.clear();
3574        history_search.matches.clear();
3575        history_search.selected_index = 0;
3576        history_search.is_active = true;
3577        history_search.original_input = original_input.clone();
3578
3579        // Initialize with all history entries
3580        let history = self.command_history.borrow();
3581        let all_entries = history.get_all();
3582        info!(
3583            target: "history",
3584            "Loaded {} history entries for search",
3585            all_entries.len()
3586        );
3587
3588        // Log the last few entries to verify they're there (showing in display order - newest first)
3589        if !all_entries.is_empty() {
3590            let recent_count = std::cmp::min(5, all_entries.len());
3591            info!(target: "history", "Most recent {} entries (newest first):", recent_count);
3592            // Show in reverse order since we display newest first
3593            for (i, entry) in all_entries.iter().rev().take(recent_count).enumerate() {
3594                info!(target: "history", "  [{}] '{}'", i, entry.command);
3595            }
3596        }
3597
3598        // Show entries in reverse order (most recent first)
3599        history_search.matches = all_entries
3600            .iter()
3601            .rev() // Most recent first
3602            .cloned()
3603            .map(|entry| crate::history::HistoryMatch {
3604                entry,
3605                indices: Vec::new(),
3606                score: 0,
3607            })
3608            .collect();
3609
3610        eprintln!(
3611            "[DEBUG] Created {} matches in history_search",
3612            history_search.matches.len()
3613        );
3614
3615        if let Some(ref debug_service) = *self.debug_service.borrow() {
3616            debug_service.info(
3617                "HistorySearch",
3618                format!(
3619                    "Started history search with {} entries",
3620                    history_search.matches.len()
3621                ),
3622            );
3623        }
3624    }
3625
3626    pub fn update_history_search(&self, query: String) {
3627        let mut history_search = self.history_search.borrow_mut();
3628        let old_query = history_search.query.clone();
3629        history_search.query = query.clone();
3630
3631        if query.is_empty() {
3632            // Show all history when no search
3633            let history = self.command_history.borrow();
3634            let all_entries = history.get_all();
3635            history_search.matches = all_entries
3636                .iter()
3637                .cloned()
3638                .map(|entry| crate::history::HistoryMatch {
3639                    entry,
3640                    indices: Vec::new(),
3641                    score: 0,
3642                })
3643                .collect();
3644        } else {
3645            // Use fuzzy search
3646            use fuzzy_matcher::skim::SkimMatcherV2;
3647            use fuzzy_matcher::FuzzyMatcher;
3648
3649            let matcher = SkimMatcherV2::default();
3650            let history = self.command_history.borrow();
3651            let mut matches: Vec<crate::history::HistoryMatch> = history
3652                .get_all()
3653                .iter()
3654                .cloned()
3655                .filter_map(|entry| {
3656                    matcher
3657                        .fuzzy_indices(&entry.command, &query)
3658                        .map(|(score, indices)| crate::history::HistoryMatch {
3659                            entry,
3660                            score,
3661                            indices,
3662                        })
3663                })
3664                .collect();
3665
3666            // Sort by score (highest first)
3667            matches.sort_by(|a, b| b.score.cmp(&a.score));
3668            history_search.matches = matches;
3669        }
3670
3671        // Reset selected index if it's out of bounds
3672        if history_search.selected_index >= history_search.matches.len() {
3673            history_search.selected_index = 0;
3674        }
3675
3676        if let Some(ref debug_service) = *self.debug_service.borrow() {
3677            debug_service.info(
3678                "HistorySearch",
3679                format!(
3680                    "Updated history search: '{}' -> '{}', {} matches",
3681                    old_query,
3682                    query,
3683                    history_search.matches.len()
3684                ),
3685            );
3686        }
3687    }
3688
3689    /// Update history search with schema context (columns and source)
3690    pub fn update_history_search_with_schema(
3691        &self,
3692        query: String,
3693        columns: &[String],
3694        source: Option<&str>,
3695    ) {
3696        let mut history_search = self.history_search.borrow_mut();
3697        let old_query = history_search.query.clone();
3698        let old_matches_count = history_search.matches.len();
3699
3700        history_search.query = query.clone();
3701
3702        // Use the schema-aware search from command_history
3703        history_search.matches = self
3704            .command_history
3705            .borrow()
3706            .search_with_schema(&query, columns, source);
3707
3708        // Reset selected index
3709        history_search.selected_index = 0;
3710
3711        if let Some(ref debug_service) = *self.debug_service.borrow() {
3712            debug_service.info(
3713                "HistorySearch",
3714                format!(
3715                    "Updated history search with schema: '{}' -> '{}', matches: {} -> {}, columns: {}, source: {:?}",
3716                    old_query,
3717                    query,
3718                    old_matches_count,
3719                    history_search.matches.len(),
3720                    columns.len(),
3721                    source
3722                ),
3723            );
3724        }
3725    }
3726
3727    /// Handle character input during history search
3728    pub fn history_search_add_char(&self, c: char) {
3729        let mut history_search = self.history_search.borrow_mut();
3730        let old_query = history_search.query.clone();
3731        history_search.query.push(c);
3732
3733        if let Some(ref debug_service) = *self.debug_service.borrow() {
3734            debug_service.info(
3735                "HistorySearch",
3736                format!(
3737                    "Added char '{}': '{}' -> '{}'",
3738                    c, old_query, history_search.query
3739                ),
3740            );
3741        }
3742    }
3743
3744    /// Handle backspace during history search
3745    pub fn history_search_backspace(&self) {
3746        let mut history_search = self.history_search.borrow_mut();
3747        let old_query = history_search.query.clone();
3748        history_search.query.pop();
3749
3750        if let Some(ref debug_service) = *self.debug_service.borrow() {
3751            debug_service.info(
3752                "HistorySearch",
3753                format!("Backspace: '{}' -> '{}'", old_query, history_search.query),
3754            );
3755        }
3756    }
3757
3758    pub fn history_search_next(&self) {
3759        let mut history_search = self.history_search.borrow_mut();
3760        if !history_search.matches.is_empty() {
3761            let old_index = history_search.selected_index;
3762            history_search.selected_index =
3763                (history_search.selected_index + 1) % history_search.matches.len();
3764
3765            if let Some(ref debug_service) = *self.debug_service.borrow() {
3766                debug_service.info(
3767                    "HistorySearch",
3768                    format!(
3769                        "Navigate next: {} -> {}",
3770                        old_index, history_search.selected_index
3771                    ),
3772                );
3773            }
3774        }
3775    }
3776
3777    pub fn history_search_previous(&self) {
3778        let mut history_search = self.history_search.borrow_mut();
3779        if !history_search.matches.is_empty() {
3780            let old_index = history_search.selected_index;
3781            history_search.selected_index = if history_search.selected_index == 0 {
3782                history_search.matches.len() - 1
3783            } else {
3784                history_search.selected_index - 1
3785            };
3786
3787            if let Some(ref debug_service) = *self.debug_service.borrow() {
3788                debug_service.info(
3789                    "HistorySearch",
3790                    format!(
3791                        "Navigate previous: {} -> {}",
3792                        old_index, history_search.selected_index
3793                    ),
3794                );
3795            }
3796        }
3797    }
3798
3799    pub fn get_selected_history_command(&self) -> Option<String> {
3800        let history_search = self.history_search.borrow();
3801        history_search
3802            .matches
3803            .get(history_search.selected_index)
3804            .map(|m| m.entry.command.clone())
3805    }
3806
3807    pub fn accept_history_search(&self) -> Option<String> {
3808        let mut history_search = self.history_search.borrow_mut();
3809        if history_search.is_active {
3810            let command = history_search
3811                .matches
3812                .get(history_search.selected_index)
3813                .map(|m| m.entry.command.clone());
3814
3815            if let Some(ref debug_service) = *self.debug_service.borrow() {
3816                debug_service.info(
3817                    "HistorySearch",
3818                    format!("Accepted history command: {command:?}"),
3819                );
3820            }
3821
3822            history_search.clear();
3823            command
3824        } else {
3825            None
3826        }
3827    }
3828
3829    pub fn cancel_history_search(&self) -> String {
3830        let mut history_search = self.history_search.borrow_mut();
3831        let original = history_search.original_input.clone();
3832
3833        if let Some(ref debug_service) = *self.debug_service.borrow() {
3834            debug_service.info(
3835                "HistorySearch",
3836                format!("Cancelled history search, restoring: '{original}'"),
3837            );
3838        }
3839
3840        history_search.clear();
3841        original
3842    }
3843
3844    pub fn history_search(&self) -> std::cell::Ref<'_, HistorySearchState> {
3845        self.history_search.borrow()
3846    }
3847
3848    pub fn is_history_search_active(&self) -> bool {
3849        self.history_search.borrow().is_active
3850    }
3851
3852    // Navigation operations with logging (V16 implementation)
3853    pub fn navigate_to(&self, row: usize, col: usize) {
3854        let mut navigation = self.navigation.borrow_mut();
3855        let old_row = navigation.selected_row;
3856        let old_col = navigation.selected_column;
3857
3858        // Update position
3859        navigation.selected_row = row.min(navigation.total_rows.saturating_sub(1));
3860        navigation.selected_column = col.min(navigation.total_columns.saturating_sub(1));
3861
3862        let new_row = navigation.selected_row;
3863        let new_col = navigation.selected_column;
3864
3865        // Add to history
3866        navigation.add_to_history(new_row, new_col);
3867
3868        // Ensure position is visible
3869        navigation.ensure_visible(new_row, new_col);
3870
3871        let scroll_offset = navigation.scroll_offset;
3872        drop(navigation);
3873
3874        if let Some(ref debug_service) = *self.debug_service.borrow() {
3875            debug_service.log(
3876                "Navigation",
3877                DebugLevel::Info,
3878                format!(
3879                    "Navigate: ({old_row}, {old_col}) -> ({new_row}, {new_col}), scroll: {scroll_offset:?}"
3880                ),
3881                Some("navigate_to".to_string()),
3882            );
3883        }
3884    }
3885
3886    pub fn navigate_relative(&self, delta_row: i32, delta_col: i32) {
3887        let navigation = self.navigation.borrow();
3888        let current_row = navigation.selected_row;
3889        let current_col = navigation.selected_column;
3890        drop(navigation);
3891
3892        let new_row = if delta_row >= 0 {
3893            current_row.saturating_add(delta_row as usize)
3894        } else {
3895            current_row.saturating_sub(delta_row.unsigned_abs() as usize)
3896        };
3897
3898        let new_col = if delta_col >= 0 {
3899            current_col.saturating_add(delta_col as usize)
3900        } else {
3901            current_col.saturating_sub(delta_col.unsigned_abs() as usize)
3902        };
3903
3904        self.navigate_to(new_row, new_col);
3905    }
3906
3907    pub fn navigate_to_row(&self, row: usize) {
3908        let navigation = self.navigation.borrow();
3909        let current_col = navigation.selected_column;
3910        drop(navigation);
3911
3912        if let Some(ref debug_service) = *self.debug_service.borrow() {
3913            debug_service.log(
3914                "Navigation",
3915                DebugLevel::Info,
3916                format!("Jump to row: {row}"),
3917                Some("navigate_to_row".to_string()),
3918            );
3919        }
3920
3921        self.navigate_to(row, current_col);
3922    }
3923
3924    pub fn navigate_to_column(&self, col: usize) {
3925        let navigation = self.navigation.borrow();
3926        let current_row = navigation.selected_row;
3927        drop(navigation);
3928
3929        if let Some(ref debug_service) = *self.debug_service.borrow() {
3930            debug_service.log(
3931                "Navigation",
3932                DebugLevel::Info,
3933                format!("Jump to column: {col}"),
3934                Some("navigate_to_column".to_string()),
3935            );
3936        }
3937
3938        self.navigate_to(current_row, col);
3939    }
3940
3941    pub fn update_data_size(&self, rows: usize, columns: usize) {
3942        let mut navigation = self.navigation.borrow_mut();
3943        let old_totals = (navigation.total_rows, navigation.total_columns);
3944        navigation.update_totals(rows, columns);
3945
3946        if let Some(ref debug_service) = *self.debug_service.borrow() {
3947            debug_service.log(
3948                "Navigation",
3949                DebugLevel::Info,
3950                format!(
3951                    "Data size updated: {:?} -> ({}, {}), position: ({}, {})",
3952                    old_totals, rows, columns, navigation.selected_row, navigation.selected_column
3953                ),
3954                Some("update_data_size".to_string()),
3955            );
3956        }
3957    }
3958
3959    pub fn set_viewport_size(&self, rows: usize, columns: usize) {
3960        let mut navigation = self.navigation.borrow_mut();
3961        let old_viewport = (navigation.viewport_rows, navigation.viewport_columns);
3962        let selected_row = navigation.selected_row;
3963        let selected_column = navigation.selected_column;
3964
3965        navigation.set_viewport_size(rows, columns);
3966
3967        // Ensure current position is still visible with new viewport
3968        navigation.ensure_visible(selected_row, selected_column);
3969
3970        let scroll_offset = navigation.scroll_offset;
3971        drop(navigation);
3972
3973        if let Some(ref debug_service) = *self.debug_service.borrow() {
3974            debug_service.log(
3975                "Navigation",
3976                DebugLevel::Info,
3977                format!(
3978                    "Viewport size updated: {old_viewport:?} -> ({rows}, {columns}), scroll adjusted: {scroll_offset:?}"
3979                ),
3980                Some("set_viewport_size".to_string()),
3981            );
3982        }
3983    }
3984
3985    pub fn toggle_viewport_lock(&self) {
3986        let mut navigation = self.navigation.borrow_mut();
3987        navigation.viewport_lock = !navigation.viewport_lock;
3988
3989        if navigation.viewport_lock {
3990            navigation.viewport_lock_row = Some(navigation.selected_row);
3991        } else {
3992            navigation.viewport_lock_row = None;
3993        }
3994
3995        if let Some(ref debug_service) = *self.debug_service.borrow() {
3996            debug_service.log(
3997                "Navigation",
3998                DebugLevel::Info,
3999                format!(
4000                    "Viewport lock: {} at row {:?}",
4001                    navigation.viewport_lock, navigation.viewport_lock_row
4002                ),
4003                Some("toggle_viewport_lock".to_string()),
4004            );
4005        }
4006    }
4007
4008    pub fn toggle_cursor_lock(&self) {
4009        let mut navigation = self.navigation.borrow_mut();
4010        navigation.cursor_lock = !navigation.cursor_lock;
4011
4012        if navigation.cursor_lock {
4013            // Calculate visual position (position within viewport)
4014            let visual_position = navigation
4015                .selected_row
4016                .saturating_sub(navigation.scroll_offset.0);
4017            navigation.cursor_lock_position = Some(visual_position);
4018        } else {
4019            navigation.cursor_lock_position = None;
4020        }
4021
4022        if let Some(ref debug_service) = *self.debug_service.borrow() {
4023            debug_service.log(
4024                "Navigation",
4025                DebugLevel::Info,
4026                format!(
4027                    "Cursor lock: {} at visual position {:?}",
4028                    navigation.cursor_lock, navigation.cursor_lock_position
4029                ),
4030                Some("toggle_cursor_lock".to_string()),
4031            );
4032        }
4033    }
4034
4035    pub fn is_cursor_locked(&self) -> bool {
4036        self.navigation.borrow().cursor_lock
4037    }
4038
4039    // Navigation state access - DEPRECATED: Will be removed after migration
4040    pub fn navigation(&self) -> std::cell::Ref<'_, NavigationState> {
4041        self.navigation.borrow()
4042    }
4043
4044    pub fn navigation_mut(&self) -> std::cell::RefMut<'_, NavigationState> {
4045        self.navigation.borrow_mut()
4046    }
4047
4048    // NEW: Proxy-based navigation access (single source of truth)
4049    pub fn navigation_proxy(&self) -> NavigationProxy {
4050        NavigationProxy::new(self.buffers.current())
4051    }
4052
4053    pub fn navigation_proxy_mut(&mut self) -> NavigationProxyMut {
4054        NavigationProxyMut::new(self.buffers.current_mut())
4055    }
4056
4057    // get_current_position is defined earlier in the file at line 2946
4058
4059    pub fn get_scroll_offset(&self) -> (usize, usize) {
4060        self.navigation.borrow().scroll_offset
4061    }
4062
4063    pub fn is_viewport_locked(&self) -> bool {
4064        self.navigation.borrow().viewport_lock
4065    }
4066
4067    // Results state methods (V17 implementation)
4068    /// Set query results with comprehensive logging and performance tracking
4069    pub fn set_results(
4070        &self,
4071        results: QueryResponse,
4072        execution_time: Duration,
4073        from_cache: bool,
4074    ) -> Result<()> {
4075        let query_text = results.query.select.join(", ");
4076        let row_count = results.count;
4077
4078        if let Some(ref debug_service) = *self.debug_service.borrow() {
4079            debug_service.log(
4080                "ResultsState",
4081                DebugLevel::Info,
4082                format!(
4083                    "[RESULTS] Setting results: query='{}', rows={}, time={}ms, cached={}",
4084                    query_text.chars().take(50).collect::<String>(),
4085                    row_count,
4086                    execution_time.as_millis(),
4087                    from_cache
4088                ),
4089                Some("set_results".to_string()),
4090            );
4091        }
4092
4093        self.results
4094            .borrow_mut()
4095            .set_results(results, execution_time, from_cache)?;
4096
4097        if let Some(ref debug_service) = *self.debug_service.borrow() {
4098            let stats = self.results.borrow().get_performance_stats();
4099            debug_service.log(
4100                "ResultsState",
4101                DebugLevel::Info,
4102                format!(
4103                    "[RESULTS] Performance stats: total_queries={}, cache_hit_rate={:.2}%, avg_time={:.2}ms",
4104                    stats.total_queries,
4105                    stats.cache_hit_rate * 100.0,
4106                    stats.average_execution_time_ms
4107                ),
4108                Some("performance_stats".to_string()),
4109            );
4110        }
4111
4112        Ok(())
4113    }
4114
4115    /// Get current query results
4116    pub fn get_results(&self) -> Option<QueryResponse> {
4117        self.results.borrow().get_results().cloned()
4118    }
4119
4120    /// Cache query results with logging
4121    pub fn cache_results(&self, query_key: String, results: QueryResponse) -> Result<()> {
4122        if let Some(ref debug_service) = *self.debug_service.borrow() {
4123            debug_service.log(
4124                "ResultsCache",
4125                DebugLevel::Info,
4126                format!(
4127                    "[RESULTS] Caching results: key='{}', rows={}",
4128                    query_key.chars().take(30).collect::<String>(),
4129                    results.count
4130                ),
4131                Some("cache_results".to_string()),
4132            );
4133        }
4134
4135        let result = self
4136            .results
4137            .borrow_mut()
4138            .cache_results(query_key.clone(), results);
4139
4140        if let Some(ref debug_service) = *self.debug_service.borrow() {
4141            let cache_stats = self.results.borrow().get_cache_stats();
4142            debug_service.log(
4143                "ResultsCache",
4144                DebugLevel::Info,
4145                format!(
4146                    "[RESULTS] Cache stats: entries={}, memory={}MB, hit_rate={:.2}%",
4147                    cache_stats.entry_count,
4148                    cache_stats.memory_usage / (1024 * 1024),
4149                    cache_stats.hit_rate * 100.0
4150                ),
4151                Some("cache_stats".to_string()),
4152            );
4153        }
4154
4155        result
4156    }
4157
4158    /// Get cached results with access tracking
4159    pub fn get_cached_results(&self, query_key: &str) -> Option<QueryResponse> {
4160        if let Some(result) = self.results.borrow_mut().get_cached_results(query_key) {
4161            if let Some(ref debug_service) = *self.debug_service.borrow() {
4162                debug_service.log(
4163                    "ResultsCache",
4164                    DebugLevel::Trace,
4165                    format!(
4166                        "[RESULTS] Cache HIT for key: '{}'",
4167                        query_key.chars().take(30).collect::<String>()
4168                    ),
4169                    Some("cache_hit".to_string()),
4170                );
4171            }
4172            Some(result.clone())
4173        } else {
4174            if let Some(ref debug_service) = *self.debug_service.borrow() {
4175                debug_service.log(
4176                    "ResultsCache",
4177                    DebugLevel::Trace,
4178                    format!(
4179                        "[RESULTS] Cache MISS for key: '{}'",
4180                        query_key.chars().take(30).collect::<String>()
4181                    ),
4182                    Some("cache_miss".to_string()),
4183                );
4184            }
4185            None
4186        }
4187    }
4188
4189    /// Clear results cache
4190    pub fn clear_results_cache(&self) {
4191        let before_count = self.results.borrow().get_cache_stats().entry_count;
4192        self.results.borrow_mut().clear_cache();
4193
4194        if let Some(ref debug_service) = *self.debug_service.borrow() {
4195            debug_service.log(
4196                "ResultsCache",
4197                DebugLevel::Info,
4198                format!("[RESULTS] Cache cleared: removed {before_count} entries"),
4199                Some("clear_cache".to_string()),
4200            );
4201        }
4202    }
4203
4204    // Clipboard operations with logging
4205
4206    /// Get clipboard state (read-only)
4207    pub fn clipboard(&self) -> std::cell::Ref<'_, ClipboardState> {
4208        self.clipboard.borrow()
4209    }
4210
4211    /// Get clipboard state (mutable)
4212    pub fn clipboard_mut(&self) -> std::cell::RefMut<'_, ClipboardState> {
4213        self.clipboard.borrow_mut()
4214    }
4215
4216    // Chord operations
4217
4218    /// Get chord state (read-only)
4219    pub fn chord(&self) -> std::cell::Ref<'_, ChordState> {
4220        self.chord.borrow()
4221    }
4222
4223    /// Get chord state (mutable)
4224    pub fn chord_mut(&self) -> std::cell::RefMut<'_, ChordState> {
4225        self.chord.borrow_mut()
4226    }
4227
4228    // Undo/Redo operations
4229
4230    /// Get undo/redo state (read-only)
4231    pub fn undo_redo(&self) -> std::cell::Ref<'_, UndoRedoState> {
4232        self.undo_redo.borrow()
4233    }
4234
4235    /// Get undo/redo state (mutable)
4236    pub fn undo_redo_mut(&self) -> std::cell::RefMut<'_, UndoRedoState> {
4237        self.undo_redo.borrow_mut()
4238    }
4239
4240    // Scroll operations
4241
4242    /// Get scroll state (read-only)
4243    pub fn scroll(&self) -> std::cell::Ref<'_, ScrollState> {
4244        self.scroll.borrow()
4245    }
4246
4247    /// Get scroll state (mutable)
4248    pub fn scroll_mut(&self) -> std::cell::RefMut<'_, ScrollState> {
4249        self.scroll.borrow_mut()
4250    }
4251
4252    /// Yank a cell to clipboard
4253    pub fn yank_cell(
4254        &self,
4255        row: usize,
4256        column: usize,
4257        value: String,
4258        preview: String,
4259    ) -> Result<()> {
4260        let description = format!("cell at [{row}, {column}]");
4261        let size_bytes = value.len();
4262
4263        trace!(
4264            "yank_cell: Starting clipboard write for {} ({} bytes)",
4265            description,
4266            size_bytes
4267        );
4268        trace!(
4269            "yank_cell: Value preview: '{}'",
4270            if value.len() > 50 {
4271                &value[..50]
4272            } else {
4273                &value
4274            }
4275        );
4276
4277        // Copy to system clipboard
4278        let mut system_clipboard = Clipboard::new()?;
4279        trace!("yank_cell: Setting clipboard text ({} bytes)", value.len());
4280        system_clipboard.set_text(&value)?;
4281
4282        // Verify clipboard write
4283        let clipboard_content = system_clipboard.get_text().unwrap_or_default();
4284        if clipboard_content != value {
4285            return Err(anyhow!(
4286                "Clipboard write verification failed. Expected {} chars, wrote {} chars",
4287                value.len(),
4288                clipboard_content.len()
4289            ));
4290        }
4291
4292        let item = YankedItem {
4293            description: description.clone(),
4294            full_value: value.clone(),
4295            preview: preview.clone(),
4296            yank_type: YankType::Cell { row, column },
4297            yanked_at: Local::now(),
4298            size_bytes,
4299        };
4300
4301        self.clipboard.borrow_mut().add_yank(item);
4302
4303        if let Some(ref debug_service) = *self.debug_service.borrow() {
4304            debug_service.info(
4305                "Clipboard",
4306                format!(
4307                    "Yanked {}: '{}' ({} bytes)",
4308                    description,
4309                    if preview.len() > 50 {
4310                        format!("{}...", &preview[..50])
4311                    } else {
4312                        preview
4313                    },
4314                    size_bytes
4315                ),
4316            );
4317        }
4318
4319        Ok(())
4320    }
4321
4322    /// Yank a row to clipboard
4323    pub fn yank_row(&self, row: usize, value: String, preview: String) -> Result<()> {
4324        let description = format!("row {row}");
4325        let size_bytes = value.len();
4326
4327        // Copy to system clipboard
4328        let mut system_clipboard = Clipboard::new()?;
4329        system_clipboard.set_text(&value)?;
4330
4331        // Verify clipboard write
4332        let clipboard_content = system_clipboard.get_text().unwrap_or_default();
4333        if clipboard_content != value {
4334            return Err(anyhow!(
4335                "Clipboard write verification failed. Expected {} chars, wrote {} chars",
4336                value.len(),
4337                clipboard_content.len()
4338            ));
4339        }
4340
4341        let item = YankedItem {
4342            description: description.clone(),
4343            full_value: value.clone(),
4344            preview: preview.clone(),
4345            yank_type: YankType::Row { row },
4346            yanked_at: Local::now(),
4347            size_bytes,
4348        };
4349
4350        self.clipboard.borrow_mut().add_yank(item);
4351
4352        if let Some(ref debug_service) = *self.debug_service.borrow() {
4353            debug_service.info(
4354                "Clipboard",
4355                format!(
4356                    "Yanked {}: {} columns ({} bytes)",
4357                    description,
4358                    value.split('\t').count(),
4359                    size_bytes
4360                ),
4361            );
4362        }
4363
4364        Ok(())
4365    }
4366
4367    /// Yank a column to clipboard
4368    pub fn yank_column(
4369        &self,
4370        column_name: String,
4371        column_index: usize,
4372        value: String,
4373        preview: String,
4374    ) -> Result<()> {
4375        let description = format!("column '{column_name}'");
4376        let size_bytes = value.len();
4377        let row_count = value.lines().count();
4378
4379        // Copy to system clipboard
4380        let mut system_clipboard = Clipboard::new()?;
4381        system_clipboard.set_text(&value)?;
4382
4383        // Verify clipboard write
4384        let clipboard_content = system_clipboard.get_text().unwrap_or_default();
4385        if clipboard_content != value {
4386            return Err(anyhow!(
4387                "Clipboard write verification failed. Expected {} chars, wrote {} chars",
4388                value.len(),
4389                clipboard_content.len()
4390            ));
4391        }
4392
4393        let item = YankedItem {
4394            description: description.clone(),
4395            full_value: value.clone(),
4396            preview: preview.clone(),
4397            yank_type: YankType::Column {
4398                name: column_name.clone(),
4399                index: column_index,
4400            },
4401            yanked_at: Local::now(),
4402            size_bytes,
4403        };
4404
4405        self.clipboard.borrow_mut().add_yank(item);
4406
4407        if let Some(ref debug_service) = *self.debug_service.borrow() {
4408            debug_service.info(
4409                "Clipboard",
4410                format!("Yanked {description}: {row_count} rows ({size_bytes} bytes)"),
4411            );
4412        }
4413
4414        Ok(())
4415    }
4416
4417    /// Yank all data to clipboard
4418    pub fn yank_all(&self, value: String, preview: String) -> Result<()> {
4419        let size_bytes = value.len();
4420        let row_count = value.lines().count();
4421
4422        // Copy to system clipboard
4423        let mut system_clipboard = Clipboard::new()?;
4424        system_clipboard.set_text(&value)?;
4425
4426        // Verify clipboard write
4427        let clipboard_content = system_clipboard.get_text().unwrap_or_default();
4428        if clipboard_content != value {
4429            return Err(anyhow!(
4430                "Clipboard write verification failed. Expected {} chars, wrote {} chars",
4431                value.len(),
4432                clipboard_content.len()
4433            ));
4434        }
4435
4436        let item = YankedItem {
4437            description: "all data".to_string(),
4438            full_value: value.clone(),
4439            preview: preview.clone(),
4440            yank_type: YankType::All,
4441            yanked_at: Local::now(),
4442            size_bytes,
4443        };
4444
4445        self.clipboard.borrow_mut().add_yank(item);
4446
4447        if let Some(ref debug_service) = *self.debug_service.borrow() {
4448            debug_service.info(
4449                "Clipboard",
4450                format!("Yanked all data: {row_count} rows ({size_bytes} bytes)"),
4451            );
4452        }
4453
4454        Ok(())
4455    }
4456
4457    /// Yank a test case to clipboard
4458    pub fn yank_test_case(&self, value: String) -> Result<()> {
4459        let size_bytes = value.len();
4460        let line_count = value.lines().count();
4461
4462        // Copy to system clipboard
4463        let mut system_clipboard = Clipboard::new()?;
4464        system_clipboard.set_text(&value)?;
4465
4466        // Verify clipboard write
4467        let clipboard_content = system_clipboard.get_text().unwrap_or_default();
4468        if clipboard_content != value {
4469            return Err(anyhow!(
4470                "Clipboard write verification failed. Expected {} chars, wrote {} chars",
4471                value.len(),
4472                clipboard_content.len()
4473            ));
4474        }
4475
4476        let item = YankedItem {
4477            description: "Test Case".to_string(),
4478            full_value: value.clone(),
4479            preview: format!("{line_count} lines of test case"),
4480            yank_type: YankType::TestCase,
4481            yanked_at: Local::now(),
4482            size_bytes,
4483        };
4484
4485        self.clipboard.borrow_mut().add_yank(item);
4486
4487        if let Some(ref debug_service) = *self.debug_service.borrow() {
4488            debug_service.info(
4489                "Clipboard",
4490                format!("Yanked test case: {line_count} lines ({size_bytes} bytes)"),
4491            );
4492        }
4493
4494        Ok(())
4495    }
4496
4497    /// Yank debug context to clipboard
4498    pub fn yank_debug_context(&self, value: String) -> Result<()> {
4499        let size_bytes = value.len();
4500        let line_count = value.lines().count();
4501
4502        // Copy to system clipboard
4503        let mut system_clipboard = Clipboard::new()?;
4504        system_clipboard.set_text(&value)?;
4505
4506        // Verify clipboard write
4507        let clipboard_content = system_clipboard.get_text().unwrap_or_default();
4508        if clipboard_content != value {
4509            return Err(anyhow!(
4510                "Clipboard write verification failed. Expected {} chars, wrote {} chars",
4511                value.len(),
4512                clipboard_content.len()
4513            ));
4514        }
4515
4516        let item = YankedItem {
4517            description: "Debug Context".to_string(),
4518            full_value: value.clone(),
4519            preview: "Query context with data for test creation".to_string(),
4520            yank_type: YankType::DebugContext,
4521            yanked_at: Local::now(),
4522            size_bytes,
4523        };
4524
4525        self.clipboard.borrow_mut().add_yank(item);
4526
4527        if let Some(ref debug_service) = *self.debug_service.borrow() {
4528            debug_service.info(
4529                "Clipboard",
4530                format!("Yanked debug context: {line_count} lines ({size_bytes} bytes)"),
4531            );
4532        }
4533
4534        Ok(())
4535    }
4536
4537    /// Clear clipboard
4538    pub fn clear_clipboard(&self) {
4539        let had_item = self.clipboard.borrow().last_yanked.is_some();
4540        self.clipboard.borrow_mut().clear();
4541
4542        if had_item {
4543            if let Some(ref debug_service) = *self.debug_service.borrow() {
4544                debug_service.info("Clipboard", "Clipboard cleared".to_string());
4545            }
4546        }
4547    }
4548
4549    /// Get clipboard statistics for debug display
4550    pub fn get_clipboard_stats(&self) -> String {
4551        self.clipboard.borrow().get_stats()
4552    }
4553
4554    /// Read from system clipboard
4555    pub fn read_from_clipboard(&self) -> Result<String> {
4556        let mut system_clipboard = Clipboard::new()?;
4557        let text = system_clipboard.get_text()?;
4558        Ok(text)
4559    }
4560
4561    /// Write to system clipboard without tracking
4562    pub fn write_to_clipboard(&self, text: &str) -> Result<()> {
4563        let mut system_clipboard = Clipboard::new()?;
4564        system_clipboard.set_text(text)?;
4565
4566        // Verify the write
4567        let clipboard_content = system_clipboard.get_text().unwrap_or_default();
4568        if clipboard_content != text {
4569            return Err(anyhow!(
4570                "Clipboard write verification failed. Expected {} chars, wrote {} chars",
4571                text.len(),
4572                clipboard_content.len()
4573            ));
4574        }
4575
4576        Ok(())
4577    }
4578
4579    /// Get comprehensive results statistics
4580    pub fn get_results_stats(&self) -> (CacheStats, PerformanceStats) {
4581        let results = self.results.borrow();
4582        (results.get_cache_stats(), results.get_performance_stats())
4583    }
4584
4585    /// Check if current results are from cache
4586    pub fn is_results_from_cache(&self) -> bool {
4587        self.results.borrow().from_cache
4588    }
4589
4590    /// Get last query execution time
4591    pub fn get_last_execution_time(&self) -> Duration {
4592        self.results.borrow().last_execution_time
4593    }
4594
4595    /// Get memory usage information
4596    pub fn get_results_memory_usage(&self) -> (usize, usize) {
4597        let cache_stats = self.results.borrow().get_cache_stats();
4598        (cache_stats.memory_usage, cache_stats.memory_limit)
4599    }
4600    // Widget access
4601    pub fn widgets(&self) -> &WidgetStates {
4602        &self.widgets
4603    }
4604
4605    pub fn widgets_mut(&mut self) -> &mut WidgetStates {
4606        &mut self.widgets
4607    }
4608
4609    // UI state access
4610    pub fn cache_list(&self) -> &CacheListState {
4611        &self.cache_list
4612    }
4613
4614    pub fn cache_list_mut(&mut self) -> &mut CacheListState {
4615        &mut self.cache_list
4616    }
4617
4618    pub fn column_stats(&self) -> &ColumnStatsState {
4619        &self.column_stats
4620    }
4621
4622    pub fn column_stats_mut(&mut self) -> &mut ColumnStatsState {
4623        &mut self.column_stats
4624    }
4625
4626    pub fn jump_to_row(&self) -> &JumpToRowState {
4627        &self.jump_to_row
4628    }
4629
4630    pub fn jump_to_row_mut(&mut self) -> &mut JumpToRowState {
4631        &mut self.jump_to_row
4632    }
4633
4634    // History access
4635    pub fn command_history(&self) -> std::cell::Ref<'_, CommandHistory> {
4636        self.command_history.borrow()
4637    }
4638
4639    pub fn command_history_mut(&self) -> std::cell::RefMut<'_, CommandHistory> {
4640        self.command_history.borrow_mut()
4641    }
4642
4643    // Results cache access
4644    pub fn results_cache(&self) -> &ResultsCache {
4645        &self.results_cache
4646    }
4647
4648    pub fn results_cache_mut(&mut self) -> &mut ResultsCache {
4649        &mut self.results_cache
4650    }
4651
4652    // Mode management with validation
4653    pub fn current_mode(&self) -> AppMode {
4654        self.mode_stack.last().cloned().unwrap_or(AppMode::Command)
4655    }
4656
4657    pub fn enter_mode(&mut self, mode: AppMode) -> Result<()> {
4658        let current = self.current_mode();
4659        if let Some(ref debug_service) = *self.debug_service.borrow() {
4660            debug_service.info(
4661                "AppStateContainer",
4662                format!("MODE TRANSITION: {current:?} -> {mode:?}"),
4663            );
4664        }
4665
4666        // Validate transition
4667        match (current, mode.clone()) {
4668            // Add validation rules here
4669            _ => {
4670                // debug!(target: "state", "Mode transition allowed");
4671            }
4672        }
4673
4674        self.mode_stack.push(mode);
4675        if let Some(ref debug_service) = *self.debug_service.borrow() {
4676            debug_service.info(
4677                "AppStateContainer",
4678                format!("Mode stack: {:?}", self.mode_stack),
4679            );
4680        }
4681        Ok(())
4682    }
4683
4684    pub fn exit_mode(&mut self) -> Result<AppMode> {
4685        if self.mode_stack.len() > 1 {
4686            let exited = self.mode_stack.pop().unwrap();
4687            let new_mode = self.current_mode();
4688            if let Some(ref debug_service) = *self.debug_service.borrow() {
4689                debug_service.info(
4690                    "AppStateContainer",
4691                    format!("MODE EXIT: {exited:?} -> {new_mode:?}"),
4692                );
4693                debug_service.info(
4694                    "AppStateContainer",
4695                    format!("Mode stack after exit: {:?}", self.mode_stack),
4696                );
4697            }
4698            Ok(new_mode)
4699        } else {
4700            // debug!(target: "state", "Cannot exit base mode");
4701            Ok(self.current_mode())
4702        }
4703    }
4704
4705    // Debug control
4706    pub fn toggle_debug(&mut self) {
4707        self.debug_enabled = !self.debug_enabled;
4708        if let Some(ref debug_service) = *self.debug_service.borrow() {
4709            debug_service.info(
4710                "AppStateContainer",
4711                format!("Debug mode: {}", self.debug_enabled),
4712            );
4713        }
4714    }
4715
4716    /// Set the debug service for logging (can be called through Arc due to `RefCell`)
4717    pub fn set_debug_service(&self, debug_service: crate::debug_service::DebugService) {
4718        *self.debug_service.borrow_mut() = Some(debug_service);
4719        if let Some(ref service) = *self.debug_service.borrow() {
4720            service.info("AppStateContainer", "Debug service connected".to_string());
4721            service.info(
4722                "AppStateContainer",
4723                "AppStateContainer constructed with debug logging".to_string(),
4724            );
4725        }
4726    }
4727
4728    pub fn is_debug_enabled(&self) -> bool {
4729        self.debug_enabled
4730    }
4731
4732    // Help control methods with comprehensive logging
4733    pub fn toggle_help(&self) {
4734        let mut help = self.help.borrow_mut();
4735        let old_visible = help.is_visible;
4736        help.toggle();
4737
4738        if let Some(ref debug_service) = *self.debug_service.borrow() {
4739            debug_service.info(
4740                "Help",
4741                format!(
4742                    "Toggled help: {} -> {}, open_count: {}",
4743                    old_visible, help.is_visible, help.open_count
4744                ),
4745            );
4746        }
4747    }
4748
4749    pub fn is_help_visible(&self) -> bool {
4750        self.help.borrow().is_visible
4751    }
4752
4753    pub fn set_help_visible(&self, visible: bool) {
4754        let mut help = self.help.borrow_mut();
4755        let old_visible = help.is_visible;
4756
4757        if visible {
4758            help.show();
4759        } else {
4760            help.hide();
4761        }
4762
4763        if let Some(ref debug_service) = *self.debug_service.borrow() {
4764            debug_service.info(
4765                "Help",
4766                format!(
4767                    "Set help visibility: {} -> {}, scroll reset: {}",
4768                    old_visible, help.is_visible, visible
4769                ),
4770            );
4771        }
4772    }
4773
4774    /// Scroll help down by one line
4775    pub fn help_scroll_down(&self) {
4776        let mut help = self.help.borrow_mut();
4777        let old_scroll = help.scroll_offset;
4778        help.scroll_down(1);
4779
4780        if let Some(ref debug_service) = *self.debug_service.borrow() {
4781            if old_scroll != help.scroll_offset {
4782                debug_service.info(
4783                    "Help",
4784                    format!("Scrolled down: {} -> {}", old_scroll, help.scroll_offset),
4785                );
4786            }
4787        }
4788    }
4789
4790    /// Scroll help up by one line
4791    pub fn help_scroll_up(&self) {
4792        let mut help = self.help.borrow_mut();
4793        let old_scroll = help.scroll_offset;
4794        help.scroll_up(1);
4795
4796        if let Some(ref debug_service) = *self.debug_service.borrow() {
4797            if old_scroll != help.scroll_offset {
4798                debug_service.info(
4799                    "Help",
4800                    format!("Scrolled up: {} -> {}", old_scroll, help.scroll_offset),
4801                );
4802            }
4803        }
4804    }
4805
4806    /// Page down in help (10 lines)
4807    pub fn help_page_down(&self) {
4808        let mut help = self.help.borrow_mut();
4809        let old_scroll = help.scroll_offset;
4810        help.scroll_down(10);
4811
4812        if let Some(ref debug_service) = *self.debug_service.borrow() {
4813            debug_service.info(
4814                "Help",
4815                format!("Page down: {} -> {}", old_scroll, help.scroll_offset),
4816            );
4817        }
4818    }
4819
4820    /// Page up in help (10 lines)
4821    pub fn help_page_up(&self) {
4822        let mut help = self.help.borrow_mut();
4823        let old_scroll = help.scroll_offset;
4824        help.scroll_up(10);
4825
4826        if let Some(ref debug_service) = *self.debug_service.borrow() {
4827            debug_service.info(
4828                "Help",
4829                format!("Page up: {} -> {}", old_scroll, help.scroll_offset),
4830            );
4831        }
4832    }
4833
4834    /// Set maximum scroll for help based on content
4835    pub fn set_help_max_scroll(&self, content_lines: usize, viewport_height: usize) {
4836        let mut help = self.help.borrow_mut();
4837        let old_max = help.max_scroll;
4838        help.set_max_scroll(content_lines, viewport_height);
4839
4840        if let Some(ref debug_service) = *self.debug_service.borrow() {
4841            if old_max != help.max_scroll {
4842                debug_service.info(
4843                    "Help",
4844                    format!(
4845                        "Updated max scroll: {} -> {} (content: {}, viewport: {})",
4846                        old_max, help.max_scroll, content_lines, viewport_height
4847                    ),
4848                );
4849            }
4850        }
4851    }
4852
4853    /// Get current help scroll offset
4854    pub fn help_scroll_offset(&self) -> u16 {
4855        self.help.borrow().scroll_offset
4856    }
4857
4858    /// Get help state for debugging
4859    pub fn help_state(&self) -> std::cell::Ref<'_, HelpState> {
4860        self.help.borrow()
4861    }
4862
4863    // Key press management - uses interior mutability so it can be called through Arc
4864    pub fn log_key_press(&self, key: KeyEvent, action: Option<String>) {
4865        let mode = self.current_mode();
4866        let entry = KeyPressEntry::new(key, mode.clone(), action.clone());
4867
4868        // Log to debug service with platform info
4869        if let Some(ref debug_service) = *self.debug_service.borrow() {
4870            let platform_info = if entry.platform == Platform::Windows
4871                && (key.code == KeyCode::Char('$') || key.code == KeyCode::Char('^'))
4872                && key.modifiers.contains(KeyModifiers::SHIFT)
4873            {
4874                " [Windows: SHIFT modifier present]"
4875            } else {
4876                ""
4877            };
4878
4879            debug_service.info(
4880                "KeyPress",
4881                format!(
4882                    "Key: {:?}, Mode: {:?}, Action: {:?}, Platform: {:?}{}",
4883                    key, mode, action, entry.platform, platform_info
4884                ),
4885            );
4886        }
4887
4888        self.key_press_history.borrow_mut().add(entry);
4889    }
4890
4891    pub fn clear_key_history(&self) {
4892        self.key_press_history.borrow_mut().clear();
4893        if let Some(ref debug_service) = *self.debug_service.borrow() {
4894            debug_service.info("AppStateContainer", "Key press history cleared".to_string());
4895        }
4896    }
4897
4898    /// Normalize a key event for platform-specific differences
4899    /// This handles cases like Windows sending Shift+$ instead of just $
4900    /// and platform differences in how Shift+Arrow keys are reported
4901    pub fn normalize_key(&self, key: KeyEvent) -> KeyEvent {
4902        let platform = Platform::detect();
4903
4904        // Handle platform-specific key differences
4905        match platform {
4906            Platform::Windows => {
4907                match key.code {
4908                    // Special characters that come with SHIFT modifier on Windows
4909                    KeyCode::Char(
4910                        '$' | '^' | ':' | '!' | '@' | '#' | '%' | '&' | '*' | '(' | ')',
4911                    ) => {
4912                        // Remove SHIFT modifier for these characters on Windows
4913                        let mut normalized_modifiers = key.modifiers;
4914                        normalized_modifiers.remove(KeyModifiers::SHIFT);
4915
4916                        if let Some(ref debug_service) = *self.debug_service.borrow() {
4917                            if normalized_modifiers != key.modifiers {
4918                                debug_service.info(
4919                                    "KeyNormalize",
4920                                    format!(
4921                                        "Windows key normalization: {:?} with {:?} -> {:?}",
4922                                        key.code, key.modifiers, normalized_modifiers
4923                                    ),
4924                                );
4925                            }
4926                        }
4927
4928                        KeyEvent::new(key.code, normalized_modifiers)
4929                    }
4930                    // On Windows, Shift+Arrow might come as actual arrow keys with SHIFT
4931                    KeyCode::Left if key.modifiers.contains(KeyModifiers::SHIFT) => {
4932                        if let Some(ref debug_service) = *self.debug_service.borrow() {
4933                            debug_service.info(
4934                                "KeyNormalize",
4935                                "Windows: Shift+Left -> '<' character for column movement"
4936                                    .to_string(),
4937                            );
4938                        }
4939                        // Convert to '<' character without SHIFT for consistency
4940                        KeyEvent::new(KeyCode::Char('<'), KeyModifiers::NONE)
4941                    }
4942                    KeyCode::Right if key.modifiers.contains(KeyModifiers::SHIFT) => {
4943                        if let Some(ref debug_service) = *self.debug_service.borrow() {
4944                            debug_service.info(
4945                                "KeyNormalize",
4946                                "Windows: Shift+Right -> '>' character for column movement"
4947                                    .to_string(),
4948                            );
4949                        }
4950                        // Convert to '>' character without SHIFT for consistency
4951                        KeyEvent::new(KeyCode::Char('>'), KeyModifiers::NONE)
4952                    }
4953                    // Handle Windows specific case where column movement comes as '>' and '<' WITH SHIFT
4954                    KeyCode::Char('<') if key.modifiers.contains(KeyModifiers::SHIFT) => {
4955                        if let Some(ref debug_service) = *self.debug_service.borrow() {
4956                            debug_service.info(
4957                                "KeyNormalize",
4958                                "Windows: Shift+'<' -> '<' character for column movement"
4959                                    .to_string(),
4960                            );
4961                        }
4962                        // Remove SHIFT modifier to normalize for column movement
4963                        KeyEvent::new(KeyCode::Char('<'), KeyModifiers::NONE)
4964                    }
4965                    KeyCode::Char('>') if key.modifiers.contains(KeyModifiers::SHIFT) => {
4966                        if let Some(ref debug_service) = *self.debug_service.borrow() {
4967                            debug_service.info(
4968                                "KeyNormalize",
4969                                "Windows: Shift+'>' -> '>' character for column movement"
4970                                    .to_string(),
4971                            );
4972                        }
4973                        // Remove SHIFT modifier to normalize for column movement
4974                        KeyEvent::new(KeyCode::Char('>'), KeyModifiers::NONE)
4975                    }
4976                    _ => key,
4977                }
4978            }
4979            Platform::Linux | Platform::MacOS => {
4980                // On Unix-like systems, Shift+Arrow keys typically come as '>' and '<' characters
4981                // No additional normalization needed for these platforms currently
4982                key
4983            }
4984            Platform::Unknown => key,
4985        }
4986    }
4987
4988    /// Generate comprehensive debug dump for F5
4989    pub fn debug_dump(&self) -> String {
4990        let mut dump = String::new();
4991
4992        dump.push_str("=== APP STATE CONTAINER DEBUG DUMP ===\n\n");
4993
4994        // Mode information
4995        dump.push_str("MODE INFORMATION:\n");
4996        dump.push_str(&format!("  Current Mode: {:?}\n", self.current_mode()));
4997        dump.push_str(&format!("  Mode Stack: {:?}\n", self.mode_stack));
4998        dump.push('\n');
4999
5000        // UI Flags
5001        dump.push_str("UI FLAGS:\n");
5002        dump.push_str(&format!("  Debug Enabled: {}\n", self.debug_enabled));
5003        dump.push('\n');
5004
5005        // Help State
5006        let help = self.help.borrow();
5007        dump.push_str("HELP STATE:\n");
5008        dump.push_str(&format!("  Visible: {}\n", help.is_visible));
5009        dump.push_str(&format!("  Scroll Offset: {}\n", help.scroll_offset));
5010        dump.push_str(&format!("  Max Scroll: {}\n", help.max_scroll));
5011        dump.push_str(&format!("  Open Count: {}\n", help.open_count));
5012        if let Some(ref last_opened) = help.last_opened {
5013            dump.push_str(&format!("  Last Opened: {:?} ago\n", last_opened.elapsed()));
5014        }
5015        dump.push('\n');
5016
5017        // Input state
5018        dump.push_str("INPUT STATE:\n");
5019        let input = self.command_input.borrow();
5020        dump.push_str(&format!("  Text: '{}'\n", input.text));
5021        dump.push_str(&format!("  Cursor: {}\n", input.cursor_position));
5022        dump.push_str(&format!(
5023            "  Last Query: '{}'\n",
5024            if input.last_executed_query.len() > 100 {
5025                format!("{}...", &input.last_executed_query[..100])
5026            } else {
5027                input.last_executed_query.clone()
5028            }
5029        ));
5030        dump.push('\n');
5031
5032        // Search state
5033        dump.push_str("SEARCH STATE:\n");
5034        let search = self.search.borrow();
5035        if search.is_active {
5036            dump.push_str(&format!("  Pattern: '{}'\n", search.pattern));
5037            dump.push_str(&format!("  Matches: {} found\n", search.matches.len()));
5038            dump.push_str(&format!(
5039                "  Current: {} of {}\n",
5040                if search.matches.is_empty() {
5041                    0
5042                } else {
5043                    search.current_match + 1
5044                },
5045                search.matches.len()
5046            ));
5047            if let Some(ref last_time) = search.last_search_time {
5048                dump.push_str(&format!("  Search time: {:?}\n", last_time.elapsed()));
5049            }
5050        } else {
5051            dump.push_str("  [Inactive]\n");
5052        }
5053
5054        // Search history
5055        if !search.history.is_empty() {
5056            dump.push_str("  Recent searches:\n");
5057            for (i, entry) in search.history.iter().rev().take(5).enumerate() {
5058                dump.push_str(&format!(
5059                    "    {}. '{}' → {} matches",
5060                    i + 1,
5061                    if entry.pattern.len() > 30 {
5062                        format!("{}...", &entry.pattern[..30])
5063                    } else {
5064                        entry.pattern.clone()
5065                    },
5066                    entry.match_count
5067                ));
5068                if let Some(duration) = entry.duration_ms {
5069                    dump.push_str(&format!(" ({duration}ms)"));
5070                }
5071                dump.push_str(&format!(" at {}\n", entry.timestamp.format("%H:%M:%S")));
5072            }
5073        }
5074        dump.push('\n');
5075
5076        // Filter state
5077        dump.push_str("FILTER STATE:\n");
5078        let filter = self.filter.borrow();
5079        if filter.is_active {
5080            dump.push_str(&format!("  Pattern: '{}'\n", filter.pattern));
5081            dump.push_str(&format!(
5082                "  Filtered Rows: {}\n",
5083                filter.filtered_indices.len()
5084            ));
5085            dump.push_str(&format!(
5086                "  Case Insensitive: {}\n",
5087                filter.case_insensitive
5088            ));
5089            if let Some(ref last_time) = filter.last_filter_time {
5090                dump.push_str(&format!("  Last Filter: {:?} ago\n", last_time.elapsed()));
5091            }
5092        } else {
5093            dump.push_str("  [Inactive]\n");
5094        }
5095        dump.push_str(&format!("  Total Filters: {}\n", filter.total_filters));
5096        dump.push_str(&format!("  History Items: {}\n", filter.history.len()));
5097        if !filter.history.is_empty() {
5098            dump.push_str("  Recent filters:\n");
5099            for (i, entry) in filter.history.iter().take(5).enumerate() {
5100                dump.push_str(&format!(
5101                    "    {}. '{}' ({} matches) at {}\n",
5102                    i + 1,
5103                    if entry.pattern.len() > 30 {
5104                        format!("{}...", &entry.pattern[..30])
5105                    } else {
5106                        entry.pattern.clone()
5107                    },
5108                    entry.match_count,
5109                    entry.timestamp.format("%H:%M:%S")
5110                ));
5111            }
5112        }
5113        dump.push('\n');
5114
5115        // Column search state
5116        let column_search = self.column_search.borrow();
5117        if column_search.is_active {
5118            dump.push_str("COLUMN SEARCH STATE (ACTIVE):\n");
5119            dump.push_str(&format!("  Pattern: '{}'\n", column_search.pattern));
5120            dump.push_str(&format!(
5121                "  Matching Columns: {}\n",
5122                column_search.matching_columns.len()
5123            ));
5124            if !column_search.matching_columns.is_empty() {
5125                for (i, (idx, name)) in column_search.matching_columns.iter().take(5).enumerate() {
5126                    dump.push_str(&format!(
5127                        "    [{}] {}: '{}'\n",
5128                        if i == column_search.current_match {
5129                            "*"
5130                        } else {
5131                            " "
5132                        },
5133                        idx,
5134                        name
5135                    ));
5136                }
5137            }
5138            dump.push('\n');
5139        }
5140
5141        // History search state (Ctrl+R)
5142        let history_search = self.history_search.borrow();
5143        if history_search.is_active {
5144            dump.push_str("HISTORY SEARCH STATE (ACTIVE):\n");
5145            dump.push_str(&format!("  Query: '{}'\n", history_search.query));
5146            dump.push_str(&format!("  Matches: {}\n", history_search.matches.len()));
5147            dump.push_str(&format!("  Selected: {}\n", history_search.selected_index));
5148            dump.push_str(&format!(
5149                "  Original Input: '{}'\n",
5150                history_search.original_input
5151            ));
5152            if !history_search.matches.is_empty() {
5153                dump.push_str("  Top matches:\n");
5154                for (i, m) in history_search.matches.iter().take(5).enumerate() {
5155                    dump.push_str(&format!(
5156                        "    [{}] Score: {}, '{}'\n",
5157                        if i == history_search.selected_index {
5158                            "*"
5159                        } else {
5160                            " "
5161                        },
5162                        m.score,
5163                        if m.entry.command.len() > 50 {
5164                            format!("{}...", &m.entry.command[..50])
5165                        } else {
5166                            m.entry.command.clone()
5167                        }
5168                    ));
5169                }
5170            }
5171            dump.push('\n');
5172        }
5173
5174        // Navigation state with enhanced viewport information
5175        let navigation = self.navigation.borrow();
5176        dump.push_str("NAVIGATION STATE:\n");
5177        dump.push_str(&format!(
5178            "  Cursor Position: row={}, col={}\n",
5179            navigation.selected_row, navigation.selected_column
5180        ));
5181        dump.push_str(&format!(
5182            "  Scroll Offset: row={}, col={}\n",
5183            navigation.scroll_offset.0, navigation.scroll_offset.1
5184        ));
5185        dump.push_str(&format!(
5186            "  Viewport Dimensions: {} rows x {} cols\n",
5187            navigation.viewport_rows, navigation.viewport_columns
5188        ));
5189        dump.push_str(&format!(
5190            "  Data Size: {} rows x {} cols\n",
5191            navigation.total_rows, navigation.total_columns
5192        ));
5193
5194        // Viewport boundary analysis
5195        dump.push_str("\nVIEWPORT BOUNDARIES:\n");
5196        let at_top = navigation.selected_row == 0;
5197        let at_bottom = navigation.selected_row == navigation.total_rows.saturating_sub(1);
5198        let at_left = navigation.selected_column == 0;
5199        let at_right = navigation.selected_column == navigation.total_columns.saturating_sub(1);
5200
5201        dump.push_str(&format!("  At Top Edge: {at_top}\n"));
5202        dump.push_str(&format!("  At Bottom Edge: {at_bottom}\n"));
5203        dump.push_str(&format!("  At Left Edge: {at_left}\n"));
5204        dump.push_str(&format!("  At Right Edge: {at_right}\n"));
5205
5206        // Scrolling state
5207        let viewport_bottom = navigation.scroll_offset.0 + navigation.viewport_rows;
5208        let viewport_right = navigation.scroll_offset.1 + navigation.viewport_columns;
5209        let should_scroll_down = navigation.selected_row >= viewport_bottom.saturating_sub(1);
5210        let should_scroll_up = navigation.selected_row < navigation.scroll_offset.0;
5211        let should_scroll_right = navigation.selected_column >= viewport_right.saturating_sub(1);
5212        let should_scroll_left = navigation.selected_column < navigation.scroll_offset.1;
5213
5214        dump.push_str("\nSCROLLING STATE:\n");
5215        dump.push_str(&format!(
5216            "  Visible Row Range: {} to {}\n",
5217            navigation.scroll_offset.0,
5218            viewport_bottom.min(navigation.total_rows).saturating_sub(1)
5219        ));
5220        dump.push_str(&format!(
5221            "  Visible Col Range: {} to {}\n",
5222            navigation.scroll_offset.1,
5223            viewport_right
5224                .min(navigation.total_columns)
5225                .saturating_sub(1)
5226        ));
5227        dump.push_str(&format!(
5228            "  Should Scroll Down: {} (cursor at {}, viewport bottom at {})\n",
5229            should_scroll_down,
5230            navigation.selected_row,
5231            viewport_bottom.saturating_sub(1)
5232        ));
5233        dump.push_str(&format!(
5234            "  Should Scroll Up: {} (cursor at {}, viewport top at {})\n",
5235            should_scroll_up, navigation.selected_row, navigation.scroll_offset.0
5236        ));
5237        dump.push_str(&format!("  Should Scroll Right: {should_scroll_right}\n"));
5238        dump.push_str(&format!("  Should Scroll Left: {should_scroll_left}\n"));
5239
5240        dump.push_str(&format!(
5241            "\n  Viewport Lock: {} at row {:?}\n",
5242            navigation.viewport_lock, navigation.viewport_lock_row
5243        ));
5244        dump.push_str(&format!(
5245            "  Cursor Lock: {} at visual position {:?}\n",
5246            navigation.cursor_lock, navigation.cursor_lock_position
5247        ));
5248
5249        if !navigation.selection_history.is_empty() {
5250            dump.push_str("\n  Recent positions:\n");
5251            for (i, &(row, col)) in navigation
5252                .selection_history
5253                .iter()
5254                .rev()
5255                .take(5)
5256                .enumerate()
5257            {
5258                dump.push_str(&format!("    {}. ({}, {})\n", i + 1, row, col));
5259            }
5260        }
5261        dump.push('\n');
5262
5263        // Column search state
5264        dump.push_str("COLUMN SEARCH STATE:\n");
5265        let column_search = self.column_search.borrow();
5266        if column_search.is_active {
5267            dump.push_str(&format!("  Pattern: '{}'\n", column_search.pattern));
5268            dump.push_str(&format!(
5269                "  Matches: {} columns found\n",
5270                column_search.matching_columns.len()
5271            ));
5272            if !column_search.matching_columns.is_empty() {
5273                dump.push_str(&format!(
5274                    "  Current: {} of {}\n",
5275                    column_search.current_match + 1,
5276                    column_search.matching_columns.len()
5277                ));
5278                dump.push_str("  Matching columns:\n");
5279                for (i, (idx, name)) in column_search.matching_columns.iter().enumerate() {
5280                    dump.push_str(&format!(
5281                        "    {}[{}] {} (index {})\n",
5282                        if i == column_search.current_match {
5283                            "*"
5284                        } else {
5285                            " "
5286                        },
5287                        i + 1,
5288                        name,
5289                        idx
5290                    ));
5291                }
5292            }
5293            if let Some(ref last_time) = column_search.last_search_time {
5294                dump.push_str(&format!("  Search time: {:?}\n", last_time.elapsed()));
5295            }
5296        } else {
5297            dump.push_str("  [Inactive]\n");
5298        }
5299        dump.push_str(&format!(
5300            "  Total searches: {}\n",
5301            column_search.total_searches
5302        ));
5303        dump.push_str(&format!(
5304            "  History items: {}\n",
5305            column_search.history.len()
5306        ));
5307        if !column_search.history.is_empty() {
5308            dump.push_str("  Recent searches:\n");
5309            for (i, entry) in column_search.history.iter().take(5).enumerate() {
5310                dump.push_str(&format!(
5311                    "    {}. '{}' ({} matches) at {}\n",
5312                    i + 1,
5313                    entry.pattern,
5314                    entry.match_count,
5315                    entry.timestamp.format("%H:%M:%S")
5316                ));
5317            }
5318        }
5319        dump.push('\n');
5320
5321        // Sort state
5322        dump.push_str("SORT STATE:\n");
5323        let sort = self.sort.borrow();
5324        if let (Some(col), Some(name)) = (sort.column, &sort.column_name) {
5325            dump.push_str(&format!(
5326                "  Current: Column {} ({}) {}\n",
5327                col,
5328                name,
5329                match sort.order {
5330                    SortOrder::Ascending => "Ascending ↑",
5331                    SortOrder::Descending => "Descending ↓",
5332                    SortOrder::None => "None",
5333                }
5334            ));
5335        } else {
5336            dump.push_str("  Current: No sorting applied\n");
5337        }
5338        if let Some(ref last_time) = sort.last_sort_time {
5339            dump.push_str(&format!("  Last sort: {:?} ago\n", last_time.elapsed()));
5340        }
5341        dump.push_str(&format!("  Total sorts: {}\n", sort.total_sorts));
5342        dump.push_str(&format!("  History items: {}\n", sort.history.len()));
5343        if !sort.history.is_empty() {
5344            dump.push_str("  Recent sorts:\n");
5345            for (i, entry) in sort.history.iter().rev().take(5).enumerate() {
5346                dump.push_str(&format!(
5347                    "    {}. Column {} ({}) {} - {} rows\n",
5348                    i + 1,
5349                    entry.column_index,
5350                    entry.column_name,
5351                    match entry.order {
5352                        SortOrder::Ascending => "↑",
5353                        SortOrder::Descending => "↓",
5354                        SortOrder::None => "-",
5355                    },
5356                    entry.row_count
5357                ));
5358            }
5359        }
5360        dump.push('\n');
5361
5362        // Selection state
5363        dump.push_str("SELECTION STATE:\n");
5364        let selection = self.selection.borrow();
5365        dump.push_str(&format!("  Mode: {:?}\n", selection.mode));
5366        if let Some(row) = selection.selected_row {
5367            dump.push_str(&format!("  Selected Row: {row}\n"));
5368        } else {
5369            dump.push_str("  Selected Row: None\n");
5370        }
5371        dump.push_str(&format!(
5372            "  Selected Column: {}\n",
5373            selection.selected_column
5374        ));
5375        if !selection.selected_cells.is_empty() {
5376            dump.push_str(&format!(
5377                "  Selected Cells: {} cells\n",
5378                selection.selected_cells.len()
5379            ));
5380            if selection.selected_cells.len() <= 5 {
5381                for (row, col) in &selection.selected_cells {
5382                    dump.push_str(&format!("    - ({row}, {col})\n"));
5383                }
5384            } else {
5385                for (row, col) in selection.selected_cells.iter().take(3) {
5386                    dump.push_str(&format!("    - ({row}, {col})\n"));
5387                }
5388                dump.push_str(&format!(
5389                    "    ... and {} more\n",
5390                    selection.selected_cells.len() - 3
5391                ));
5392            }
5393        }
5394        if let Some((row, col)) = selection.selection_anchor {
5395            dump.push_str(&format!("  Selection Anchor: ({row}, {col})\n"));
5396        }
5397        dump.push_str(&format!(
5398            "  Total Selections: {}\n",
5399            selection.total_selections
5400        ));
5401        if let Some(ref last_time) = selection.last_selection_time {
5402            dump.push_str(&format!(
5403                "  Last Selection: {:?} ago\n",
5404                last_time.elapsed()
5405            ));
5406        }
5407        dump.push_str(&format!("  History Items: {}\n", selection.history.len()));
5408        if !selection.history.is_empty() {
5409            dump.push_str("  Recent selections:\n");
5410            for (i, entry) in selection.history.iter().rev().take(5).enumerate() {
5411                dump.push_str(&format!(
5412                    "    {}. {:?} mode at {}\n",
5413                    i + 1,
5414                    entry.mode,
5415                    entry.timestamp.format("%H:%M:%S")
5416                ));
5417            }
5418        }
5419        dump.push('\n');
5420
5421        // Clipboard state
5422        dump.push_str("CLIPBOARD STATE:\n");
5423        let clipboard = self.clipboard.borrow();
5424        if let Some(ref yanked) = clipboard.last_yanked {
5425            dump.push_str(&format!("  Last Yanked: {}\n", yanked.description));
5426            dump.push_str(&format!("  Type: {:?}\n", yanked.yank_type));
5427            dump.push_str(&format!("  Size: {} bytes\n", yanked.size_bytes));
5428            dump.push_str(&format!(
5429                "  Preview: {}\n",
5430                if yanked.preview.len() > 60 {
5431                    format!("{}...", &yanked.preview[..60])
5432                } else {
5433                    yanked.preview.clone()
5434                }
5435            ));
5436            dump.push_str(&format!(
5437                "  Yanked at: {}\n",
5438                yanked.yanked_at.format("%H:%M:%S")
5439            ));
5440        } else {
5441            dump.push_str("  [Empty]\n");
5442        }
5443        dump.push_str(&format!("  Total yanks: {}\n", clipboard.total_yanks));
5444        dump.push_str(&format!(
5445            "  History items: {}\n",
5446            clipboard.yank_history.len()
5447        ));
5448        if !clipboard.yank_history.is_empty() {
5449            dump.push_str("  Recent yanks:\n");
5450            for (i, item) in clipboard.yank_history.iter().take(5).enumerate() {
5451                dump.push_str(&format!(
5452                    "    {}. {} ({} bytes) at {}\n",
5453                    i + 1,
5454                    item.description,
5455                    item.size_bytes,
5456                    item.yanked_at.format("%H:%M:%S")
5457                ));
5458            }
5459        }
5460        dump.push('\n');
5461
5462        // Chord state
5463        dump.push_str("CHORD STATE:\n");
5464        let chord = self.chord.borrow();
5465        if chord.current_chord.is_empty() {
5466            dump.push_str("  No active chord\n");
5467        } else {
5468            dump.push_str(&format!("  Active chord: '{}'\n", chord.get_chord_string()));
5469            if let Some(ref start) = chord.chord_start {
5470                if let Ok(elapsed) = start.elapsed() {
5471                    dump.push_str(&format!("  Time elapsed: {:.1}s\n", elapsed.as_secs_f32()));
5472                }
5473            }
5474            if let Some(ref desc) = chord.description {
5475                dump.push_str(&format!("  Description: {desc}\n"));
5476            }
5477        }
5478
5479        dump.push_str("\nREGISTERED CHORDS:\n");
5480        let mut chords: Vec<_> = chord.registered_chords.iter().collect();
5481        chords.sort_by_key(|(k, _)| k.as_str());
5482        for (chord_seq, action) in chords {
5483            dump.push_str(&format!("  {chord_seq} → {action}\n"));
5484        }
5485
5486        if !chord.history.is_empty() {
5487            dump.push_str("\nCHORD HISTORY:\n");
5488            for (i, (chord_str, action, timestamp)) in
5489                chord.history.iter().rev().take(5).enumerate()
5490            {
5491                if let Ok(elapsed) = timestamp.elapsed() {
5492                    dump.push_str(&format!(
5493                        "  {}. {} → {} ({:.1}s ago)\n",
5494                        i + 1,
5495                        chord_str,
5496                        action,
5497                        elapsed.as_secs_f32()
5498                    ));
5499                }
5500            }
5501        }
5502        dump.push('\n');
5503
5504        // Undo/Redo state
5505        dump.push_str("UNDO/REDO STATE:\n");
5506        let undo_redo = self.undo_redo.borrow();
5507        dump.push_str(&format!(
5508            "  Undo stack: {} entries\n",
5509            undo_redo.undo_stack.len()
5510        ));
5511        dump.push_str(&format!(
5512            "  Redo stack: {} entries\n",
5513            undo_redo.redo_stack.len()
5514        ));
5515        if !undo_redo.undo_stack.is_empty() {
5516            dump.push_str("  Recent undo entries:\n");
5517            for (i, (text, cursor)) in undo_redo.undo_stack.iter().rev().take(3).enumerate() {
5518                let preview = if text.len() > 50 {
5519                    format!("{}...", &text[..50])
5520                } else {
5521                    text.clone()
5522                };
5523                dump.push_str(&format!(
5524                    "    {}. '{}' (cursor: {})\n",
5525                    i + 1,
5526                    preview,
5527                    cursor
5528                ));
5529            }
5530        }
5531        dump.push('\n');
5532
5533        // Scroll state
5534        dump.push_str("SCROLL STATE:\n");
5535        let scroll = self.scroll.borrow();
5536        dump.push_str(&format!("  Help scroll: {}\n", scroll.help_scroll));
5537        dump.push_str(&format!("  Input scroll: {}\n", scroll.input_scroll_offset));
5538        dump.push_str(&format!(
5539            "  Viewport scroll: ({}, {})\n",
5540            scroll.viewport_scroll_offset.0, scroll.viewport_scroll_offset.1
5541        ));
5542        dump.push_str(&format!(
5543            "  Last visible rows: {}\n",
5544            scroll.last_visible_rows
5545        ));
5546        dump.push('\n');
5547
5548        // Widget states using DebugInfoProvider trait
5549        dump.push_str(&self.widgets.search_modes.debug_info());
5550        dump.push('\n');
5551        if let Some(ref history) = self.widgets.history {
5552            dump.push_str(&history.debug_info());
5553            dump.push('\n');
5554        }
5555        dump.push_str(&self.widgets.help.debug_info());
5556        dump.push('\n');
5557        dump.push_str(&self.widgets.stats.debug_info());
5558        dump.push('\n');
5559        // TODO: Add debug widget info when available
5560        // dump.push_str(&self.widgets.debug.debug_info());
5561        // dump.push_str("\n");
5562
5563        // Buffer information
5564        dump.push_str("BUFFER STATE:\n");
5565        dump.push_str(&format!(
5566            "  Current Buffer ID: {}\n",
5567            self.current_buffer_id
5568        ));
5569        // TODO: Add buffer count when method is available
5570        // dump.push_str(&format!("  Total Buffers: {}\n", self.buffers.count()));
5571        if let Some(_buffer) = self.current_buffer() {
5572            // TODO: Add buffer mode and results when methods are available
5573            // dump.push_str(&format!("  Buffer Mode: {:?}\n", buffer.get_mode()));
5574            // if let Some(datatable) = buffer.get_datatable() {
5575            //     dump.push_str(&format!("  Results: {} rows x {} cols\n",
5576            //         datatable.row_count(),
5577            //         datatable.column_count()
5578            //     ));
5579            // }
5580            dump.push_str("  Buffer: Present\n");
5581        } else {
5582            dump.push_str("  Buffer: None\n");
5583        }
5584        dump.push('\n');
5585
5586        // Cache state
5587        dump.push_str("CACHE STATE:\n");
5588        dump.push_str(&format!(
5589            "  Cached Results: {}\n",
5590            self.results_cache.cache.len()
5591        ));
5592        dump.push_str(&format!(
5593            "  Max Cache Size: {}\n",
5594            self.results_cache.max_size
5595        ));
5596        dump.push('\n');
5597
5598        // History state
5599        dump.push_str("HISTORY STATE:\n");
5600        dump.push_str(&format!(
5601            "  Total Commands: {}\n",
5602            self.command_history.borrow().get_all().len()
5603        ));
5604        dump.push('\n');
5605
5606        // Key press history
5607        dump.push_str(&self.key_press_history.borrow().format_history());
5608        dump.push('\n');
5609
5610        // Platform-specific key information
5611        dump.push_str("PLATFORM INFO:\n");
5612        dump.push_str(&format!("  Platform: {:?}\n", Platform::detect()));
5613        dump.push_str("  Key Normalization: ");
5614        if Platform::detect() == Platform::Windows {
5615            dump.push_str("ACTIVE (Windows special chars)\n");
5616        } else {
5617            dump.push_str("INACTIVE\n");
5618        }
5619        dump.push('\n');
5620
5621        dump.push_str("=== END DEBUG DUMP ===\n");
5622
5623        dump
5624    }
5625
5626    /// Pretty print the state for debugging
5627    pub fn pretty_print(&self) -> String {
5628        format!("{self:#?}")
5629    }
5630
5631    // ==================== STATE DELEGATION METHODS ====================
5632    // These methods delegate to the current Buffer to eliminate state duplication
5633    // Eventually, the duplicate state fields in AppStateContainer will be removed
5634
5635    // --- Navigation State Delegation ---
5636
5637    /// Get selected row from current buffer
5638    pub fn delegated_selected_row(&self) -> Option<usize> {
5639        self.current_buffer()?.get_selected_row()
5640    }
5641
5642    /// Set selected row in current buffer
5643    pub fn set_delegated_selected_row(&mut self, row: Option<usize>) {
5644        if let Some(buffer) = self.current_buffer_mut() {
5645            buffer.set_selected_row(row);
5646        }
5647    }
5648
5649    /// Get current column from current buffer
5650    pub fn delegated_current_column(&self) -> usize {
5651        self.current_buffer()
5652            .map_or(0, super::buffer::BufferAPI::get_current_column)
5653    }
5654
5655    /// Set current column in current buffer
5656    pub fn set_delegated_current_column(&mut self, col: usize) {
5657        if let Some(buffer) = self.current_buffer_mut() {
5658            buffer.set_current_column(col);
5659        }
5660    }
5661
5662    /// Get scroll offset from current buffer
5663    pub fn delegated_scroll_offset(&self) -> (usize, usize) {
5664        self.current_buffer()
5665            .map_or((0, 0), super::buffer::BufferAPI::get_scroll_offset)
5666    }
5667
5668    /// Set scroll offset in current buffer
5669    pub fn set_delegated_scroll_offset(&mut self, offset: (usize, usize)) {
5670        if let Some(buffer) = self.current_buffer_mut() {
5671            buffer.set_scroll_offset(offset);
5672        }
5673    }
5674
5675    // --- Search State Delegation ---
5676
5677    /// Get search pattern from current buffer
5678    pub fn delegated_search_pattern(&self) -> String {
5679        self.current_buffer()
5680            .map(super::buffer::BufferAPI::get_search_pattern)
5681            .unwrap_or_default()
5682    }
5683
5684    /// Set search pattern in current buffer
5685    pub fn set_delegated_search_pattern(&mut self, pattern: String) {
5686        if let Some(buffer) = self.current_buffer_mut() {
5687            buffer.set_search_pattern(pattern);
5688        }
5689    }
5690
5691    /// Get search matches from current buffer
5692    pub fn delegated_search_matches(&self) -> Vec<(usize, usize)> {
5693        self.current_buffer()
5694            .map(super::buffer::BufferAPI::get_search_matches)
5695            .unwrap_or_default()
5696    }
5697
5698    /// Set search matches in current buffer
5699    pub fn set_delegated_search_matches(&mut self, matches: Vec<(usize, usize)>) {
5700        if let Some(buffer) = self.current_buffer_mut() {
5701            buffer.set_search_matches(matches);
5702        }
5703    }
5704
5705    // --- Filter State Delegation ---
5706
5707    /// Get filter pattern from current buffer
5708    pub fn delegated_filter_pattern(&self) -> String {
5709        self.current_buffer()
5710            .map(super::buffer::BufferAPI::get_filter_pattern)
5711            .unwrap_or_default()
5712    }
5713
5714    /// Set filter pattern in current buffer
5715    pub fn set_delegated_filter_pattern(&mut self, pattern: String) {
5716        if let Some(buffer) = self.current_buffer_mut() {
5717            buffer.set_filter_pattern(pattern);
5718        }
5719    }
5720
5721    /// Check if filter is active in current buffer
5722    pub fn delegated_filter_active(&self) -> bool {
5723        self.current_buffer()
5724            .is_some_and(super::buffer::BufferAPI::is_filter_active)
5725    }
5726
5727    /// Set filter active state in current buffer
5728    pub fn set_delegated_filter_active(&mut self, active: bool) {
5729        if let Some(buffer) = self.current_buffer_mut() {
5730            buffer.set_filter_active(active);
5731        }
5732    }
5733
5734    // --- Sort State Delegation ---
5735
5736    /// Get sort column from current buffer
5737    pub fn delegated_sort_column(&self) -> Option<usize> {
5738        self.current_buffer()?.get_sort_column()
5739    }
5740
5741    /// Set sort column in current buffer
5742    pub fn set_delegated_sort_column(&mut self, column: Option<usize>) {
5743        if let Some(buffer) = self.current_buffer_mut() {
5744            buffer.set_sort_column(column);
5745        }
5746    }
5747
5748    /// Get sort order from current buffer
5749    pub fn delegated_sort_order(&self) -> SortOrder {
5750        self.current_buffer()
5751            .map_or(SortOrder::None, super::buffer::BufferAPI::get_sort_order)
5752    }
5753
5754    /// Set sort order in current buffer
5755    pub fn set_delegated_sort_order(&mut self, order: SortOrder) {
5756        if let Some(buffer) = self.current_buffer_mut() {
5757            buffer.set_sort_order(order);
5758        }
5759    }
5760
5761    // ==================== BUFFER PROXY METHODS ====================
5762    // These methods proxy Buffer operations that TUI currently calls directly
5763    // This is part of migrating to AppStateContainer as the single entry point
5764
5765    /// Set the current mode (proxy to Buffer)
5766    pub fn set_mode(&mut self, mode: AppMode) {
5767        if let Some(buffer) = self.current_buffer_mut() {
5768            buffer.set_mode(mode);
5769        }
5770        // TODO: Add any side effects or state synchronization here
5771    }
5772
5773    /// Get the current mode (proxy to Buffer)
5774    pub fn get_mode(&self) -> AppMode {
5775        self.current_buffer()
5776            .map_or(AppMode::Command, super::buffer::BufferAPI::get_mode)
5777    }
5778
5779    /// Set status message (proxy to Buffer)
5780    pub fn set_status_message(&mut self, message: String) {
5781        if let Some(buffer) = self.current_buffer_mut() {
5782            buffer.set_status_message(message);
5783        }
5784    }
5785
5786    /// Get status message (proxy to Buffer)
5787    pub fn get_status_message(&self) -> String {
5788        self.current_buffer()
5789            .map(super::buffer::BufferAPI::get_status_message)
5790            .unwrap_or_default()
5791    }
5792
5793    /// Set dataview (proxy to Buffer)
5794    pub fn set_dataview(&mut self, dataview: Option<crate::data::data_view::DataView>) {
5795        if let Some(buffer) = self.current_buffer_mut() {
5796            buffer.set_dataview(dataview);
5797        }
5798    }
5799
5800    /// Get dataview (proxy to Buffer)
5801    pub fn get_dataview(&self) -> Option<&crate::data::data_view::DataView> {
5802        self.current_buffer()?.dataview.as_ref()
5803    }
5804
5805    /// Set last results row (proxy to Buffer)
5806    pub fn set_last_results_row(&mut self, row: Option<usize>) {
5807        if let Some(buffer) = self.current_buffer_mut() {
5808            buffer.set_last_results_row(row);
5809        }
5810    }
5811
5812    /// Set last scroll offset (proxy to Buffer)
5813    pub fn set_last_scroll_offset(&mut self, offset: (usize, usize)) {
5814        if let Some(buffer) = self.current_buffer_mut() {
5815            buffer.set_last_scroll_offset(offset);
5816        }
5817    }
5818
5819    /// Get input text (proxy to Buffer)
5820    pub fn get_input_text(&self) -> String {
5821        self.current_buffer()
5822            .map(super::buffer::BufferAPI::get_input_text)
5823            .unwrap_or_default()
5824    }
5825
5826    /// Get input cursor position (proxy to Buffer)
5827    pub fn get_input_cursor_position(&self) -> usize {
5828        self.current_buffer()
5829            .map_or(0, super::buffer::BufferAPI::get_input_cursor_position)
5830    }
5831
5832    /// Get last query (proxy to Buffer)
5833    pub fn get_last_query(&self) -> String {
5834        self.current_buffer()
5835            .map(super::buffer::BufferAPI::get_last_query)
5836            .unwrap_or_default()
5837    }
5838
5839    // Note: set_input_text, set_input_cursor_position, get_scroll_offset,
5840    // and set_scroll_offset already exist in this file
5841
5842    /// Check if buffer is modified (proxy to Buffer)
5843    pub fn is_buffer_modified(&self) -> bool {
5844        self.current_buffer()
5845            .is_some_and(super::buffer::BufferAPI::is_modified)
5846    }
5847
5848    /// Set buffer modified state (proxy to Buffer)
5849    pub fn set_buffer_modified(&mut self, modified: bool) {
5850        if let Some(buffer) = self.current_buffer_mut() {
5851            buffer.set_modified(modified);
5852        }
5853    }
5854
5855    /// Get dataview (proxy to Buffer) - returns reference
5856    pub fn get_buffer_dataview(&self) -> Option<&crate::data::data_view::DataView> {
5857        self.current_buffer()?.dataview.as_ref()
5858    }
5859
5860    /// Get mutable dataview (proxy to Buffer)
5861    pub fn get_buffer_dataview_mut(&mut self) -> Option<&mut crate::data::data_view::DataView> {
5862        self.current_buffer_mut()?.dataview.as_mut()
5863    }
5864
5865    /// Get original source `DataTable` (proxy to Buffer)
5866    pub fn get_original_source(&self) -> Option<&crate::data::datatable::DataTable> {
5867        self.current_buffer()?.get_original_source()
5868    }
5869
5870    /// Check if buffer has dataview (proxy to Buffer)
5871    pub fn has_dataview(&self) -> bool {
5872        self.current_buffer()
5873            .is_some_and(super::buffer::BufferAPI::has_dataview)
5874    }
5875
5876    /// Check if case insensitive mode (proxy to Buffer)
5877    pub fn is_case_insensitive(&self) -> bool {
5878        self.current_buffer()
5879            .is_some_and(super::buffer::BufferAPI::is_case_insensitive)
5880    }
5881
5882    /// Get edit mode (proxy to Buffer)
5883    pub fn get_edit_mode(&self) -> Option<crate::buffer::EditMode> {
5884        self.current_buffer()
5885            .map(super::buffer::BufferAPI::get_edit_mode)
5886    }
5887
5888    /// Check if show row numbers (proxy to Buffer)
5889    pub fn is_show_row_numbers(&self) -> bool {
5890        self.current_buffer()
5891            .is_some_and(super::buffer::BufferAPI::is_show_row_numbers)
5892    }
5893
5894    /// Check if compact mode (proxy to Buffer)
5895    pub fn is_compact_mode(&self) -> bool {
5896        self.current_buffer()
5897            .is_some_and(super::buffer::BufferAPI::is_compact_mode)
5898    }
5899
5900    /// Set input cursor position (proxy to Buffer)
5901    pub fn set_input_cursor_position(&mut self, pos: usize) {
5902        if let Some(buffer) = self.current_buffer_mut() {
5903            // Use the correct method that updates input_manager
5904            buffer.set_input_cursor_position(pos);
5905        }
5906
5907        // Also update command_input for compatibility
5908        self.command_input.borrow_mut().cursor_position = pos;
5909    }
5910
5911    /// Set search pattern (proxy to Buffer)
5912    pub fn set_search_pattern(&mut self, pattern: String) {
5913        if let Some(buffer) = self.current_buffer_mut() {
5914            buffer.set_search_pattern(pattern);
5915        }
5916    }
5917
5918    /// Set filter pattern (proxy to Buffer)
5919    pub fn set_filter_pattern(&mut self, pattern: String) {
5920        if let Some(buffer) = self.current_buffer_mut() {
5921            buffer.set_filter_pattern(pattern);
5922        }
5923    }
5924
5925    /// Set fuzzy filter pattern (proxy to Buffer)
5926    pub fn set_fuzzy_filter_pattern(&mut self, pattern: String) {
5927        if let Some(buffer) = self.current_buffer_mut() {
5928            buffer.set_fuzzy_filter_pattern(pattern);
5929        }
5930    }
5931
5932    /// Set fuzzy filter active (proxy to Buffer)
5933    pub fn set_fuzzy_filter_active(&mut self, active: bool) {
5934        if let Some(buffer) = self.current_buffer_mut() {
5935            buffer.set_fuzzy_filter_active(active);
5936        }
5937    }
5938
5939    /// Is fuzzy filter active (proxy to Buffer)
5940    pub fn is_fuzzy_filter_active(&self) -> bool {
5941        self.current_buffer()
5942            .is_some_and(super::buffer::BufferAPI::is_fuzzy_filter_active)
5943    }
5944
5945    /// Set fuzzy filter indices (proxy to Buffer)
5946    pub fn set_fuzzy_filter_indices(&mut self, indices: Vec<usize>) {
5947        if let Some(buffer) = self.current_buffer_mut() {
5948            buffer.set_fuzzy_filter_indices(indices);
5949        }
5950    }
5951
5952    /// Is kill ring empty (proxy to Buffer)
5953    pub fn is_kill_ring_empty(&self) -> bool {
5954        self.current_buffer()
5955            .is_none_or(super::buffer::BufferAPI::is_kill_ring_empty)
5956    }
5957
5958    /// Set selected row (proxy to Buffer)
5959    pub fn set_selected_row(&mut self, row: Option<usize>) {
5960        if let Some(buffer) = self.current_buffer_mut() {
5961            buffer.set_selected_row(row);
5962        }
5963    }
5964
5965    /// Set input text (proxy to Buffer) - properly syncs both buffer and `command_input`
5966    pub fn set_buffer_input_text(&mut self, text: String) {
5967        // Update the actual buffer
5968        if let Some(buffer) = self.current_buffer_mut() {
5969            buffer.set_input_text(text.clone());
5970        }
5971
5972        // Also update command_input for compatibility
5973        let mut input = self.command_input.borrow_mut();
5974        input.text = text.clone();
5975        input.cursor_position = text.len();
5976    }
5977
5978    /// Get input text (proxy to Buffer)
5979    pub fn get_buffer_input_text(&self) -> String {
5980        self.current_buffer()
5981            .map(super::buffer::BufferAPI::get_input_text)
5982            .unwrap_or_default()
5983    }
5984
5985    /// Set input text with cursor position (proxy to Buffer) - properly syncs both
5986    pub fn set_buffer_input_text_with_cursor(&mut self, text: String, cursor: usize) {
5987        // Update the actual buffer
5988        if let Some(buffer) = self.current_buffer_mut() {
5989            buffer.set_input_text(text.clone());
5990            buffer.set_input_cursor_position(cursor);
5991        }
5992
5993        // Also update command_input for compatibility
5994        let mut input = self.command_input.borrow_mut();
5995        input.text = text;
5996        input.cursor_position = cursor;
5997    }
5998
5999    /// Set current column (proxy to Buffer)
6000    pub fn set_current_column_buffer(&mut self, col: usize) {
6001        if let Some(buffer) = self.current_buffer_mut() {
6002            buffer.set_current_column(col);
6003        }
6004    }
6005
6006    /// Set show row numbers (proxy to Buffer)
6007    pub fn set_show_row_numbers(&mut self, show: bool) {
6008        if let Some(buffer) = self.current_buffer_mut() {
6009            buffer.set_show_row_numbers(show);
6010        }
6011    }
6012
6013    /// Set filter active (proxy to Buffer)
6014    pub fn set_filter_active(&mut self, active: bool) {
6015        if let Some(buffer) = self.current_buffer_mut() {
6016            buffer.set_filter_active(active);
6017        }
6018    }
6019
6020    /// Set compact mode (proxy to Buffer)
6021    pub fn set_compact_mode(&mut self, compact: bool) {
6022        if let Some(buffer) = self.current_buffer_mut() {
6023            buffer.set_compact_mode(compact);
6024        }
6025    }
6026
6027    /// Set case insensitive (proxy to Buffer)
6028    pub fn set_case_insensitive(&mut self, insensitive: bool) {
6029        if let Some(buffer) = self.current_buffer_mut() {
6030            buffer.set_case_insensitive(insensitive);
6031        }
6032    }
6033
6034    /// Get selected row from buffer (proxy to Buffer)
6035    pub fn get_buffer_selected_row(&self) -> Option<usize> {
6036        self.current_buffer()?.get_selected_row()
6037    }
6038
6039    /// Get search pattern from buffer (proxy to Buffer)
6040    pub fn get_search_pattern(&self) -> String {
6041        self.current_buffer()
6042            .map(super::buffer::BufferAPI::get_search_pattern)
6043            .unwrap_or_default()
6044    }
6045
6046    /// Get fuzzy filter pattern (proxy to Buffer)
6047    pub fn get_fuzzy_filter_pattern(&self) -> String {
6048        self.current_buffer()
6049            .map(super::buffer::BufferAPI::get_fuzzy_filter_pattern)
6050            .unwrap_or_default()
6051    }
6052
6053    // ========== VIM SEARCH OPERATIONS ==========
6054
6055    /// Check if vim search should handle the current key
6056    pub fn vim_search_should_handle_key(&self) -> bool {
6057        let mode = self.get_mode();
6058        let pattern = self.get_search_pattern();
6059
6060        // Handle keys if in search mode or have an active search pattern
6061        mode == AppMode::Search || !pattern.is_empty()
6062    }
6063
6064    /// Start vim search mode
6065    pub fn start_vim_search(&mut self) {
6066        self.set_mode(AppMode::Search);
6067        self.set_input_text(String::new());
6068        self.set_input_cursor_position(0);
6069        self.set_status_message("Search: /".to_string());
6070    }
6071
6072    /// Exit vim search mode
6073    pub fn exit_vim_search(&mut self) {
6074        self.set_mode(AppMode::Results);
6075        self.clear_search_state();
6076        self.set_status_message("Search mode exited".to_string());
6077    }
6078
6079    /// Get fuzzy filter indices (proxy to Buffer)
6080    pub fn get_fuzzy_filter_indices(&self) -> Vec<usize> {
6081        self.current_buffer()
6082            .map(|b| b.get_fuzzy_filter_indices().clone())
6083            .unwrap_or_default()
6084    }
6085
6086    /// Set scroll offset (proxy to Buffer)
6087    pub fn set_scroll_offset(&mut self, offset: (usize, usize)) {
6088        if let Some(buffer) = self.current_buffer_mut() {
6089            buffer.set_scroll_offset(offset);
6090        }
6091    }
6092
6093    // ==================== GROUPED OPERATIONS ====================
6094    // These combine multiple buffer operations that are commonly done together
6095
6096    /// Save state for undo (proxy to Buffer)
6097    pub fn save_state_for_undo(&mut self) {
6098        if let Some(buffer) = self.current_buffer_mut() {
6099            buffer.save_state_for_undo();
6100        }
6101    }
6102
6103    /// Perform undo (proxy to Buffer)
6104    pub fn perform_undo(&mut self) -> bool {
6105        self.current_buffer_mut()
6106            .is_some_and(super::buffer::BufferAPI::perform_undo)
6107    }
6108
6109    /// Perform redo (proxy to Buffer)
6110    pub fn perform_redo(&mut self) -> bool {
6111        self.current_buffer_mut()
6112            .is_some_and(super::buffer::BufferAPI::perform_redo)
6113    }
6114
6115    /// Insert character at cursor position
6116    pub fn insert_char_at_cursor(&mut self, c: char) {
6117        if let Some(buffer) = self.current_buffer_mut() {
6118            buffer.save_state_for_undo();
6119            let pos = buffer.get_input_cursor_position();
6120            let mut text = buffer.get_input_text();
6121            let mut chars: Vec<char> = text.chars().collect();
6122            chars.insert(pos, c);
6123            text = chars.iter().collect();
6124            buffer.set_input_text(text);
6125            buffer.set_input_cursor_position(pos + 1);
6126        }
6127    }
6128
6129    /// Handle input key (proxy to Buffer)
6130    pub fn handle_input_key(&mut self, key: crossterm::event::KeyEvent) -> bool {
6131        self.current_buffer_mut()
6132            .is_some_and(|b| b.handle_input_key(key))
6133    }
6134
6135    /// Set search matches and update state
6136    pub fn set_search_matches_with_index(&mut self, matches: Vec<(usize, usize)>, index: usize) {
6137        if let Some(buffer) = self.current_buffer_mut() {
6138            buffer.set_search_matches(matches);
6139            buffer.set_search_match_index(index);
6140        }
6141    }
6142
6143    /// Clear search state completely
6144    pub fn clear_search_state(&mut self) {
6145        if let Some(buffer) = self.current_buffer_mut() {
6146            buffer.set_search_matches(Vec::new());
6147            buffer.set_status_message("No matches found".to_string());
6148        }
6149    }
6150
6151    /// Set last state (results row and scroll offset)
6152    pub fn set_last_state(&mut self, row: Option<usize>, scroll_offset: (usize, usize)) {
6153        if let Some(buffer) = self.current_buffer_mut() {
6154            buffer.set_last_results_row(row);
6155            buffer.set_last_scroll_offset(scroll_offset);
6156        }
6157    }
6158
6159    /// Clear line - save undo state and clear input (grouped operation)
6160    pub fn clear_line(&mut self) {
6161        if let Some(buffer) = self.current_buffer_mut() {
6162            buffer.save_state_for_undo();
6163            buffer.set_input_text(String::new());
6164            buffer.set_input_cursor_position(0);
6165        }
6166    }
6167
6168    /// Move cursor left (grouped operation)
6169    pub fn move_input_cursor_left(&mut self) {
6170        if let Some(buffer) = self.current_buffer_mut() {
6171            let pos = buffer.get_input_cursor_position();
6172            if pos > 0 {
6173                buffer.set_input_cursor_position(pos - 1);
6174            }
6175        }
6176    }
6177
6178    /// Move cursor right (grouped operation)
6179    pub fn move_input_cursor_right(&mut self) {
6180        if let Some(buffer) = self.current_buffer_mut() {
6181            let pos = buffer.get_input_cursor_position();
6182            let text_len = buffer.get_input_text().chars().count();
6183            if pos < text_len {
6184                buffer.set_input_cursor_position(pos + 1);
6185            }
6186        }
6187    }
6188
6189    /// Backspace operation (grouped)
6190    pub fn backspace(&mut self) {
6191        if let Some(buffer) = self.current_buffer_mut() {
6192            let pos = buffer.get_input_cursor_position();
6193            if pos > 0 {
6194                buffer.save_state_for_undo();
6195                let mut text = buffer.get_input_text();
6196                let mut chars: Vec<char> = text.chars().collect();
6197                chars.remove(pos - 1);
6198                text = chars.iter().collect();
6199                buffer.set_input_text(text);
6200                buffer.set_input_cursor_position(pos - 1);
6201            }
6202        }
6203    }
6204
6205    /// Delete character at cursor (grouped)
6206    pub fn delete(&mut self) {
6207        if let Some(buffer) = self.current_buffer_mut() {
6208            let pos = buffer.get_input_cursor_position();
6209            let mut text = buffer.get_input_text();
6210            let chars_len = text.chars().count();
6211            if pos < chars_len {
6212                buffer.save_state_for_undo();
6213                let mut chars: Vec<char> = text.chars().collect();
6214                chars.remove(pos);
6215                text = chars.iter().collect();
6216                buffer.set_input_text(text);
6217            }
6218        }
6219    }
6220
6221    /// Reset navigation state (grouped operation)
6222    pub fn reset_navigation_state(&mut self) {
6223        if let Some(buffer) = self.current_buffer_mut() {
6224            buffer.set_selected_row(Some(0));
6225            buffer.set_scroll_offset((0, 0));
6226            buffer.set_current_column(0);
6227            buffer.set_last_results_row(None);
6228            buffer.set_last_scroll_offset((0, 0));
6229        }
6230    }
6231
6232    /// Clear fuzzy filter (grouped operation)
6233    pub fn clear_fuzzy_filter_state(&mut self) {
6234        if let Some(buffer) = self.current_buffer_mut() {
6235            buffer.clear_fuzzy_filter();
6236            buffer.set_fuzzy_filter_pattern(String::new());
6237            buffer.set_fuzzy_filter_active(false);
6238            buffer.set_fuzzy_filter_indices(Vec::new());
6239        }
6240    }
6241
6242    /// Get filter pattern (proxy to Buffer)
6243    pub fn get_filter_pattern(&self) -> String {
6244        self.current_buffer()
6245            .map(super::buffer::BufferAPI::get_filter_pattern)
6246            .unwrap_or_default()
6247    }
6248
6249    /// Set column statistics (proxy to Buffer)
6250    pub fn set_column_stats(&mut self, stats: Option<crate::buffer::ColumnStatistics>) {
6251        if let Some(buffer) = self.current_buffer_mut() {
6252            buffer.set_column_stats(stats);
6253        }
6254    }
6255
6256    /// Set column widths (proxy to Buffer)
6257    pub fn set_column_widths(&mut self, widths: Vec<u16>) {
6258        if let Some(buffer) = self.current_buffer_mut() {
6259            buffer.set_column_widths(widths);
6260        }
6261    }
6262
6263    /// Set current match (proxy to Buffer)
6264    pub fn set_current_match(&mut self, match_pos: Option<(usize, usize)>) {
6265        if let Some(buffer) = self.current_buffer_mut() {
6266            buffer.set_current_match(match_pos);
6267        }
6268    }
6269
6270    /// Get kill ring (proxy to Buffer)
6271    pub fn get_kill_ring(&self) -> String {
6272        self.current_buffer()
6273            .map(super::buffer::BufferAPI::get_kill_ring)
6274            .unwrap_or_default()
6275    }
6276
6277    /// Get status message (proxy to Buffer)
6278    pub fn get_buffer_status_message(&self) -> String {
6279        self.current_buffer()
6280            .map(super::buffer::BufferAPI::get_status_message)
6281            .unwrap_or_default()
6282    }
6283
6284    /// Get buffer name (proxy to Buffer)
6285    pub fn get_buffer_name(&self) -> String {
6286        self.current_buffer().map_or_else(
6287            || "No Buffer".to_string(),
6288            super::buffer::BufferAPI::get_name,
6289        )
6290    }
6291
6292    /// Get last results row (proxy to Buffer)
6293    pub fn get_last_results_row(&self) -> Option<usize> {
6294        self.current_buffer()?.get_last_results_row()
6295    }
6296
6297    /// Get last scroll offset (proxy to Buffer)
6298    pub fn get_last_scroll_offset(&self) -> (usize, usize) {
6299        self.current_buffer()
6300            .map_or((0, 0), super::buffer::BufferAPI::get_last_scroll_offset)
6301    }
6302
6303    /// Set last query (proxy to Buffer)
6304    pub fn set_last_query(&mut self, query: String) {
6305        if let Some(buffer) = self.current_buffer_mut() {
6306            buffer.set_last_query(query);
6307        }
6308    }
6309
6310    /// Get last query source (proxy to Buffer)
6311    pub fn get_last_query_source(&self) -> Option<String> {
6312        self.current_buffer()?.get_last_query_source()
6313    }
6314
6315    /// Set last visible rows (proxy to Buffer)
6316    pub fn set_last_visible_rows(&mut self, rows: usize) {
6317        if let Some(buffer) = self.current_buffer_mut() {
6318            buffer.set_last_visible_rows(rows);
6319        }
6320    }
6321}
6322
6323impl Default for AppStateContainer {
6324    fn default() -> Self {
6325        // Create a minimal AppStateContainer for testing
6326        // Uses CommandHistory::default() which handles errors gracefully
6327        let command_history = CommandHistory::default();
6328        let mut widgets = WidgetStates::new();
6329        widgets.set_history(HistoryWidget::new(command_history.clone()));
6330
6331        Self {
6332            buffers: BufferManager::new(),
6333            current_buffer_id: 0,
6334            command_input: RefCell::new(InputState::new()),
6335            search: RefCell::new(SearchState::new()),
6336            filter: RefCell::new(FilterState::new()),
6337            column_search: RefCell::new(ColumnSearchState::new()),
6338            history_search: RefCell::new(HistorySearchState::new()),
6339            sort: RefCell::new(SortState::new()),
6340            selection: RefCell::new(SelectionState::new()),
6341            completion: RefCell::new(CompletionState::default()),
6342            widgets,
6343            cache_list: CacheListState::new(),
6344            column_stats: ColumnStatsState::new(),
6345            jump_to_row: JumpToRowState::new(),
6346            navigation: RefCell::new(NavigationState::new()),
6347            command_history: RefCell::new(command_history),
6348            key_press_history: RefCell::new(KeyPressHistory::new(100)),
6349            results: RefCell::new(ResultsState::default()),
6350            clipboard: RefCell::new(ClipboardState::default()),
6351            chord: RefCell::new(ChordState::default()),
6352            undo_redo: RefCell::new(UndoRedoState::default()),
6353            scroll: RefCell::new(ScrollState::default()),
6354            results_cache: ResultsCache::new(100),
6355            mode_stack: Vec::new(),
6356            debug_enabled: false,
6357            debug_service: RefCell::new(None),
6358            help: RefCell::new(HelpState::new()),
6359        }
6360    }
6361}
6362
6363impl fmt::Debug for AppStateContainer {
6364    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
6365        f.debug_struct("AppStateContainer")
6366            .field("current_mode", &self.current_mode())
6367            .field("mode_stack", &self.mode_stack)
6368            // TODO: Add buffer count when method is available
6369            // .field("buffer_count", &self.buffers.count())
6370            .field("current_buffer_id", &self.current_buffer_id)
6371            .field("command_input", &self.command_input)
6372            .field("search_active", &self.search.borrow().is_active)
6373            .field("filter_active", &self.filter.borrow().is_active)
6374            .field(
6375                "column_search_active",
6376                &self.column_search.borrow().is_active,
6377            )
6378            .field("debug_enabled", &self.debug_enabled)
6379            .field("help_visible", &self.help.borrow().is_visible)
6380            .field("help_scroll", &self.help.borrow().scroll_offset)
6381            .field("cached_results", &self.results_cache.cache.len())
6382            .field(
6383                "history_count",
6384                &self.command_history.borrow().get_all().len(),
6385            )
6386            .finish()
6387    }
6388}
6389
6390impl fmt::Debug for WidgetStates {
6391    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
6392        f.debug_struct("WidgetStates")
6393            .field("search_modes_active", &self.search_modes.is_active())
6394            .field("history", &self.history.is_some())
6395            .field("help", &"HelpWidget")
6396            .field("stats", &"StatsWidget")
6397            // .field("debug", &"DebugWidget") // TODO: Add when available
6398            .finish()
6399    }
6400}