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