Skip to main content

sql_cli/ui/
enhanced_tui.rs

1// UI Layout Constants
2const TABLE_BORDER_WIDTH: u16 = 4; // Left border (1) + right border (1) + padding (2)
3const INPUT_AREA_HEIGHT: u16 = 3; // Height of the command input area
4const STATUS_BAR_HEIGHT: u16 = 3; // Height of the status bar
5const TOTAL_UI_CHROME: u16 = INPUT_AREA_HEIGHT + STATUS_BAR_HEIGHT; // Total non-table UI height
6const TABLE_CHROME_ROWS: u16 = 3; // Table header (1) + top border (1) + bottom border (1)
7use crate::app_state_container::{AppStateContainer, SelectionMode};
8use crate::buffer::{
9    AppMode, Buffer, BufferAPI, BufferManager, ColumnStatistics, ColumnType, EditMode,
10};
11use crate::buffer_handler::BufferHandler;
12use crate::config::config::Config;
13use crate::core::search_manager::{SearchConfig, SearchManager};
14use crate::cursor_manager::CursorManager;
15use crate::data::adapters::BufferAdapter;
16use crate::data::data_analyzer::DataAnalyzer;
17use crate::data::data_provider::DataProvider;
18use crate::data::data_view::DataView;
19use crate::debug::{DebugRegistry, MemoryTracker};
20use crate::debug_service::DebugService;
21use crate::help_text::HelpText;
22use crate::services::QueryOrchestrator;
23use crate::sql::hybrid_parser::HybridParser;
24use crate::sql_highlighter::SqlHighlighter;
25use crate::state::StateDispatcher;
26use crate::ui::debug::DebugContext;
27use crate::ui::input::action_handlers::ActionHandlerContext;
28use crate::ui::input::actions::{Action, ActionContext, ActionResult};
29use crate::ui::key_handling::{
30    format_key_for_display, ChordResult, KeyChordHandler, KeyDispatcher, KeyMapper,
31    KeyPressIndicator, KeySequenceRenderer,
32};
33use crate::ui::rendering::table_widget_manager::TableWidgetManager;
34use crate::ui::search::vim_search_adapter::VimSearchAdapter;
35use crate::ui::state::shadow_state::ShadowStateManager;
36use crate::ui::traits::{
37    BufferManagementBehavior, ColumnBehavior, InputBehavior, NavigationBehavior, YankBehavior,
38};
39use crate::ui::viewport::ColumnPackingMode;
40use crate::ui::viewport_manager::{ViewportEfficiency, ViewportManager};
41use crate::utils::logging::LogRingBuffer;
42use crate::widget_traits::DebugInfoProvider;
43use crate::widgets::debug_widget::DebugWidget;
44use crate::widgets::editor_widget::{BufferAction, EditorAction, EditorWidget};
45use crate::widgets::help_widget::HelpWidget;
46use crate::widgets::search_modes_widget::{SearchMode, SearchModesAction, SearchModesWidget};
47use crate::widgets::stats_widget::StatsWidget;
48use crate::widgets::tab_bar_widget::TabBarWidget;
49use crate::{buffer, data_analyzer, dual_logging};
50use anyhow::Result;
51use crossterm::{
52    event::{
53        self, DisableMouseCapture, EnableMouseCapture, Event, KeyCode, KeyEvent, KeyModifiers,
54    },
55    execute,
56    terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen},
57};
58use std::cell::RefCell;
59
60use ratatui::{
61    backend::{Backend, CrosstermBackend},
62    layout::{Constraint, Direction, Layout, Rect},
63    style::{Color, Modifier, Style},
64    text::{Line, Span, Text},
65    widgets::{Block, Borders, Paragraph},
66    Frame, Terminal,
67};
68use std::io;
69use std::sync::Arc;
70use tracing::{debug, error, info, trace, warn};
71use tui_input::{backend::crossterm::EventHandler, Input};
72
73/// `CommandEditor` handles all command mode input and state management
74/// This is the first step in extracting command mode from the main TUI
75struct CommandEditor {
76    // References to shared state (will be passed in from TUI)
77    input: Input,
78
79    // Command-specific state
80    _scroll_offset: usize,
81    _last_cursor_position: usize,
82    _history_search_term: Option<String>,
83}
84
85impl CommandEditor {
86    fn new() -> Self {
87        Self {
88            input: Input::default(),
89            _scroll_offset: 0,
90            _last_cursor_position: 0,
91            _history_search_term: None,
92        }
93    }
94
95    /// Handle command mode input, returning true if the app should exit
96    fn handle_input(
97        &mut self,
98        key: KeyEvent,
99        _state_container: &mut AppStateContainer,
100        _shadow_state: &RefCell<crate::ui::state::shadow_state::ShadowStateManager>,
101    ) -> Result<bool> {
102        debug!(
103            "CommandEditor::handle_input - key: {:?}, current text: '{}', cursor: {}",
104            key,
105            self.input.value(),
106            self.input.cursor()
107        );
108
109        // Handle comprehensive text editing operations
110        match key.code {
111            // === Character Input ===
112            KeyCode::Char(c) => {
113                if key.modifiers.is_empty() || key.modifiers == KeyModifiers::SHIFT {
114                    // Simple character input
115                    let before = self.input.value().to_string();
116                    let before_cursor = self.input.cursor();
117                    self.input.handle_event(&Event::Key(key));
118                    let after = self.input.value().to_string();
119                    let after_cursor = self.input.cursor();
120                    debug!(
121                        "CommandEditor processed char '{}': text '{}' -> '{}', cursor {} -> {}",
122                        c, before, after, before_cursor, after_cursor
123                    );
124                    return Ok(false);
125                }
126
127                // === Ctrl Key Combinations ===
128                if key.modifiers.contains(KeyModifiers::CONTROL) {
129                    match c {
130                        'a' | 'A' => {
131                            // Move to beginning of line
132                            self.input = self.input.clone().with_cursor(0);
133                            return Ok(false);
134                        }
135                        'e' | 'E' => {
136                            // Move to end of line
137                            let len = self.input.value().len();
138                            self.input = self.input.clone().with_cursor(len);
139                            return Ok(false);
140                        }
141                        'k' | 'K' => {
142                            // Kill line - delete from cursor to end
143                            let cursor = self.input.cursor();
144                            let text = self.input.value();
145                            if cursor < text.len() {
146                                let new_text = text[..cursor].to_string();
147                                self.input = Input::from(new_text).with_cursor(cursor);
148                            }
149                            return Ok(false);
150                        }
151                        'u' | 'U' => {
152                            // Kill line backward - delete from start to cursor
153                            let cursor = self.input.cursor();
154                            let text = self.input.value();
155                            if cursor > 0 {
156                                let new_text = text[cursor..].to_string();
157                                self.input = Input::from(new_text).with_cursor(0);
158                            }
159                            return Ok(false);
160                        }
161                        'w' | 'W' => {
162                            // Delete word backward
163                            self.delete_word_backward();
164                            return Ok(false);
165                        }
166                        'd' | 'D' => {
167                            // Delete word forward (if at word boundary)
168                            self.delete_word_forward();
169                            return Ok(false);
170                        }
171                        'v' | 'V' => {
172                            // Paste from clipboard
173                            // We need to handle this at the parent level since clipboard access
174                            // requires state_container. Return a special indicator.
175                            // For now, just don't handle it here so it falls through to parent
176                            debug!("CommandEditor: Ctrl+V detected, letting parent handle clipboard paste");
177                        }
178                        'y' | 'Y' => {
179                            // Yank (paste from kill ring)
180                            // Similar to Ctrl+V, needs parent handling
181                            debug!("CommandEditor: Ctrl+Y detected, letting parent handle yank");
182                        }
183                        _ => {}
184                    }
185                }
186
187                // === Alt Key Combinations ===
188                if key.modifiers.contains(KeyModifiers::ALT) {
189                    match c {
190                        'b' | 'B' => {
191                            // Move word backward
192                            self.move_word_backward();
193                            return Ok(false);
194                        }
195                        'f' | 'F' => {
196                            debug!("CommandEditor: Alt+F - Move word forward");
197                            // Move word forward
198                            self.move_word_forward();
199                            return Ok(false);
200                        }
201                        'd' | 'D' => {
202                            // Delete word forward
203                            self.delete_word_forward();
204                            return Ok(false);
205                        }
206                        _ => {}
207                    }
208                }
209            }
210
211            // === Basic Navigation and Editing ===
212            KeyCode::Backspace => {
213                self.input.handle_event(&Event::Key(key));
214                return Ok(false);
215            }
216            KeyCode::Delete => {
217                self.input.handle_event(&Event::Key(key));
218                return Ok(false);
219            }
220            KeyCode::Left => {
221                if key.modifiers.contains(KeyModifiers::CONTROL) {
222                    self.move_word_backward();
223                } else {
224                    self.input.handle_event(&Event::Key(key));
225                }
226                return Ok(false);
227            }
228            KeyCode::Right => {
229                if key.modifiers.contains(KeyModifiers::CONTROL) {
230                    self.move_word_forward();
231                } else {
232                    self.input.handle_event(&Event::Key(key));
233                }
234                return Ok(false);
235            }
236            KeyCode::Home => {
237                self.input = self.input.clone().with_cursor(0);
238                return Ok(false);
239            }
240            KeyCode::End => {
241                let len = self.input.value().len();
242                self.input = self.input.clone().with_cursor(len);
243                return Ok(false);
244            }
245
246            // === Tab Completion ===
247            KeyCode::Tab => {
248                // Tab completion needs access to full TUI state
249                // Let parent handle it by not returning early
250            }
251
252            _ => {}
253        }
254
255        // Key not handled by CommandEditor
256        Ok(false)
257    }
258
259    // === Helper Methods for Text Operations ===
260
261    fn delete_word_backward(&mut self) {
262        let cursor = self.input.cursor();
263        let text = self.input.value();
264
265        if cursor == 0 {
266            return;
267        }
268
269        // Find start of current/previous word
270        let chars: Vec<char> = text.chars().collect();
271        let mut pos = cursor;
272
273        // Skip trailing spaces
274        while pos > 0 && chars[pos - 1].is_whitespace() {
275            pos -= 1;
276        }
277
278        // Skip word characters
279        while pos > 0 && !chars[pos - 1].is_whitespace() {
280            pos -= 1;
281        }
282
283        // Delete from pos to cursor
284        let new_text = format!("{}{}", &text[..pos], &text[cursor..]);
285        self.input = Input::from(new_text).with_cursor(pos);
286    }
287
288    fn delete_word_forward(&mut self) {
289        let cursor = self.input.cursor();
290        let text = self.input.value();
291
292        if cursor >= text.len() {
293            return;
294        }
295
296        // Find end of current/next word
297        let chars: Vec<char> = text.chars().collect();
298        let mut pos = cursor;
299
300        // Skip word characters
301        while pos < chars.len() && !chars[pos].is_whitespace() {
302            pos += 1;
303        }
304
305        // Skip trailing spaces
306        while pos < chars.len() && chars[pos].is_whitespace() {
307            pos += 1;
308        }
309
310        // Delete from cursor to pos
311        let new_text = format!("{}{}", &text[..cursor], &text[pos..]);
312        self.input = Input::from(new_text).with_cursor(cursor);
313    }
314
315    fn move_word_backward(&mut self) {
316        let cursor = self.input.cursor();
317        let text = self.input.value();
318
319        if cursor == 0 {
320            return;
321        }
322
323        let chars: Vec<char> = text.chars().collect();
324        let mut pos = cursor;
325
326        // Skip trailing spaces
327        while pos > 0 && chars[pos - 1].is_whitespace() {
328            pos -= 1;
329        }
330
331        // Skip word characters
332        while pos > 0 && !chars[pos - 1].is_whitespace() {
333            pos -= 1;
334        }
335
336        self.input = self.input.clone().with_cursor(pos);
337    }
338
339    fn move_word_forward(&mut self) {
340        let cursor = self.input.cursor();
341        let text = self.input.value();
342
343        if cursor >= text.len() {
344            return;
345        }
346
347        let chars: Vec<char> = text.chars().collect();
348        let mut pos = cursor;
349
350        // Skip word characters
351        while pos < chars.len() && !chars[pos].is_whitespace() {
352            pos += 1;
353        }
354
355        // Skip trailing spaces
356        while pos < chars.len() && chars[pos].is_whitespace() {
357            pos += 1;
358        }
359
360        self.input = self.input.clone().with_cursor(pos);
361    }
362
363    /// Get the current input text
364    fn get_text(&self) -> String {
365        self.input.value().to_string()
366    }
367
368    /// Set the input text
369    fn set_text(&mut self, text: String) {
370        self.input = Input::from(text);
371    }
372
373    /// Get the current cursor position
374    fn get_cursor(&self) -> usize {
375        self.input.cursor()
376    }
377
378    /// Set the cursor position
379    fn set_cursor(&mut self, pos: usize) {
380        let text = self.input.value().to_string();
381        self.input = tui_input::Input::new(text).with_cursor(pos);
382    }
383}
384
385pub struct EnhancedTuiApp {
386    // State container - manages all state (owned directly, no Arc needed)
387    state_container: AppStateContainer,
388    // Debug service for logging (ServiceContainer removed)
389    debug_service: Option<DebugService>,
390
391    input: Input,
392    command_editor: CommandEditor, // New: Handles command mode input and state
393    cursor_manager: CursorManager, // New: manages cursor/navigation logic
394    data_analyzer: DataAnalyzer,   // New: manages data analysis/statistics
395    hybrid_parser: HybridParser,
396
397    // Configuration
398    config: Config,
399
400    sql_highlighter: SqlHighlighter,
401    pub(crate) debug_widget: DebugWidget,
402    editor_widget: EditorWidget,
403    stats_widget: StatsWidget,
404    help_widget: HelpWidget,
405    search_modes_widget: SearchModesWidget,
406    vim_search_adapter: RefCell<VimSearchAdapter>, // State-aware vim search adapter
407    search_manager: RefCell<SearchManager>,        // New: Centralized search logic
408    state_dispatcher: RefCell<StateDispatcher>,    // Coordinates state changes
409    key_chord_handler: KeyChordHandler,            // Manages key sequences and history
410    key_dispatcher: KeyDispatcher,                 // Maps keys to actions
411    key_mapper: KeyMapper,                         // New action-based key mapping system
412
413    // Buffer management now in AppStateContainer
414    // buffer_manager field removed - using state_container.buffers() instead
415    buffer_handler: BufferHandler, // Handles buffer operations like switching
416
417    // Performance tracking
418    pub(crate) navigation_timings: Vec<String>, // Track last N navigation timings for debugging
419    pub(crate) render_timings: Vec<String>,     // Track last N render timings for debugging
420    // Cache
421    log_buffer: Option<LogRingBuffer>, // Ring buffer for debug logs
422
423    // Data source tracking
424    data_source: Option<String>, // e.g., "trades.csv", "data.json", "https://api.example.com"
425
426    // Visual enhancements
427    key_indicator: KeyPressIndicator,
428    key_sequence_renderer: KeySequenceRenderer,
429
430    // Viewport management (RefCell for interior mutability during render)
431    pub(crate) viewport_manager: RefCell<Option<ViewportManager>>,
432    viewport_efficiency: RefCell<Option<ViewportEfficiency>>,
433
434    // Shadow state manager for observing state transitions
435    shadow_state: RefCell<crate::ui::state::shadow_state::ShadowStateManager>,
436
437    // Table widget manager for centralized table state/rendering
438    table_widget_manager: RefCell<TableWidgetManager>,
439
440    // Services
441    query_orchestrator: QueryOrchestrator,
442
443    // Debug system
444    pub(crate) debug_registry: DebugRegistry,
445    pub(crate) memory_tracker: MemoryTracker,
446}
447
448impl DebugContext for EnhancedTuiApp {
449    fn buffer(&self) -> &dyn BufferAPI {
450        self.state_container
451            .current_buffer()
452            .expect("Buffer should exist")
453    }
454
455    fn buffer_mut(&mut self) -> &mut dyn BufferAPI {
456        self.state_container
457            .current_buffer_mut()
458            .expect("Buffer should exist")
459    }
460
461    fn get_debug_widget(&self) -> &DebugWidget {
462        &self.debug_widget
463    }
464
465    fn get_debug_widget_mut(&mut self) -> &mut DebugWidget {
466        &mut self.debug_widget
467    }
468
469    fn get_shadow_state(&self) -> &RefCell<ShadowStateManager> {
470        &self.shadow_state
471    }
472
473    fn get_buffer_manager(&self) -> &BufferManager {
474        self.state_container.buffers()
475    }
476
477    fn get_viewport_manager(&self) -> &RefCell<Option<ViewportManager>> {
478        &self.viewport_manager
479    }
480
481    fn get_state_container(&self) -> &AppStateContainer {
482        &self.state_container
483    }
484
485    fn get_state_container_mut(&mut self) -> &mut AppStateContainer {
486        &mut self.state_container
487    }
488
489    fn get_navigation_timings(&self) -> &Vec<String> {
490        &self.navigation_timings
491    }
492
493    fn get_render_timings(&self) -> &Vec<String> {
494        &self.render_timings
495    }
496
497    fn debug_current_buffer(&mut self) {
498        // Debug output disabled - was corrupting TUI display
499        // Use tracing/logging instead if debugging is needed
500    }
501
502    fn get_input_cursor(&self) -> usize {
503        EnhancedTuiApp::get_input_cursor(self)
504    }
505
506    fn get_visual_cursor(&self) -> (usize, usize) {
507        EnhancedTuiApp::get_visual_cursor(self)
508    }
509
510    fn get_input_text(&self) -> String {
511        EnhancedTuiApp::get_input_text(self)
512    }
513
514    fn get_buffer_mut_if_available(&mut self) -> Option<&mut Buffer> {
515        self.state_container.buffers_mut().current_mut()
516    }
517
518    fn set_mode_via_shadow_state(&mut self, mode: AppMode, trigger: &str) {
519        if let Some(buffer) = self.state_container.buffers_mut().current_mut() {
520            debug!(
521                "set_mode_via_shadow_state: Setting mode to {:?} with trigger '{}'",
522                mode, trigger
523            );
524            self.shadow_state
525                .borrow_mut()
526                .set_mode(mode, buffer, trigger);
527        } else {
528            error!(
529                "set_mode_via_shadow_state: No buffer available! Cannot set mode to {:?}",
530                mode
531            );
532        }
533    }
534
535    fn collect_current_state(
536        &self,
537    ) -> (AppMode, String, String, Option<usize>, usize, usize, usize) {
538        if let Some(buffer) = self.state_container.buffers().current() {
539            let mode = buffer.get_mode();
540            if mode == AppMode::Debug {
541                (mode, String::new(), String::new(), None, 0, 0, 0)
542            } else {
543                (
544                    mode,
545                    buffer.get_last_query(),
546                    buffer.get_input_text(),
547                    buffer.get_selected_row(),
548                    self.state_container.get_current_column(),
549                    buffer.get_dataview().map_or(0, |v| v.source().row_count()),
550                    buffer
551                        .get_dataview()
552                        .map_or(0, super::super::data::data_view::DataView::row_count),
553                )
554            }
555        } else {
556            (
557                AppMode::Command,
558                String::new(),
559                String::new(),
560                None,
561                0,
562                0,
563                0,
564            )
565        }
566    }
567
568    fn format_buffer_manager_state(&self) -> String {
569        let buffer_names: Vec<String> = self
570            .state_container
571            .buffers()
572            .all_buffers()
573            .iter()
574            .map(super::super::buffer::BufferAPI::get_name)
575            .collect();
576        let buffer_count = self.state_container.buffers().all_buffers().len();
577        let buffer_index = self.state_container.buffers().current_index();
578
579        format!(
580            "\n========== BUFFER MANAGER STATE ==========\n\
581            Number of Buffers: {}\n\
582            Current Buffer Index: {}\n\
583            Buffer Names: {}\n",
584            buffer_count,
585            buffer_index,
586            buffer_names.join(", ")
587        )
588    }
589
590    fn debug_generate_viewport_efficiency(&self) -> String {
591        if let Some(ref efficiency) = *self.viewport_efficiency.borrow() {
592            let mut result = String::from("\n========== VIEWPORT EFFICIENCY ==========\n");
593            result.push_str(&efficiency.to_debug_string());
594            result.push_str("\n==========================================\n");
595            result
596        } else {
597            String::new()
598        }
599    }
600
601    fn debug_generate_key_chord_info(&self) -> String {
602        let mut result = String::from("\n");
603        result.push_str(&self.key_chord_handler.format_debug_info());
604        result.push_str("========================================\n");
605        result
606    }
607
608    fn debug_generate_search_modes_info(&self) -> String {
609        let mut result = String::from("\n");
610        result.push_str(&self.search_modes_widget.debug_info());
611        result
612    }
613
614    fn debug_generate_state_container_info(&self) -> String {
615        let mut result = String::from("\n");
616        result.push_str(&self.state_container.debug_dump());
617        result.push('\n');
618        result
619    }
620
621    fn collect_debug_info(&self) -> String {
622        // Simplified version - the full version is in toggle_debug_mode
623        let mut debug_info = String::new();
624        debug_info
625            .push_str(&self.debug_generate_parser_info(&self.state_container.get_input_text()));
626        debug_info.push_str(&self.debug_generate_memory_info());
627        debug_info
628    }
629
630    // Forward all the debug generation methods to the existing implementations
631    fn debug_generate_parser_info(&self, query: &str) -> String {
632        EnhancedTuiApp::debug_generate_parser_info(self, query)
633    }
634
635    fn debug_generate_navigation_state(&self) -> String {
636        // Call the actual implementation method on self (defined below in impl EnhancedTuiApp)
637        Self::debug_generate_navigation_state(self)
638    }
639
640    fn debug_generate_column_search_state(&self) -> String {
641        // Call the actual implementation method on self (defined below in impl EnhancedTuiApp)
642        Self::debug_generate_column_search_state(self)
643    }
644
645    fn debug_generate_trace_logs(&self) -> String {
646        // Call the actual implementation method on self (defined below in impl EnhancedTuiApp)
647        Self::debug_generate_trace_logs(self)
648    }
649
650    fn debug_generate_state_logs(&self) -> String {
651        // Call the actual implementation method on self (defined below in impl EnhancedTuiApp)
652        Self::debug_generate_state_logs(self)
653    }
654}
655
656impl EnhancedTuiApp {
657    // ========== STATE ACCESS ==========
658    /// Get immutable reference to state container for debug purposes
659    pub(crate) fn state_container(&self) -> &AppStateContainer {
660        &self.state_container
661    }
662
663    /// Get mutable reference to state container for debug purposes  
664    pub(crate) fn state_container_mut(&mut self) -> &mut AppStateContainer {
665        &mut self.state_container
666    }
667
668    // ========== STATE SYNCHRONIZATION ==========
669
670    /// Synchronize `NavigationState` with `ViewportManager`
671    /// This is the reverse - update `NavigationState` from `ViewportManager`
672    pub(crate) fn sync_navigation_with_viewport(&self) {
673        debug!(target: "column_search_sync", "sync_navigation_with_viewport: ENTRY");
674        let viewport_borrow = self.viewport_manager.borrow();
675
676        if let Some(viewport) = viewport_borrow.as_ref() {
677            let mut nav = self.state_container.navigation_mut();
678
679            // Log current state before sync
680            debug!(target: "column_search_sync", "sync_navigation_with_viewport: BEFORE - nav.selected_column: {}, viewport.crosshair_col: {}", 
681                nav.selected_column, viewport.get_crosshair_col());
682            debug!(target: "column_search_sync", "sync_navigation_with_viewport: BEFORE - nav.selected_row: {}, viewport.crosshair_row: {}", 
683                nav.selected_row, viewport.get_crosshair_row());
684
685            // Update NavigationState from ViewportManager's authoritative position
686            nav.selected_row = viewport.get_selected_row();
687            nav.selected_column = viewport.get_selected_column();
688            nav.scroll_offset = viewport.get_scroll_offset();
689
690            // Log state after sync
691            debug!(target: "column_search_sync", "sync_navigation_with_viewport: AFTER - nav.selected_column: {}, nav.selected_row: {}", 
692                nav.selected_column, nav.selected_row);
693            debug!(target: "column_search_sync", "sync_navigation_with_viewport: Successfully synced NavigationState with ViewportManager");
694        } else {
695            debug!(target: "column_search_sync", "sync_navigation_with_viewport: No ViewportManager available to sync with");
696        }
697        debug!(target: "column_search_sync", "sync_navigation_with_viewport: EXIT");
698    }
699
700    /// Synchronize mode across all state containers
701    /// This ensures `AppStateContainer`, Buffer, and `ShadowState` are all in sync
702    fn sync_mode(&mut self, mode: AppMode, trigger: &str) {
703        debug!(target: "column_search_sync", "sync_mode: ENTRY - mode: {:?}, trigger: '{}'", mode, trigger);
704        // Delegate to StateCoordinator for centralized sync logic
705        use crate::ui::state::state_coordinator::StateCoordinator;
706        StateCoordinator::sync_mode_with_refs(
707            &mut self.state_container,
708            &self.shadow_state,
709            mode,
710            trigger,
711        );
712        debug!(target: "column_search_sync", "sync_mode: EXIT - StateCoordinator::sync_mode_with_refs completed");
713    }
714
715    /// Save current `ViewportManager` state to the current buffer
716    fn save_viewport_to_current_buffer(&mut self) {
717        let viewport_borrow = self.viewport_manager.borrow();
718
719        if let Some(viewport) = viewport_borrow.as_ref() {
720            if let Some(buffer) = self.state_container.buffers_mut().current_mut() {
721                // Save crosshair position
722                buffer.set_selected_row(Some(viewport.get_selected_row()));
723                buffer.set_current_column(viewport.get_selected_column());
724
725                // Save scroll offset
726                buffer.set_scroll_offset(viewport.get_scroll_offset());
727            }
728        }
729    }
730
731    /// Restore `ViewportManager` state from the current buffer
732    fn restore_viewport_from_current_buffer(&mut self) {
733        if let Some(buffer) = self.state_container.buffers().current() {
734            // Check if this buffer has a DataView
735            if let Some(dataview) = buffer.get_dataview() {
736                // Check if we need to create or update ViewportManager
737                let needs_new_viewport = {
738                    let viewport_borrow = self.viewport_manager.borrow();
739                    viewport_borrow.is_none()
740                };
741
742                if needs_new_viewport {
743                    // Create new ViewportManager for this buffer's DataView
744                    self.viewport_manager =
745                        RefCell::new(Some(ViewportManager::new(Arc::new(dataview.clone()))));
746                } else {
747                    // Update existing ViewportManager with new DataView
748                    let mut viewport_borrow = self.viewport_manager.borrow_mut();
749                    if let Some(ref mut viewport) = viewport_borrow.as_mut() {
750                        viewport.set_dataview(Arc::new(dataview.clone()));
751                    }
752                }
753
754                // Now restore the position
755                let mut viewport_borrow = self.viewport_manager.borrow_mut();
756                if let Some(ref mut viewport) = viewport_borrow.as_mut() {
757                    // The data dimensions are already updated by set_dataview above
758                    // Just restore the saved position
759
760                    // Restore crosshair position
761                    let row = buffer.get_selected_row().unwrap_or(0);
762                    let col = buffer.get_current_column();
763                    viewport.set_crosshair(row, col);
764
765                    // Restore scroll offset
766                    let scroll_offset = buffer.get_scroll_offset();
767                    viewport.set_scroll_offset(scroll_offset.0, scroll_offset.1);
768
769                    // Update terminal size to trigger viewport recalculation
770                    let term_width = viewport.get_terminal_width();
771                    let term_height = viewport.get_terminal_height() as u16;
772                    viewport.update_terminal_size(term_width, term_height);
773
774                    // Also update NavigationState for consistency
775                    drop(viewport_borrow);
776                    self.sync_navigation_with_viewport();
777                }
778
779                // Also update TableWidgetManager with the new DataView
780                let mut table_manager = self.table_widget_manager.borrow_mut();
781                table_manager.set_dataview(Arc::new(dataview.clone()));
782                table_manager.force_render(); // Force a re-render with new data
783            }
784        }
785    }
786
787    // ========== VIEWPORT CALCULATIONS ==========
788
789    /// Calculate the number of data rows available for the table
790    /// This accounts for all UI chrome (input area, status bar) and table chrome (header, borders)
791    fn calculate_available_data_rows(terminal_height: u16) -> u16 {
792        crate::ui::rendering::ui_layout_utils::calculate_available_data_rows(terminal_height)
793    }
794
795    /// Calculate the number of data rows available for a table area
796    /// This accounts only for table chrome (header, borders)
797    fn calculate_table_data_rows(table_area_height: u16) -> u16 {
798        crate::ui::rendering::ui_layout_utils::calculate_table_data_rows(table_area_height)
799    }
800
801    // ========== BUFFER MANAGEMENT ==========
802
803    /// Get current buffer if available (for reading)
804    fn current_buffer(&self) -> Option<&dyn buffer::BufferAPI> {
805        self.state_container
806            .buffers()
807            .current()
808            .map(|b| b as &dyn buffer::BufferAPI)
809    }
810
811    /// Get current buffer (panics if none exists)
812    /// Use this when we know a buffer should always exist
813    fn buffer(&self) -> &dyn buffer::BufferAPI {
814        self.current_buffer()
815            .expect("No buffer available - this should not happen")
816    }
817
818    // ========== ACTION CONTEXT ==========
819
820    /// Build action context from current state
821    fn build_action_context(&self) -> ActionContext {
822        let nav = self.state_container.navigation();
823        let dataview = self.state_container.get_buffer_dataview();
824
825        ActionContext {
826            mode: self.state_container.get_mode(),
827            selection_mode: self.state_container.get_selection_mode(),
828            has_results: dataview.is_some(),
829            has_filter: !self.state_container.get_filter_pattern().is_empty()
830                || !self.state_container.get_fuzzy_filter_pattern().is_empty(),
831            has_search: !self.state_container.get_search_pattern().is_empty()
832                || self
833                    .vim_search_adapter
834                    .borrow()
835                    .should_handle_key(&self.state_container)
836                || self.state_container.column_search().is_active,
837            row_count: dataview.as_ref().map_or(0, |v| v.row_count()),
838            column_count: dataview.as_ref().map_or(0, |v| v.column_count()),
839            current_row: nav.selected_row,
840            current_column: nav.selected_column,
841        }
842    }
843
844    // ========== ACTION HANDLERS ==========
845
846    /// Try to handle an action using the new action system
847    fn try_handle_action(
848        &mut self,
849        action: Action,
850        context: &ActionContext,
851    ) -> Result<ActionResult> {
852        // First, try the visitor pattern action handlers
853        // Create a temporary dispatcher to avoid borrowing conflicts
854        let temp_dispatcher = crate::ui::input::action_handlers::ActionDispatcher::new();
855        match temp_dispatcher.dispatch(&action, context, self) {
856            Ok(ActionResult::NotHandled) => {
857                // Action not handled by visitor pattern, fall back to existing switch
858                // This allows for gradual migration
859            }
860            result => {
861                // Action was handled (successfully or with error) by visitor pattern
862                return result;
863            }
864        }
865
866        use Action::{
867            Backspace, CycleColumnPacking, Delete, ExecuteQuery, ExitCurrentMode, HideEmptyColumns,
868            InsertChar, MoveCursorEnd, MoveCursorHome, MoveCursorLeft, MoveCursorRight,
869            NextSearchMatch, PreviousSearchMatch, Redo, ShowColumnStatistics, Sort,
870            StartHistorySearch, SwitchMode, SwitchModeWithCursor, Undo,
871        };
872
873        // Fallback to existing switch statement for actions not yet in visitor pattern
874        match action {
875            // The following actions are now handled by visitor pattern handlers:
876            // - Navigation: NavigationActionHandler
877            // - Toggle operations: ToggleActionHandler (ToggleSelectionMode, ToggleRowNumbers, etc.)
878            // - Clear operations: ClearActionHandler (ClearFilter, ClearLine)
879            // - Exit operations: ExitActionHandler (Quit, ForceQuit)
880            // - Column operations: ColumnActionHandler
881            // - Export operations: ExportActionHandler
882            // - Yank operations: YankActionHandler
883            // - UI operations: UIActionHandler (ShowHelp)
884            // - Debug/Viewport operations: DebugViewportActionHandler (ShowDebugInfo, StartJumpToRow, ToggleCursorLock, ToggleViewportLock)
885
886            // NextColumn and PreviousColumn are now handled by NavigationActionHandler in visitor pattern
887            Sort(_column_idx) => {
888                // For now, always sort by current column (like 's' key does)
889                self.toggle_sort_current_column();
890                Ok(ActionResult::Handled)
891            }
892            // HideColumn and UnhideAllColumns are now handled by ColumnActionHandler in visitor pattern
893            HideEmptyColumns => {
894                tracing::info!("HideEmptyColumns action triggered");
895
896                // Use ViewportManager to hide empty columns
897                let (count, updated_dataview) = {
898                    let mut viewport_manager_borrow = self.viewport_manager.borrow_mut();
899                    if let Some(ref mut viewport_manager) = *viewport_manager_borrow {
900                        tracing::debug!("ViewportManager available, checking for empty columns");
901                        let count = viewport_manager.hide_empty_columns();
902                        if count > 0 {
903                            (count, Some(viewport_manager.clone_dataview()))
904                        } else {
905                            (count, None)
906                        }
907                    } else {
908                        tracing::warn!("No ViewportManager available to hide columns");
909                        (0, None)
910                    }
911                };
912
913                // Sync the updated DataView back to the Buffer if columns were hidden
914                if let Some(updated_dataview) = updated_dataview {
915                    self.state_container.set_dataview(Some(updated_dataview));
916                }
917
918                tracing::info!("Hidden {} empty columns", count);
919                let message = if count > 0 {
920                    format!("Hidden {count} empty columns (press Ctrl+Shift+H to unhide)")
921                } else {
922                    "No empty columns found".to_string()
923                };
924                self.state_container.set_status_message(message);
925                Ok(ActionResult::Handled)
926            }
927            // MoveColumnLeft, MoveColumnRight - handled by ColumnArrangementActionHandler
928            // StartSearch, StartColumnSearch, StartFilter, StartFuzzyFilter - handled by ModeActionHandler
929            ExitCurrentMode => {
930                // Handle escape based on current mode
931                match context.mode {
932                    AppMode::Results => {
933                        // VimSearchAdapter now handles Escape in Results mode
934                        // If we get here, search wasn't active, so switch to Command mode
935                        // Save current position before switching to Command mode
936                        if let Some(selected) = self.state_container.get_table_selected_row() {
937                            self.state_container.set_last_results_row(Some(selected));
938                            let scroll_offset = self.state_container.get_scroll_offset();
939                            self.state_container.set_last_scroll_offset(scroll_offset);
940                        }
941
942                        // Restore the last executed query to input_text for editing
943                        let last_query = self.state_container.get_last_query();
944                        let current_input = self.state_container.get_input_text();
945                        debug!(target: "mode", "Exiting Results mode: current input_text='{}', last_query='{}'", current_input, last_query);
946
947                        if !last_query.is_empty() {
948                            debug!(target: "buffer", "Restoring last_query to input_text: '{}'", last_query);
949                            // Use the helper method to sync all three input states
950                            self.set_input_text(last_query.clone());
951                        } else if !current_input.is_empty() {
952                            debug!(target: "buffer", "No last_query but input_text has content, keeping: '{}'", current_input);
953                        } else {
954                            debug!(target: "buffer", "No last_query to restore when exiting Results mode");
955                        }
956
957                        debug!(target: "mode", "Switching from Results to Command mode");
958                        self.state_container.set_mode(AppMode::Command);
959                        self.shadow_state
960                            .borrow_mut()
961                            .observe_mode_change(AppMode::Command, "escape_from_results");
962                        self.state_container.set_table_selected_row(None);
963                    }
964                    AppMode::Help => {
965                        // Return to previous mode (usually Results)
966                        // Use proper mode synchronization
967                        self.set_mode_via_shadow_state(AppMode::Results, "escape_from_help");
968                        self.state_container.set_help_visible(false);
969                    }
970                    AppMode::Debug => {
971                        // Return to Results mode
972                        // Use proper mode synchronization
973                        self.set_mode_via_shadow_state(AppMode::Results, "escape_from_debug");
974                    }
975                    _ => {
976                        // For other modes, generally go back to Command
977                        // Use proper mode synchronization
978                        self.set_mode_via_shadow_state(AppMode::Command, "escape_to_command");
979                    }
980                }
981                Ok(ActionResult::Handled)
982            }
983            SwitchMode(target_mode) => {
984                // Switch to the specified mode
985                // For Command->Results, only switch if we have results
986                if target_mode == AppMode::Results && !context.has_results {
987                    // Can't switch to Results mode without results
988                    self.state_container.set_status_message(
989                        "No results to display. Run a query first.".to_string(),
990                    );
991                    Ok(ActionResult::Handled)
992                } else {
993                    self.state_container.set_mode(target_mode.clone());
994
995                    // Observe the mode change in shadow state
996                    let trigger = match target_mode {
997                        AppMode::Command => "switch_to_command",
998                        AppMode::Results => "switch_to_results",
999                        AppMode::Help => "switch_to_help",
1000                        AppMode::History => "switch_to_history",
1001                        _ => "switch_mode",
1002                    };
1003                    self.shadow_state
1004                        .borrow_mut()
1005                        .observe_mode_change(target_mode.clone(), trigger);
1006
1007                    let msg = match target_mode {
1008                        AppMode::Command => "Command mode - Enter SQL queries",
1009                        AppMode::Results => {
1010                            "Results mode - Navigate with arrows/hjkl, Tab for command"
1011                        }
1012                        _ => "",
1013                    };
1014                    if !msg.is_empty() {
1015                        self.state_container.set_status_message(msg.to_string());
1016                    }
1017                    Ok(ActionResult::Handled)
1018                }
1019            }
1020            SwitchModeWithCursor(target_mode, cursor_position) => {
1021                use crate::ui::input::actions::{CursorPosition, SqlClause};
1022
1023                // Switch to the target mode
1024                self.state_container.set_mode(target_mode.clone());
1025
1026                // Observe the mode change in shadow state
1027                let trigger = match cursor_position {
1028                    CursorPosition::End => "a_key_pressed",
1029                    CursorPosition::Current => "i_key_pressed",
1030                    CursorPosition::AfterClause(_) => "clause_navigation",
1031                };
1032                self.shadow_state
1033                    .borrow_mut()
1034                    .observe_mode_change(target_mode.clone(), trigger);
1035
1036                // Position the cursor based on the requested position
1037                match cursor_position {
1038                    CursorPosition::Current => {
1039                        // Keep cursor where it is (do nothing)
1040                    }
1041                    CursorPosition::End => {
1042                        // Move cursor to end of input
1043                        let text = self.state_container.get_buffer_input_text();
1044                        let text_len = text.len();
1045                        self.state_container.set_input_cursor_position(text_len);
1046                        // Also sync the TUI's input field
1047                        self.input = tui_input::Input::new(text).with_cursor(text_len);
1048                    }
1049                    CursorPosition::AfterClause(clause) => {
1050                        // Use the SQL parser to find the clause position
1051                        let input_text = self.state_container.get_input_text();
1052
1053                        // Use the lexer to tokenize with positions
1054                        use crate::sql::recursive_parser::Lexer;
1055                        let mut lexer = Lexer::new(&input_text);
1056                        let tokens = lexer.tokenize_all_with_positions();
1057
1058                        // Find the position after the specified clause
1059                        let mut cursor_pos = None;
1060                        for i in 0..tokens.len() {
1061                            let (_, end_pos, ref token) = tokens[i];
1062
1063                            // Check if this token matches the clause we're looking for
1064                            use crate::sql::recursive_parser::Token;
1065                            let clause_matched = match (&clause, token) {
1066                                (SqlClause::Select, Token::Select) => true,
1067                                (SqlClause::From, Token::From) => true,
1068                                (SqlClause::Where, Token::Where) => true,
1069                                (SqlClause::OrderBy, Token::OrderBy) => true,
1070                                (SqlClause::GroupBy, Token::GroupBy) => true,
1071                                (SqlClause::Having, Token::Having) => true,
1072                                (SqlClause::Limit, Token::Limit) => true,
1073                                _ => false,
1074                            };
1075
1076                            if clause_matched {
1077                                // Find the end of this clause (before the next keyword or end of query)
1078                                let mut clause_end = end_pos;
1079
1080                                // Skip to the next significant token after the clause keyword
1081                                for j in (i + 1)..tokens.len() {
1082                                    let (_, token_end, ref next_token) = tokens[j];
1083
1084                                    // Stop at the next SQL clause keyword
1085                                    match next_token {
1086                                        Token::Select
1087                                        | Token::From
1088                                        | Token::Where
1089                                        | Token::OrderBy
1090                                        | Token::GroupBy
1091                                        | Token::Having
1092                                        | Token::Limit => break,
1093                                        _ => clause_end = token_end,
1094                                    }
1095                                }
1096
1097                                cursor_pos = Some(clause_end);
1098                                break;
1099                            }
1100                        }
1101
1102                        // If we found the clause, position cursor after it
1103                        // If not found, append at the end with the clause
1104                        if let Some(pos) = cursor_pos {
1105                            self.state_container.set_input_cursor_position(pos);
1106                            // Also sync the TUI's input field
1107                            let text = self.state_container.get_buffer_input_text();
1108                            self.input = tui_input::Input::new(text).with_cursor(pos);
1109                        } else {
1110                            // Clause not found, append it at the end
1111                            let clause_text = match clause {
1112                                SqlClause::Where => " WHERE ",
1113                                SqlClause::OrderBy => " ORDER BY ",
1114                                SqlClause::GroupBy => " GROUP BY ",
1115                                SqlClause::Having => " HAVING ",
1116                                SqlClause::Limit => " LIMIT ",
1117                                SqlClause::Select => "SELECT ",
1118                                SqlClause::From => " FROM ",
1119                            };
1120
1121                            let mut new_text = self.state_container.get_input_text();
1122                            new_text.push_str(clause_text);
1123                            let cursor_pos = new_text.len();
1124                            // Use the helper method that syncs everything
1125                            self.set_input_text_with_cursor(new_text, cursor_pos);
1126                        }
1127                    }
1128                }
1129
1130                // Update status message
1131                let msg = match target_mode {
1132                    AppMode::Command => "Command mode - Enter SQL queries",
1133                    _ => "",
1134                };
1135                if !msg.is_empty() {
1136                    self.state_container.set_status_message(msg.to_string());
1137                }
1138
1139                Ok(ActionResult::Handled)
1140            }
1141
1142            // Editing actions - only work in Command mode
1143            // MoveCursorLeft is now handled by InputCursorActionHandler in visitor pattern
1144            MoveCursorLeft => Ok(ActionResult::NotHandled),
1145            // MoveCursorRight is now handled by InputCursorActionHandler in visitor pattern
1146            MoveCursorRight => Ok(ActionResult::NotHandled),
1147            // MoveCursorHome is now handled by InputCursorActionHandler in visitor pattern
1148            MoveCursorHome => Ok(ActionResult::NotHandled),
1149            // MoveCursorEnd is now handled by InputCursorActionHandler in visitor pattern
1150            MoveCursorEnd => Ok(ActionResult::NotHandled),
1151            // Backspace is now handled by TextEditActionHandler in visitor pattern
1152            Backspace => Ok(ActionResult::NotHandled),
1153            // Delete is now handled by TextEditActionHandler in visitor pattern
1154            Delete => Ok(ActionResult::NotHandled),
1155            // ClearLine is now handled by ClearActionHandler in visitor pattern
1156            // Undo is now handled by TextEditActionHandler in visitor pattern
1157            Undo => Ok(ActionResult::NotHandled),
1158            // Redo is now handled by TextEditActionHandler in visitor pattern
1159            Redo => Ok(ActionResult::NotHandled),
1160            ExecuteQuery => {
1161                if context.mode == AppMode::Command {
1162                    // Delegate to existing execute query logic
1163                    self.handle_execute_query()?;
1164                    Ok(ActionResult::Handled)
1165                } else {
1166                    Ok(ActionResult::NotHandled)
1167                }
1168            }
1169            InsertChar(c) => {
1170                if context.mode == AppMode::Command {
1171                    self.state_container.insert_char_at_cursor(c);
1172
1173                    // Clear completion state when typing
1174                    self.state_container.clear_completion();
1175
1176                    // Handle completion
1177                    self.handle_completion();
1178
1179                    Ok(ActionResult::Handled)
1180                } else {
1181                    Ok(ActionResult::NotHandled)
1182                }
1183            }
1184
1185            NextSearchMatch => {
1186                // n key: navigate to next search match only if search is active (not after Escape)
1187                debug!(target: "column_search_sync", "NextSearchMatch: 'n' key pressed, checking if search navigation should be handled");
1188                // Use StateCoordinator to determine if search navigation should be handled
1189                use crate::ui::state::state_coordinator::StateCoordinator;
1190                let should_handle = StateCoordinator::should_handle_next_match(
1191                    &self.state_container,
1192                    Some(&self.vim_search_adapter),
1193                );
1194                debug!(target: "column_search_sync", "NextSearchMatch: StateCoordinator::should_handle_next_match returned: {}", should_handle);
1195                if should_handle {
1196                    debug!(target: "column_search_sync", "NextSearchMatch: Calling vim_search_next()");
1197                    self.vim_search_next();
1198                    debug!(target: "column_search_sync", "NextSearchMatch: vim_search_next() completed");
1199                } else {
1200                    debug!(target: "column_search_sync", "NextSearchMatch: No active search (or cancelled with Escape), ignoring 'n' key");
1201                }
1202                Ok(ActionResult::Handled)
1203            }
1204            PreviousSearchMatch => {
1205                // Shift+N behavior: search navigation only if vim search is active, otherwise toggle row numbers
1206                // Use StateCoordinator to determine if search navigation should be handled
1207                use crate::ui::state::state_coordinator::StateCoordinator;
1208                if StateCoordinator::should_handle_previous_match(
1209                    &self.state_container,
1210                    Some(&self.vim_search_adapter),
1211                ) {
1212                    self.vim_search_previous();
1213                } else {
1214                    // Delegate to the ToggleRowNumbers action for consistency
1215                    return self.try_handle_action(Action::ToggleRowNumbers, context);
1216                }
1217                Ok(ActionResult::Handled)
1218            }
1219            ShowColumnStatistics => {
1220                self.calculate_column_statistics();
1221                Ok(ActionResult::Handled)
1222            }
1223            StartHistorySearch => {
1224                use crate::ui::state::state_coordinator::StateCoordinator;
1225
1226                // Get current input before delegating
1227                let current_input = self.get_input_text();
1228
1229                // Use StateCoordinator for all state transitions
1230                let (input_to_use, _match_count) = StateCoordinator::start_history_search_with_refs(
1231                    &mut self.state_container,
1232                    &self.shadow_state,
1233                    current_input,
1234                );
1235
1236                // Update input if it changed (e.g., from Results mode)
1237                if input_to_use != self.get_input_text() {
1238                    self.set_input_text(input_to_use);
1239                }
1240
1241                // Initialize with schema context (implementation stays in TUI)
1242                self.update_history_matches_in_container();
1243
1244                Ok(ActionResult::Handled)
1245            }
1246            CycleColumnPacking => {
1247                let message = {
1248                    let mut viewport_manager_borrow = self.viewport_manager.borrow_mut();
1249                    let viewport_manager = viewport_manager_borrow
1250                        .as_mut()
1251                        .expect("ViewportManager must exist");
1252                    let new_mode = viewport_manager.cycle_packing_mode();
1253                    format!("Column packing: {}", new_mode.display_name())
1254                };
1255                self.state_container.set_status_message(message);
1256                Ok(ActionResult::Handled)
1257            }
1258            _ => {
1259                // Action not yet implemented in new system
1260                Ok(ActionResult::NotHandled)
1261            }
1262        }
1263    }
1264
1265    // ========== DATA PROVIDER ACCESS ==========
1266
1267    /// Get a `DataProvider` view of the current buffer
1268    /// This allows using the new trait-based data access pattern
1269    fn get_data_provider(&self) -> Option<Box<dyn DataProvider + '_>> {
1270        // For now, we'll use BufferAdapter for Buffer data
1271        // In the future, we can check data source type and return appropriate adapter
1272        if let Some(buffer) = self.state_container.buffers().current() {
1273            // V51: Check for DataView first, then DataTable
1274            if buffer.has_dataview() {
1275                return Some(Box::new(BufferAdapter::new(buffer)));
1276            }
1277        }
1278        None
1279    }
1280
1281    // Note: edit_mode methods removed - use buffer directly
1282
1283    // ========== INPUT MANAGEMENT ==========
1284
1285    // Helper to get input text from buffer or fallback to direct input
1286    fn get_input_text(&self) -> String {
1287        // For special modes that use the input field for their own purposes
1288        if self.shadow_state.borrow().is_in_search_mode() {
1289            // These modes temporarily use the input field for their patterns
1290            self.input.value().to_string() // TODO: Migrate to buffer-based input
1291        } else {
1292            // Get from the actual buffer to ensure consistency
1293            self.state_container.get_buffer_input_text()
1294        }
1295    }
1296
1297    // Helper to get cursor position from buffer or fallback to direct input
1298    fn get_input_cursor(&self) -> usize {
1299        // For special modes that use the input field directly
1300        if self.shadow_state.borrow().is_in_search_mode() {
1301            // These modes use the input field for their patterns
1302            self.input.cursor()
1303        } else {
1304            // All other modes use the buffer
1305            self.state_container.get_input_cursor_position()
1306        }
1307    }
1308
1309    // Helper to set input text through buffer and sync input field
1310    fn set_input_text(&mut self, text: String) {
1311        let old_text = self.state_container.get_input_text();
1312        let mode = self.shadow_state.borrow().get_mode();
1313
1314        // Log every input text change with context
1315        info!(target: "input", "SET_INPUT_TEXT: '{}' -> '{}' (mode: {:?})",
1316              if old_text.len() > 50 { format!("{}...", &old_text[..50]) } else { old_text.clone() },
1317              if text.len() > 50 { format!("{}...", &text[..50]) } else { text.clone() },
1318              mode);
1319
1320        // Use the proper proxy method that syncs both buffer and command_input
1321        self.state_container.set_buffer_input_text(text.clone());
1322
1323        // Always update the input field for all modes
1324        // TODO: Eventually migrate special modes to use buffer input
1325        self.input = tui_input::Input::new(text.clone()).with_cursor(text.len());
1326    }
1327
1328    // Helper to set input text with specific cursor position
1329    fn set_input_text_with_cursor(&mut self, text: String, cursor_pos: usize) {
1330        let old_text = self.state_container.get_buffer_input_text();
1331        let old_cursor = self.state_container.get_input_cursor_position();
1332        let mode = self.state_container.get_mode();
1333
1334        // Log every input text change with cursor position
1335        info!(target: "input", "SET_INPUT_TEXT_WITH_CURSOR: '{}' (cursor {}) -> '{}' (cursor {}) (mode: {:?})",
1336              if old_text.len() > 50 { format!("{}...", &old_text[..50]) } else { old_text.clone() },
1337              old_cursor,
1338              if text.len() > 50 { format!("{}...", &text[..50]) } else { text.clone() },
1339              cursor_pos,
1340              mode);
1341
1342        // Use the proper proxy method that syncs both buffer and command_input
1343        self.state_container
1344            .set_buffer_input_text_with_cursor(text.clone(), cursor_pos);
1345
1346        // Always update the input field for consistency
1347        // TODO: Eventually migrate special modes to use buffer input
1348        self.input = tui_input::Input::new(text.clone()).with_cursor(cursor_pos);
1349    }
1350
1351    // MASTER SYNC METHOD - Use this whenever input changes!
1352    // This ensures all three input states stay synchronized:
1353    // 1. Buffer's input_text and cursor
1354    // 2. self.input (tui_input widget)
1355    // 3. AppStateContainer's command_input
1356    fn sync_all_input_states(&mut self) {
1357        let text = self.state_container.get_input_text();
1358        let cursor = self.state_container.get_input_cursor_position();
1359        let mode = self.state_container.get_mode();
1360
1361        // Get caller for debugging
1362        let backtrace_str = std::backtrace::Backtrace::capture().to_string();
1363        let caller = backtrace_str
1364            .lines()
1365            .skip(3) // Skip backtrace frames
1366            .find(|line| line.contains("enhanced_tui") && !line.contains("sync_all_input_states"))
1367            .and_then(|line| line.split("::").last())
1368            .unwrap_or("unknown");
1369
1370        // Update the tui_input widget
1371        self.input = tui_input::Input::new(text.clone()).with_cursor(cursor);
1372
1373        // Sync with AppStateContainer
1374        self.state_container
1375            .set_input_text_with_cursor(text.clone(), cursor);
1376
1377        // Reset horizontal scroll to show the cursor properly
1378        // This fixes the issue where switching between queries of different lengths
1379        // leaves the scroll offset in the wrong position
1380        self.cursor_manager.reset_horizontal_scroll();
1381        self.state_container.scroll_mut().input_scroll_offset = 0;
1382
1383        // Update scroll to ensure cursor is visible
1384        self.update_horizontal_scroll(120); // Will be properly updated on next render
1385
1386        info!(target: "input", "SYNC_ALL [{}]: text='{}', cursor={}, mode={:?}, scroll_reset",
1387              caller,
1388              if text.len() > 50 { format!("{}...", &text[..50]) } else { text.clone() },
1389              cursor,
1390              mode);
1391    }
1392
1393    // Helper to handle key events in the input
1394    fn handle_input_key(&mut self, key: KeyEvent) -> bool {
1395        // For special modes that handle input directly
1396        let mode = self.shadow_state.borrow().get_mode();
1397        match mode {
1398            AppMode::Search | AppMode::Filter | AppMode::FuzzyFilter | AppMode::ColumnSearch => {
1399                self.input.handle_event(&Event::Key(key));
1400                false
1401            }
1402            _ => {
1403                // Route to buffer's input handling
1404                self.state_container.handle_input_key(key)
1405            }
1406        }
1407    }
1408    // ========== CURSOR AND SELECTION ==========
1409
1410    // Helper to get visual cursor position (for rendering)
1411    fn get_visual_cursor(&self) -> (usize, usize) {
1412        // Get text and cursor from appropriate source based on mode
1413        let mode = self.state_container.get_mode();
1414        let (text, cursor) = match mode {
1415            AppMode::Search | AppMode::Filter | AppMode::FuzzyFilter | AppMode::ColumnSearch => {
1416                // Special modes use self.input directly
1417                (self.input.value().to_string(), self.input.cursor())
1418            }
1419            _ => {
1420                // Other modes use state_container
1421                (
1422                    self.state_container.get_input_text(),
1423                    self.state_container.get_input_cursor_position(),
1424                )
1425            }
1426        };
1427
1428        let lines: Vec<&str> = text.split('\n').collect();
1429        let mut current_pos = 0;
1430        for (row, line) in lines.iter().enumerate() {
1431            if current_pos + line.len() >= cursor {
1432                return (row, cursor - current_pos);
1433            }
1434            current_pos += line.len() + 1; // +1 for newline
1435        }
1436        (0, cursor)
1437    }
1438
1439    fn get_selection_mode(&self) -> SelectionMode {
1440        self.state_container.get_selection_mode()
1441        // ========== UTILITY FUNCTIONS ==========
1442    }
1443
1444    #[must_use]
1445    pub fn new(api_url: &str) -> Self {
1446        // Load configuration
1447        let config = Config::load().unwrap_or_else(|_e| {
1448            // Config loading error - using defaults
1449            Config::default()
1450        });
1451
1452        // Initialize global config for function registry
1453        crate::config::global::init_config(config.clone());
1454
1455        // Store API URL as data source if provided
1456        let data_source = if api_url.is_empty() {
1457            None
1458        } else {
1459            Some(api_url.to_string())
1460        };
1461
1462        // Log initialization
1463        if let Some(logger) = dual_logging::get_dual_logger() {
1464            logger.log(
1465                "INFO",
1466                "EnhancedTuiApp",
1467                &format!("Initializing TUI with API URL: {api_url}"),
1468            );
1469        }
1470
1471        // Create buffer manager for the state container (no longer duplicated)
1472        let mut buffer_manager = BufferManager::new();
1473        let mut buffer = buffer::Buffer::new(1);
1474        // Sync initial settings from config
1475        buffer.set_case_insensitive(config.behavior.case_insensitive_default);
1476        buffer.set_compact_mode(config.display.compact_mode);
1477        buffer.set_show_row_numbers(config.display.show_row_numbers);
1478        buffer_manager.add_buffer(buffer);
1479
1480        // Initialize state container (owns the only buffer_manager)
1481        let state_container = match AppStateContainer::new(buffer_manager) {
1482            Ok(container) => container,
1483            Err(e) => {
1484                panic!("Failed to initialize AppStateContainer: {e}");
1485            }
1486        };
1487
1488        // Initialize debug service directly (no ServiceContainer needed)
1489        let debug_service = DebugService::new(1000); // Keep last 1000 debug entries
1490        debug_service.set_enabled(true); // Enable the debug service
1491        state_container.set_debug_service(debug_service.clone_service());
1492
1493        // Create help widget
1494        let help_widget = HelpWidget::new();
1495        // Note: help_widget.set_services() removed - ServiceContainer no longer exists
1496
1497        let mut app = Self {
1498            state_container,
1499            debug_service: Some(debug_service),
1500            input: Input::default(),
1501            command_editor: CommandEditor::new(),
1502            cursor_manager: CursorManager::new(),
1503            data_analyzer: DataAnalyzer::new(),
1504            hybrid_parser: HybridParser::new(),
1505            config: config.clone(),
1506            sql_highlighter: SqlHighlighter::new(),
1507            debug_widget: DebugWidget::new(),
1508            editor_widget: EditorWidget::new(),
1509            stats_widget: StatsWidget::new(),
1510            help_widget,
1511            search_modes_widget: SearchModesWidget::new(),
1512            vim_search_adapter: RefCell::new(VimSearchAdapter::new()),
1513            state_dispatcher: RefCell::new(StateDispatcher::new()),
1514            search_manager: RefCell::new({
1515                let mut search_config = SearchConfig::default();
1516                search_config.case_sensitive = !config.behavior.case_insensitive_default;
1517                SearchManager::with_config(search_config)
1518            }),
1519            key_chord_handler: KeyChordHandler::new(),
1520            key_dispatcher: KeyDispatcher::new(),
1521            key_mapper: KeyMapper::new(),
1522            // ========== INITIALIZATION ==========
1523            // CSV fields now in Buffer (buffer_manager in AppStateContainer)
1524            buffer_handler: BufferHandler::new(),
1525            navigation_timings: Vec::new(),
1526            render_timings: Vec::new(),
1527            log_buffer: dual_logging::get_dual_logger().map(|logger| logger.ring_buffer().clone()),
1528            data_source,
1529            key_indicator: {
1530                let mut indicator = KeyPressIndicator::new();
1531                if config.display.show_key_indicator {
1532                    indicator.set_enabled(true);
1533                }
1534                indicator
1535            },
1536            key_sequence_renderer: {
1537                let mut renderer = KeySequenceRenderer::new();
1538                if config.display.show_key_indicator {
1539                    renderer.set_enabled(true);
1540                }
1541                renderer
1542            },
1543            viewport_manager: RefCell::new(None), // Will be initialized when DataView is set
1544            viewport_efficiency: RefCell::new(None),
1545            shadow_state: RefCell::new(crate::ui::state::shadow_state::ShadowStateManager::new()),
1546            table_widget_manager: RefCell::new(TableWidgetManager::new()),
1547            query_orchestrator: QueryOrchestrator::with_behavior_config(config.behavior.clone()),
1548            debug_registry: DebugRegistry::new(),
1549            memory_tracker: MemoryTracker::new(100),
1550        };
1551
1552        // Set up state dispatcher
1553        app.setup_state_coordination();
1554
1555        app
1556    }
1557
1558    /// Set up state coordination between dispatcher and components
1559    fn setup_state_coordination(&mut self) {
1560        // Connect state dispatcher to current buffer
1561        if let Some(current_buffer) = self.state_container.buffers().current() {
1562            let buffer_rc = std::rc::Rc::new(std::cell::RefCell::new(current_buffer.clone()));
1563            self.state_dispatcher.borrow_mut().set_buffer(buffer_rc);
1564        }
1565
1566        // NOTE: We would add VimSearchAdapter as subscriber here, but it requires
1567        // moving the adapter out of RefCell temporarily. For now, we'll handle
1568        // this connection when events are dispatched.
1569
1570        info!("State coordination setup complete");
1571    }
1572
1573    /// Create a TUI with a `DataView` - no file loading knowledge needed
1574    /// This is the clean separation: TUI only knows about `DataViews`
1575    pub fn new_with_dataview(dataview: DataView, source_name: &str) -> Result<Self> {
1576        // Create the base app
1577        let mut app = Self::new("");
1578
1579        // Store the data source name
1580        app.data_source = Some(source_name.to_string());
1581
1582        // Create a buffer with the DataView
1583        app.state_container.buffers_mut().clear_all();
1584        let mut buffer = buffer::Buffer::new(1);
1585
1586        // IMPORTANT: First set the DataTable to preserve the original source
1587        // The DataView contains the full DataTable when initially loaded
1588        let source_table = dataview.source_arc();
1589
1590        // MEMORY AUDIT: Track memory before and after Arc sharing
1591        use crate::utils::memory_audit;
1592        use crate::utils::memory_tracker;
1593
1594        memory_tracker::track_memory("before_arc_share");
1595
1596        // Using Arc to share the same underlying DataTable (no cloning!)
1597        buffer.set_datatable(Some(source_table));
1598
1599        memory_tracker::track_memory("after_arc_share");
1600
1601        // Log memory audit
1602        let audits = memory_audit::perform_memory_audit(
1603            buffer.get_datatable(),
1604            buffer.get_original_source(),
1605            None,
1606        );
1607        memory_audit::log_memory_audit(&audits);
1608
1609        // Then set the DataView for display
1610        buffer.set_dataview(Some(dataview.clone()));
1611        // Use just the filename for the buffer name, not the full path
1612        let buffer_name = std::path::Path::new(source_name)
1613            .file_name()
1614            .and_then(|s| s.to_str())
1615            .unwrap_or(source_name)
1616            .to_string();
1617        buffer.set_name(buffer_name);
1618
1619        // Apply config settings to the buffer
1620        buffer.set_case_insensitive(app.config.behavior.case_insensitive_default);
1621        buffer.set_compact_mode(app.config.display.compact_mode);
1622        buffer.set_show_row_numbers(app.config.display.show_row_numbers);
1623
1624        // Add the buffer
1625        app.state_container.buffers_mut().add_buffer(buffer);
1626
1627        // Update state container with the DataView
1628        app.state_container.set_dataview(Some(dataview.clone()));
1629
1630        // Initialize viewport manager with the DataView
1631        app.viewport_manager = RefCell::new(Some(ViewportManager::new(Arc::new(dataview.clone()))));
1632
1633        // Calculate initial column widths
1634        app.calculate_optimal_column_widths();
1635
1636        // Set initial navigation state
1637        let row_count = dataview.row_count();
1638        let column_count = dataview.column_count();
1639        app.state_container
1640            .update_data_size(row_count, column_count);
1641
1642        Ok(app)
1643    }
1644
1645    /// Add a `DataView` to the existing TUI (creates a new buffer)
1646    pub fn add_dataview(&mut self, dataview: DataView, source_name: &str) -> Result<()> {
1647        // Delegate all the complex state coordination to StateCoordinator
1648        use crate::ui::state::state_coordinator::StateCoordinator;
1649
1650        StateCoordinator::add_dataview_with_refs(
1651            &mut self.state_container,
1652            &self.viewport_manager,
1653            dataview,
1654            source_name,
1655            &self.config,
1656        )?;
1657
1658        // TUI-specific: Calculate optimal column widths for display
1659        self.calculate_optimal_column_widths();
1660
1661        Ok(())
1662    }
1663
1664    /// Update the viewport with a new `DataView`
1665    pub fn update_viewport_with_dataview(&mut self, dataview: DataView) {
1666        self.viewport_manager = RefCell::new(Some(ViewportManager::new(Arc::new(dataview))));
1667    }
1668
1669    /// Get vim search adapter (public for orchestrator)
1670    pub fn vim_search_adapter(&self) -> &RefCell<VimSearchAdapter> {
1671        &self.vim_search_adapter
1672    }
1673
1674    /// Pre-populate SQL command with SELECT * FROM table
1675    pub fn set_sql_query(&mut self, table_name: &str, raw_table_name: &str) {
1676        use crate::ui::state::state_coordinator::StateCoordinator;
1677
1678        // Delegate all state coordination to StateCoordinator
1679        let auto_query = StateCoordinator::set_sql_query_with_refs(
1680            &mut self.state_container,
1681            &self.shadow_state,
1682            &mut self.hybrid_parser,
1683            table_name,
1684            raw_table_name,
1685            &self.config,
1686        );
1687
1688        // Pre-populate the input field with the query
1689        self.set_input_text(auto_query);
1690    }
1691
1692    pub fn run(mut self) -> Result<()> {
1693        // Setup terminal with error handling
1694        if let Err(e) = enable_raw_mode() {
1695            return Err(anyhow::anyhow!(
1696                "Failed to enable raw mode: {}. Try running with --classic flag.",
1697                e
1698            ));
1699        }
1700
1701        let mut stdout = io::stdout();
1702        if let Err(e) = execute!(stdout, EnterAlternateScreen, EnableMouseCapture) {
1703            let _ = disable_raw_mode();
1704            return Err(anyhow::anyhow!(
1705                "Failed to setup terminal: {}. Try running with --classic flag.",
1706                e
1707            ));
1708        }
1709
1710        let backend = CrosstermBackend::new(stdout);
1711        let mut terminal = match Terminal::new(backend) {
1712            Ok(t) => t,
1713            Err(e) => {
1714                let _ = disable_raw_mode();
1715                return Err(anyhow::anyhow!(
1716                    "Failed to create terminal: {}. Try running with --classic flag.",
1717                    e
1718                ));
1719            }
1720        };
1721
1722        let res = self.run_app(&mut terminal);
1723
1724        // Always restore terminal, even on error
1725        let _ = disable_raw_mode();
1726        let _ = execute!(
1727            terminal.backend_mut(),
1728            LeaveAlternateScreen,
1729            DisableMouseCapture
1730        );
1731        let _ = terminal.show_cursor();
1732
1733        match res {
1734            Ok(()) => Ok(()),
1735            Err(e) => Err(anyhow::anyhow!("TUI error: {}", e)),
1736        }
1737    }
1738
1739    /// Initialize viewport and perform initial draw
1740    fn initialize_viewport<B: Backend>(&mut self, terminal: &mut Terminal<B>) -> Result<()> {
1741        self.update_viewport_size();
1742        info!(target: "navigation", "Initial viewport size update completed");
1743        terminal.draw(|f| self.ui(f))?;
1744        Ok(())
1745    }
1746
1747    /// Handle debounced search actions, returns true if exit is requested
1748    fn try_handle_debounced_actions<B: Backend>(
1749        &mut self,
1750        terminal: &mut Terminal<B>,
1751    ) -> Result<bool> {
1752        if !self.search_modes_widget.is_active() {
1753            return Ok(false);
1754        }
1755
1756        if let Some(action) = self.search_modes_widget.check_debounce() {
1757            let mut needs_redraw = false;
1758            if let SearchModesAction::ExecuteDebounced(mode, pattern) = action {
1759                info!(target: "search", "=== DEBOUNCED SEARCH EXECUTING ===");
1760                info!(target: "search", "Mode: {:?}, Pattern: '{}', AppMode: {:?}",
1761                      mode, pattern, self.shadow_state.borrow().get_mode());
1762
1763                // Log current position before search
1764                {
1765                    let nav = self.state_container.navigation();
1766                    info!(target: "search", "BEFORE: nav.selected_row={}, nav.selected_column={}",
1767                          nav.selected_row, nav.selected_column);
1768                    info!(target: "search", "BEFORE: buffer.selected_row={:?}, buffer.current_column={}",
1769                          self.state_container.get_buffer_selected_row(), self.state_container.get_current_column());
1770                }
1771
1772                self.execute_search_action(mode, pattern);
1773
1774                // Log position after search
1775                {
1776                    let nav = self.state_container.navigation();
1777                    info!(target: "search", "AFTER: nav.selected_row={}, nav.selected_column={}",
1778                          nav.selected_row, nav.selected_column);
1779                    info!(target: "search", "AFTER: buffer.selected_row={:?}, buffer.current_column={}",
1780                          self.state_container.get_buffer_selected_row(), self.state_container.get_current_column());
1781
1782                    // Check ViewportManager state
1783                    let viewport_manager = self.viewport_manager.borrow();
1784                    if let Some(ref vm) = *viewport_manager {
1785                        info!(target: "search", "AFTER: ViewportManager crosshair=({}, {})",
1786                              vm.get_crosshair_row(), vm.get_crosshair_col());
1787                    }
1788                }
1789
1790                info!(target: "search", "=== FORCING REDRAW ===");
1791                // CRITICAL: Force immediate redraw after search navigation
1792                needs_redraw = true;
1793            }
1794
1795            // Redraw immediately if search moved the cursor OR if TableWidgetManager needs render
1796            if needs_redraw || self.table_widget_manager.borrow().needs_render() {
1797                info!(target: "search", "Triggering redraw: needs_redraw={}, table_needs_render={}",
1798                      needs_redraw, self.table_widget_manager.borrow().needs_render());
1799                terminal.draw(|f| self.ui(f))?;
1800                self.table_widget_manager.borrow_mut().rendered();
1801            }
1802        }
1803        Ok(false)
1804    }
1805
1806    /// Handle chord processing for Results mode, returns true if exit is requested
1807    fn try_handle_chord_processing(&mut self, key: crossterm::event::KeyEvent) -> Result<bool> {
1808        // FIRST: Give VimSearchAdapter a chance to handle the key
1809        // This allows it to handle search navigation (n/N) and Escape in Results mode
1810        if self
1811            .vim_search_adapter
1812            .borrow()
1813            .should_handle_key(&self.state_container)
1814        {
1815            let handled = self
1816                .vim_search_adapter
1817                .borrow_mut()
1818                .handle_key(key.code, &mut self.state_container);
1819            if handled {
1820                debug!("VimSearchAdapter handled key: {:?}", key.code);
1821                return Ok(false); // Key was handled, don't exit
1822            }
1823        }
1824
1825        // SECOND: Try buffer operations (F11/F12, Ctrl-6, etc) - these should work in Results mode too
1826        if let Some(result) = self.try_handle_buffer_operations(&key)? {
1827            return Ok(result);
1828        }
1829
1830        let chord_result = self.key_chord_handler.process_key(key);
1831        debug!("Chord handler returned: {:?}", chord_result);
1832
1833        match chord_result {
1834            ChordResult::CompleteChord(action) => {
1835                // Handle completed chord actions through the action system
1836                debug!("Chord completed: {:?}", action);
1837                // Clear chord mode in renderer
1838                self.key_sequence_renderer.clear_chord_mode();
1839
1840                // Get mode before the borrow
1841                let current_mode = self.shadow_state.borrow().get_mode();
1842
1843                // Use the action system to handle the chord action
1844                self.try_handle_action(
1845                    action,
1846                    &ActionContext {
1847                        mode: current_mode,
1848                        selection_mode: self.state_container.get_selection_mode(),
1849                        has_results: self.state_container.get_buffer_dataview().is_some(),
1850                        has_filter: false,
1851                        has_search: false,
1852                        row_count: self.get_row_count(),
1853                        column_count: self.get_column_count(),
1854                        current_row: self.state_container.get_table_selected_row().unwrap_or(0),
1855                        current_column: self.state_container.get_current_column(),
1856                    },
1857                )?;
1858                Ok(false)
1859            }
1860            ChordResult::PartialChord(description) => {
1861                // Update status to show chord mode
1862                self.state_container.set_status_message(description.clone());
1863                // Update chord mode in renderer with available completions
1864                // Extract the completions from the description
1865                if description.contains("y=row") {
1866                    self.key_sequence_renderer
1867                        .set_chord_mode(Some("y(a,c,q,r,v)".to_string()));
1868                } else {
1869                    self.key_sequence_renderer
1870                        .set_chord_mode(Some(description.clone()));
1871                }
1872                Ok(false) // Don't exit, waiting for more keys
1873            }
1874            ChordResult::Cancelled => {
1875                self.state_container
1876                    .set_status_message("Chord cancelled".to_string());
1877                // Clear chord mode in renderer
1878                self.key_sequence_renderer.clear_chord_mode();
1879                Ok(false)
1880            }
1881            ChordResult::SingleKey(single_key) => {
1882                // Not a chord (or a chord that lapsed and was reset on this key).
1883                // Clear any lingering chord hint so the indicator reflects reality.
1884                self.key_sequence_renderer.clear_chord_mode();
1885                // Process normally
1886                self.handle_results_input(single_key)
1887            }
1888        }
1889    }
1890
1891    /// Dispatch key to appropriate mode handler, returns true if exit is requested
1892    fn try_handle_mode_dispatch(&mut self, key: crossterm::event::KeyEvent) -> Result<bool> {
1893        let mode = self.shadow_state.borrow().get_mode();
1894        debug!(
1895            "try_handle_mode_dispatch: mode={:?}, key={:?}",
1896            mode, key.code
1897        );
1898        match mode {
1899            AppMode::Command => self.handle_command_input(key),
1900            AppMode::Results => {
1901                // Results mode uses chord processing
1902                self.try_handle_chord_processing(key)
1903            }
1904            AppMode::Search | AppMode::Filter | AppMode::FuzzyFilter | AppMode::ColumnSearch => {
1905                self.handle_search_modes_input(key)
1906            }
1907            AppMode::Help => self.handle_help_input(key),
1908            AppMode::History => self.handle_history_input(key),
1909            AppMode::Debug => self.handle_debug_input(key),
1910            AppMode::PrettyQuery => self.handle_pretty_query_input(key),
1911            AppMode::JumpToRow => self.handle_jump_to_row_input(key),
1912            AppMode::ColumnStats => self.handle_column_stats_input(key),
1913        }
1914    }
1915
1916    /// Handle key event processing, returns true if exit is requested
1917    fn try_handle_key_event<B: Backend>(
1918        &mut self,
1919        terminal: &mut Terminal<B>,
1920        key: crossterm::event::KeyEvent,
1921    ) -> Result<bool> {
1922        // On Windows, filter out key release events - only handle key press
1923        // This prevents double-triggering of toggles
1924        if key.kind != crossterm::event::KeyEventKind::Press {
1925            return Ok(false);
1926        }
1927
1928        // SAFETY: Always allow Ctrl-C to exit, regardless of app state
1929        // This prevents getting stuck in unresponsive states
1930        if key.code == KeyCode::Char('c') && key.modifiers.contains(KeyModifiers::CONTROL) {
1931            info!(target: "app", "Ctrl-C detected, forcing exit");
1932            return Ok(true);
1933        }
1934
1935        // Record key press for visual indicator
1936        let key_display = format_key_for_display(&key);
1937        self.key_indicator.record_key(key_display.clone());
1938        self.key_sequence_renderer.record_key(key_display);
1939
1940        // Dispatch to appropriate mode handler
1941        let should_exit = self.try_handle_mode_dispatch(key)?;
1942
1943        if should_exit {
1944            return Ok(true);
1945        }
1946
1947        // Only redraw after handling a key event OR if TableWidgetManager needs render
1948        if self.table_widget_manager.borrow().needs_render() {
1949            info!("TableWidgetManager needs render after key event");
1950        }
1951        terminal.draw(|f| self.ui(f))?;
1952        self.table_widget_manager.borrow_mut().rendered();
1953
1954        Ok(false)
1955    }
1956
1957    /// Handle all events, returns true if exit is requested
1958    fn try_handle_events<B: Backend>(&mut self, terminal: &mut Terminal<B>) -> Result<bool> {
1959        // Use poll with timeout to allow checking for debounced actions
1960        if event::poll(std::time::Duration::from_millis(50))? {
1961            if let Event::Key(key) = event::read()? {
1962                if self.try_handle_key_event(terminal, key)? {
1963                    return Ok(true);
1964                }
1965            } else {
1966                // Ignore other events (mouse, resize, etc.) to reduce CPU
1967            }
1968        } else {
1969            // No key event. If a pending chord has lapsed, cancel it and refresh
1970            // so the "Yank mode" hint visibly disappears (chord reset indicator).
1971            if self.key_chord_handler.clear_if_timed_out() {
1972                self.key_sequence_renderer.clear_chord_mode();
1973                self.state_container
1974                    .set_status_message("Chord cancelled (timeout)".to_string());
1975                terminal.draw(|f| self.ui(f))?;
1976                return Ok(false);
1977            }
1978
1979            // No event available, but still redraw if we have pending debounced actions or table needs render
1980            if self.search_modes_widget.is_active()
1981                || self.table_widget_manager.borrow().needs_render()
1982            {
1983                if self.table_widget_manager.borrow().needs_render() {
1984                    info!("TableWidgetManager needs periodic render");
1985                }
1986                terminal.draw(|f| self.ui(f))?;
1987                self.table_widget_manager.borrow_mut().rendered();
1988            }
1989        }
1990        Ok(false)
1991    }
1992
1993    fn run_app<B: Backend>(&mut self, terminal: &mut Terminal<B>) -> Result<()> {
1994        self.initialize_viewport(terminal)?;
1995
1996        loop {
1997            // Handle debounced search actions
1998            if self.try_handle_debounced_actions(terminal)? {
1999                break;
2000            }
2001
2002            // Handle all events (key presses, etc.)
2003            if self.try_handle_events(terminal)? {
2004                break;
2005            }
2006        }
2007        Ok(())
2008    }
2009
2010    fn handle_command_input(&mut self, key: crossterm::event::KeyEvent) -> Result<bool> {
2011        // Normalize and log the key
2012        let normalized_key = self.normalize_and_log_key(key);
2013
2014        // === PHASE 1: Try CommandEditor for text input and editing ===
2015        // IMPORTANT: This must come BEFORE try_action_system to properly intercept keys
2016        // We handle comprehensive text editing operations
2017        // Let CommandEditor handle most text-related keys (except Tab which needs full TUI state)
2018
2019        // Check for special Ctrl/Alt combinations that should NOT go to CommandEditor
2020        let is_special_combo = if let KeyCode::Char(c) = normalized_key.code {
2021            // Special Ctrl combinations
2022            (normalized_key.modifiers.contains(KeyModifiers::CONTROL) && matches!(c,
2023                'x' | 'X' | // Expand asterisk
2024                'p' | 'P' | // Previous history  
2025                'n' | 'N' | // Next history
2026                'r' | 'R' | // History search
2027                'j' | 'J' | // Export JSON
2028                'o' | 'O' | // Open buffer
2029                'b' | 'B' | // Buffer operations
2030                'l' | 'L'   // Clear screen
2031            )) ||
2032            // Special Alt combinations that aren't word navigation
2033            (normalized_key.modifiers.contains(KeyModifiers::ALT) && matches!(c,
2034                'x' | 'X' |   // Expand asterisk visible only
2035                '[' | ']' |   // Jump to prev/next SQL token
2036                ',' | '.'     // Jump prev/next token (terminal-safe aliases)
2037            ))
2038        } else {
2039            false
2040        };
2041
2042        let should_try_command_editor = !is_special_combo
2043            && matches!(
2044                normalized_key.code,
2045                KeyCode::Char(_)
2046                    | KeyCode::Backspace
2047                    | KeyCode::Delete
2048                    | KeyCode::Left
2049                    | KeyCode::Right
2050                    | KeyCode::Home
2051                    | KeyCode::End
2052            );
2053
2054        if should_try_command_editor {
2055            // IMPORTANT: Sync state TO CommandEditor before processing
2056            // This ensures CommandEditor has the current text/cursor position
2057            let before_text = self.input.value().to_string();
2058            let before_cursor = self.input.cursor();
2059
2060            if self.command_editor.get_text() != before_text {
2061                self.command_editor.set_text(before_text.clone());
2062            }
2063            if self.command_editor.get_cursor() != before_cursor {
2064                self.command_editor.set_cursor(before_cursor);
2065            }
2066
2067            // Now handle the input with proper state
2068            let result = self.command_editor.handle_input(
2069                normalized_key,
2070                &mut self.state_container,
2071                &self.shadow_state,
2072            )?;
2073
2074            // Sync the text back to the main input after processing
2075            // (This is temporary until we fully migrate)
2076            let new_text = self.command_editor.get_text();
2077            let new_cursor = self.command_editor.get_cursor();
2078
2079            // Debug logging to see what's happening
2080            if new_text != before_text || new_cursor != before_cursor {
2081                debug!(
2082                    "CommandEditor changed input: '{}' -> '{}', cursor: {} -> {}",
2083                    before_text, new_text, before_cursor, new_cursor
2084                );
2085            }
2086
2087            // Use from() instead of new() to preserve any internal state
2088            self.input = tui_input::Input::from(new_text.clone()).with_cursor(new_cursor);
2089
2090            // CRITICAL: Update the buffer, not just command_input!
2091            // The rendering uses get_buffer_input_text() which reads from the buffer
2092            if let Some(buffer) = self.state_container.buffers_mut().current_mut() {
2093                buffer.set_input_text(new_text.clone());
2094                buffer.set_input_cursor_position(new_cursor);
2095            }
2096
2097            // Also update command_input for consistency
2098            self.state_container.set_input_text(new_text);
2099            self.state_container.set_input_cursor_position(new_cursor);
2100
2101            if result {
2102                return Ok(true);
2103            }
2104
2105            // For char/backspace, we've handled it - don't let action system process it again
2106            return Ok(false);
2107        }
2108        // === END PHASE 1 ===
2109
2110        // Try the new action system first
2111        if let Some(result) = self.try_action_system(normalized_key)? {
2112            return Ok(result);
2113        }
2114
2115        // Try editor widget for high-level actions
2116        if let Some(result) = self.try_editor_widget(normalized_key)? {
2117            return Ok(result);
2118        }
2119
2120        // ORIGINAL LOGIC: Keep all existing logic as fallback
2121
2122        // Try history navigation first
2123        if let Some(result) = self.try_handle_history_navigation(&normalized_key)? {
2124            return Ok(result);
2125        }
2126
2127        // Store old cursor position
2128        let old_cursor = self.get_input_cursor();
2129
2130        // Also log to tracing
2131        trace!(target: "input", "Key: {:?} Modifiers: {:?}", key.code, key.modifiers);
2132
2133        // DON'T process chord handler in Command mode - yanking makes no sense when editing queries!
2134        // The 'y' key should just type 'y' in the query editor.
2135
2136        // Try buffer operations
2137        if let Some(result) = self.try_handle_buffer_operations(&key)? {
2138            return Ok(result);
2139        }
2140
2141        // Try function keys
2142        if let Some(result) = self.try_handle_function_keys(&key)? {
2143            return Ok(result);
2144        }
2145
2146        // Try text editing operations
2147        if let Some(result) = self.try_handle_text_editing(&key)? {
2148            return Ok(result);
2149        }
2150
2151        // Try mode transitions and core input handling
2152        if let Some(result) = self.try_handle_mode_transitions(&key, old_cursor)? {
2153            return Ok(result);
2154        }
2155
2156        // All input should be handled by the try_handle_* methods above
2157        // If we reach here, it means we missed handling a key combination
2158
2159        Ok(false)
2160    }
2161
2162    // ========== COMMAND INPUT HELPER METHODS ==========
2163    // These helpers break down the massive handle_command_input method into logical groups
2164
2165    /// Normalize key for platform differences and log it
2166    fn normalize_and_log_key(
2167        &mut self,
2168        key: crossterm::event::KeyEvent,
2169    ) -> crossterm::event::KeyEvent {
2170        let normalized = self.state_container.normalize_key(key);
2171
2172        // Get the action that will be performed (if any)
2173        let action = self
2174            .key_dispatcher
2175            .get_command_action(&normalized)
2176            .map(std::string::ToString::to_string);
2177
2178        // Log the key press
2179        if normalized != key {
2180            self.state_container
2181                .log_key_press(key, Some(format!("normalized to {normalized:?}")));
2182        }
2183        self.state_container.log_key_press(normalized, action);
2184
2185        normalized
2186    }
2187
2188    /// Try handling with the new action system
2189    fn try_action_system(
2190        &mut self,
2191        normalized_key: crossterm::event::KeyEvent,
2192    ) -> Result<Option<bool>> {
2193        let action_context = self.build_action_context();
2194        if let Some(action) = self.key_mapper.map_key(normalized_key, &action_context) {
2195            info!(
2196                "✓ Action system (Command): key {:?} -> action {:?}",
2197                normalized_key.code, action
2198            );
2199            if let Ok(result) = self.try_handle_action(action, &action_context) {
2200                match result {
2201                    ActionResult::Handled => {
2202                        debug!("Action handled by new system in Command mode");
2203                        return Ok(Some(false));
2204                    }
2205                    ActionResult::Exit => {
2206                        return Ok(Some(true));
2207                    }
2208                    ActionResult::NotHandled => {
2209                        // Fall through to existing handling
2210                    }
2211                    _ => {}
2212                }
2213            }
2214        }
2215        Ok(None)
2216    }
2217
2218    /// Try handling with editor widget
2219    fn try_editor_widget(
2220        &mut self,
2221        normalized_key: crossterm::event::KeyEvent,
2222    ) -> Result<Option<bool>> {
2223        let key_dispatcher = self.key_dispatcher.clone();
2224        let editor_result = if let Some(buffer) = self.state_container.buffers_mut().current_mut() {
2225            self.editor_widget
2226                .handle_key(normalized_key, &key_dispatcher, buffer)?
2227        } else {
2228            EditorAction::PassToMainApp(normalized_key)
2229        };
2230
2231        match editor_result {
2232            EditorAction::Quit => return Ok(Some(true)),
2233            EditorAction::ExecuteQuery => {
2234                return self.handle_execute_query().map(Some);
2235            }
2236            EditorAction::BufferAction(buffer_action) => {
2237                return self.handle_buffer_action(buffer_action).map(Some);
2238            }
2239            EditorAction::ExpandAsterisk => {
2240                return self.handle_expand_asterisk().map(Some);
2241            }
2242            EditorAction::ShowHelp => {
2243                self.state_container.set_help_visible(true);
2244                // Use proper mode synchronization
2245                self.set_mode_via_shadow_state(AppMode::Help, "help_requested");
2246                return Ok(Some(false));
2247            }
2248            EditorAction::ShowDebug => {
2249                // This is now handled by passing through to original F5 handler
2250                return Ok(Some(false));
2251            }
2252            EditorAction::ShowPrettyQuery => {
2253                self.show_pretty_query();
2254                return Ok(Some(false));
2255            }
2256            EditorAction::SwitchMode(mode) => {
2257                self.handle_editor_mode_switch(mode);
2258                return Ok(Some(false));
2259            }
2260            EditorAction::PassToMainApp(_) => {
2261                // Fall through to original logic
2262            }
2263            EditorAction::Continue => return Ok(Some(false)),
2264        }
2265
2266        Ok(None)
2267    }
2268
2269    /// Handle mode switch from editor widget
2270    fn handle_editor_mode_switch(&mut self, mode: AppMode) {
2271        debug!(target: "shadow_state", "EditorAction::SwitchMode to {:?}", mode);
2272        if let Some(buffer) = self.state_container.buffers_mut().current_mut() {
2273            // Use shadow state to set mode (with write-through to buffer)
2274            let trigger = match mode {
2275                AppMode::Results => "enter_results_mode",
2276                AppMode::Command => "enter_command_mode",
2277                AppMode::History => "enter_history_mode",
2278                _ => "switch_mode",
2279            };
2280            debug!(target: "shadow_state", "Setting mode via shadow state to {:?} with trigger {}", mode, trigger);
2281            self.shadow_state
2282                .borrow_mut()
2283                .set_mode(mode.clone(), buffer, trigger);
2284        } else {
2285            debug!(target: "shadow_state", "No buffer available for mode switch!");
2286        }
2287
2288        // Special handling for History mode - initialize history search
2289        if mode == AppMode::History {
2290            eprintln!("[DEBUG] Using AppStateContainer for history search");
2291            let current_input = self.get_input_text();
2292
2293            // Start history search
2294            self.state_container.start_history_search(current_input);
2295
2296            // Initialize with schema context
2297            self.update_history_matches_in_container();
2298
2299            // Get match count
2300            let match_count = self.state_container.history_search().matches.len();
2301
2302            self.state_container
2303                .set_status_message(format!("History search: {match_count} matches"));
2304        }
2305    }
2306
2307    /// Handle function key inputs (F1-F12)
2308    fn try_handle_function_keys(
2309        &mut self,
2310        key: &crossterm::event::KeyEvent,
2311    ) -> Result<Option<bool>> {
2312        match key.code {
2313            KeyCode::F(1) | KeyCode::Char('?') => {
2314                // Toggle between Help mode and previous mode
2315                if self.shadow_state.borrow().is_in_help_mode() {
2316                    // Exit help mode
2317                    let mode = if self.state_container.has_dataview() {
2318                        AppMode::Results
2319                    } else {
2320                        AppMode::Command
2321                    };
2322                    // Use proper mode synchronization
2323                    self.set_mode_via_shadow_state(mode, "exit_help");
2324                    self.state_container.set_help_visible(false);
2325                    self.help_widget.on_exit();
2326                } else {
2327                    // Enter help mode
2328                    self.state_container.set_help_visible(true);
2329                    // Use proper mode synchronization
2330                    self.set_mode_via_shadow_state(AppMode::Help, "help_requested");
2331                    self.help_widget.on_enter();
2332                }
2333                Ok(Some(false))
2334            }
2335            KeyCode::F(3) => {
2336                // Show pretty printed query
2337                self.show_pretty_query();
2338                Ok(Some(false))
2339            }
2340            KeyCode::F(5) => {
2341                // Toggle debug mode
2342                self.toggle_debug_mode();
2343                Ok(Some(false))
2344            }
2345            KeyCode::F(6) => {
2346                // Toggle row numbers
2347                let current = self.state_container.is_show_row_numbers();
2348                self.state_container.set_show_row_numbers(!current);
2349                self.state_container.set_status_message(format!(
2350                    "Row numbers: {}",
2351                    if current { "OFF" } else { "ON" }
2352                ));
2353                Ok(Some(false))
2354            }
2355            KeyCode::F(7) => {
2356                // Toggle compact mode
2357                let current_mode = self.state_container.is_compact_mode();
2358                self.state_container.set_compact_mode(!current_mode);
2359                let message = if current_mode {
2360                    "Compact mode disabled"
2361                } else {
2362                    "Compact mode enabled"
2363                };
2364                self.state_container.set_status_message(message.to_string());
2365                Ok(Some(false))
2366            }
2367            KeyCode::F(8) => {
2368                // Toggle case-insensitive string comparisons
2369                let current = self.state_container.is_case_insensitive();
2370                self.state_container.set_case_insensitive(!current);
2371                self.state_container.set_status_message(format!(
2372                    "Case-insensitive string comparisons: {}",
2373                    if current { "OFF" } else { "ON" }
2374                ));
2375                Ok(Some(false))
2376            }
2377            KeyCode::F(9) => {
2378                // F9 as alternative for kill line (for terminals that intercept Ctrl+K)
2379                use crate::ui::traits::input_ops::InputBehavior;
2380                InputBehavior::kill_line(self);
2381                let message = if self.state_container.is_kill_ring_empty() {
2382                    "Killed to end of line".to_string()
2383                } else {
2384                    format!(
2385                        "Killed to end of line ('{}' saved to kill ring)",
2386                        self.state_container.get_kill_ring()
2387                    )
2388                };
2389                self.state_container.set_status_message(message);
2390                Ok(Some(false))
2391            }
2392            KeyCode::F(10) => {
2393                // F10 as alternative for kill line backward
2394                use crate::ui::traits::input_ops::InputBehavior;
2395                InputBehavior::kill_line_backward(self);
2396                let message = if self.state_container.is_kill_ring_empty() {
2397                    "Killed to beginning of line".to_string()
2398                } else {
2399                    format!(
2400                        "Killed to beginning of line ('{}' saved to kill ring)",
2401                        self.state_container.get_kill_ring()
2402                    )
2403                };
2404                self.state_container.set_status_message(message);
2405                Ok(Some(false))
2406            }
2407            KeyCode::F(12) => {
2408                // Toggle key press indicator
2409                let enabled = !self.key_indicator.enabled;
2410                self.key_indicator.set_enabled(enabled);
2411                self.key_sequence_renderer.set_enabled(enabled);
2412                self.state_container.set_status_message(format!(
2413                    "Key press indicator {}",
2414                    if enabled { "enabled" } else { "disabled" }
2415                ));
2416                Ok(Some(false))
2417            }
2418            _ => Ok(None), // Not a function key we handle
2419        }
2420    }
2421
2422    /// Handle buffer management operations
2423    fn try_handle_buffer_operations(
2424        &mut self,
2425        key: &crossterm::event::KeyEvent,
2426    ) -> Result<Option<bool>> {
2427        if let Some(action) = self.key_dispatcher.get_command_action(key) {
2428            match action {
2429                "quit" => return Ok(Some(true)),
2430                "next_buffer" => {
2431                    // Save viewport state before switching
2432                    self.save_viewport_to_current_buffer();
2433
2434                    let message = self
2435                        .buffer_handler
2436                        .next_buffer(self.state_container.buffers_mut());
2437                    debug!("{}", message);
2438
2439                    // Sync all state after buffer switch
2440                    self.sync_after_buffer_switch();
2441                    return Ok(Some(false));
2442                }
2443                "previous_buffer" => {
2444                    // Save viewport state before switching
2445                    self.save_viewport_to_current_buffer();
2446
2447                    let message = self
2448                        .buffer_handler
2449                        .previous_buffer(self.state_container.buffers_mut());
2450                    debug!("{}", message);
2451
2452                    // Sync all state after buffer switch
2453                    self.sync_after_buffer_switch();
2454                    return Ok(Some(false));
2455                }
2456                "quick_switch_buffer" => {
2457                    // Save viewport state before switching
2458                    self.save_viewport_to_current_buffer();
2459
2460                    let message = self
2461                        .buffer_handler
2462                        .quick_switch(self.state_container.buffers_mut());
2463                    debug!("{}", message);
2464
2465                    // Sync all state after buffer switch
2466                    self.sync_after_buffer_switch();
2467
2468                    return Ok(Some(false));
2469                }
2470                "new_buffer" => {
2471                    let message = self
2472                        .buffer_handler
2473                        .new_buffer(self.state_container.buffers_mut(), &self.config);
2474                    debug!("{}", message);
2475                    return Ok(Some(false));
2476                }
2477                "close_buffer" => {
2478                    let (success, message) = self
2479                        .buffer_handler
2480                        .close_buffer(self.state_container.buffers_mut());
2481                    debug!("{}", message);
2482                    return Ok(Some(!success)); // Exit if we couldn't close (only one left)
2483                }
2484                "list_buffers" => {
2485                    let buffer_list = self
2486                        .buffer_handler
2487                        .list_buffers(self.state_container.buffers());
2488                    for line in &buffer_list {
2489                        debug!("{}", line);
2490                    }
2491                    return Ok(Some(false));
2492                }
2493                action if action.starts_with("switch_to_buffer_") => {
2494                    if let Some(buffer_num_str) = action.strip_prefix("switch_to_buffer_") {
2495                        if let Ok(buffer_num) = buffer_num_str.parse::<usize>() {
2496                            // Save viewport state before switching
2497                            self.save_viewport_to_current_buffer();
2498
2499                            let message = self.buffer_handler.switch_to_buffer(
2500                                self.state_container.buffers_mut(),
2501                                buffer_num - 1,
2502                            );
2503                            debug!("{}", message);
2504
2505                            // Sync all state after buffer switch
2506                            self.sync_after_buffer_switch();
2507                        }
2508                    }
2509                    return Ok(Some(false));
2510                }
2511                _ => {} // Not a buffer operation
2512            }
2513        }
2514        Ok(None)
2515    }
2516
2517    /// Handle history navigation operations
2518    fn try_handle_history_navigation(
2519        &mut self,
2520        key: &crossterm::event::KeyEvent,
2521    ) -> Result<Option<bool>> {
2522        // Handle Ctrl+R for history search
2523        if let KeyCode::Char('r') = key.code {
2524            if key.modifiers.contains(KeyModifiers::CONTROL) {
2525                // Start history search mode
2526                let current_input = self.get_input_text();
2527
2528                // Start history search
2529                self.state_container.start_history_search(current_input);
2530
2531                // Initialize with schema context
2532                self.update_history_matches_in_container();
2533
2534                // Get status
2535                let match_count = self.state_container.history_search().matches.len();
2536
2537                self.state_container.set_mode(AppMode::History);
2538                self.shadow_state
2539                    .borrow_mut()
2540                    .observe_mode_change(AppMode::History, "history_search_started");
2541                self.state_container.set_status_message(format!(
2542                    "History search started (Ctrl+R) - {match_count} matches"
2543                ));
2544                return Ok(Some(false));
2545            }
2546        }
2547
2548        // Handle Ctrl+P for previous history
2549        if let KeyCode::Char('p') = key.code {
2550            if key.modifiers.contains(KeyModifiers::CONTROL) {
2551                let history_entries = self
2552                    .state_container
2553                    .command_history()
2554                    .get_navigation_entries();
2555                let history_commands: Vec<String> =
2556                    history_entries.iter().map(|e| e.command.clone()).collect();
2557
2558                if let Some(buffer) = self.state_container.buffers_mut().current_mut() {
2559                    if buffer.navigate_history_up(&history_commands) {
2560                        self.sync_all_input_states();
2561                        self.state_container
2562                            .set_status_message("Previous command from history".to_string());
2563                    }
2564                }
2565                return Ok(Some(false));
2566            }
2567        }
2568
2569        // Handle Ctrl+N for next history
2570        if let KeyCode::Char('n') = key.code {
2571            if key.modifiers.contains(KeyModifiers::CONTROL) {
2572                let history_entries = self
2573                    .state_container
2574                    .command_history()
2575                    .get_navigation_entries();
2576                let history_commands: Vec<String> =
2577                    history_entries.iter().map(|e| e.command.clone()).collect();
2578
2579                if let Some(buffer) = self.state_container.buffers_mut().current_mut() {
2580                    if buffer.navigate_history_down(&history_commands) {
2581                        self.sync_all_input_states();
2582                        self.state_container
2583                            .set_status_message("Next command from history".to_string());
2584                    }
2585                }
2586                return Ok(Some(false));
2587            }
2588        }
2589
2590        // Handle Alt+Up/Down as alternatives
2591        match key.code {
2592            KeyCode::Up if key.modifiers.contains(KeyModifiers::ALT) => {
2593                let history_entries = self
2594                    .state_container
2595                    .command_history()
2596                    .get_navigation_entries();
2597                let history_commands: Vec<String> =
2598                    history_entries.iter().map(|e| e.command.clone()).collect();
2599
2600                if let Some(buffer) = self.state_container.buffers_mut().current_mut() {
2601                    if buffer.navigate_history_up(&history_commands) {
2602                        self.sync_all_input_states();
2603                        self.state_container
2604                            .set_status_message("Previous command (Alt+Up)".to_string());
2605                    }
2606                }
2607                Ok(Some(false))
2608            }
2609            KeyCode::Down if key.modifiers.contains(KeyModifiers::ALT) => {
2610                let history_entries = self
2611                    .state_container
2612                    .command_history()
2613                    .get_navigation_entries();
2614                let history_commands: Vec<String> =
2615                    history_entries.iter().map(|e| e.command.clone()).collect();
2616
2617                if let Some(buffer) = self.state_container.buffers_mut().current_mut() {
2618                    if buffer.navigate_history_down(&history_commands) {
2619                        self.sync_all_input_states();
2620                        self.state_container
2621                            .set_status_message("Next command (Alt+Down)".to_string());
2622                    }
2623                }
2624                Ok(Some(false))
2625            }
2626            _ => Ok(None),
2627        }
2628    }
2629
2630    /// Handle text editing operations (word movement, kill line, clipboard, etc.)
2631    fn try_handle_text_editing(
2632        &mut self,
2633        key: &crossterm::event::KeyEvent,
2634    ) -> Result<Option<bool>> {
2635        // Try dispatcher actions first
2636        if let Some(action) = self.key_dispatcher.get_command_action(key) {
2637            match action {
2638                "expand_asterisk" => {
2639                    if let Some(buffer) = self.state_container.buffers_mut().current_mut() {
2640                        if buffer.expand_asterisk(&self.hybrid_parser) {
2641                            // Sync for rendering if needed
2642                            if buffer.get_edit_mode() == EditMode::SingleLine {
2643                                let text = buffer.get_input_text();
2644                                let cursor = buffer.get_input_cursor_position();
2645                                self.set_input_text_with_cursor(text, cursor);
2646                            }
2647                        }
2648                    }
2649                    return Ok(Some(false));
2650                }
2651                "expand_asterisk_visible" => {
2652                    if let Some(buffer) = self.state_container.buffers_mut().current_mut() {
2653                        if buffer.expand_asterisk_visible() {
2654                            // Sync for rendering if needed
2655                            if buffer.get_edit_mode() == EditMode::SingleLine {
2656                                let text = buffer.get_input_text();
2657                                let cursor = buffer.get_input_cursor_position();
2658                                self.set_input_text_with_cursor(text, cursor);
2659                            }
2660                        }
2661                    }
2662                    return Ok(Some(false));
2663                }
2664                // TODO: DUPLICATED IN COMMAND_EDITOR - Can be removed after full migration
2665                "delete_word_backward" => {
2666                    use crate::ui::traits::input_ops::InputBehavior;
2667                    InputBehavior::delete_word_backward(self);
2668                    return Ok(Some(false));
2669                }
2670                // TODO: DUPLICATED IN COMMAND_EDITOR - Can be removed after full migration
2671                "delete_word_forward" => {
2672                    use crate::ui::traits::input_ops::InputBehavior;
2673                    InputBehavior::delete_word_forward(self);
2674                    return Ok(Some(false));
2675                }
2676                // TODO: DUPLICATED IN COMMAND_EDITOR - Can be removed after full migration
2677                "kill_line" => {
2678                    use crate::ui::traits::input_ops::InputBehavior;
2679                    InputBehavior::kill_line(self);
2680                    return Ok(Some(false));
2681                }
2682                // TODO: DUPLICATED IN COMMAND_EDITOR - Can be removed after full migration
2683                "kill_line_backward" => {
2684                    use crate::ui::traits::input_ops::InputBehavior;
2685                    InputBehavior::kill_line_backward(self);
2686                    return Ok(Some(false));
2687                }
2688                // TODO: DUPLICATED IN COMMAND_EDITOR - Can be removed after full migration
2689                "move_word_backward" => {
2690                    self.move_cursor_word_backward();
2691                    return Ok(Some(false));
2692                }
2693                // TODO: DUPLICATED IN COMMAND_EDITOR - Can be removed after full migration
2694                "move_word_forward" => {
2695                    self.move_cursor_word_forward();
2696                    return Ok(Some(false));
2697                }
2698                _ => {} // Not a text editing action, fall through
2699            }
2700        }
2701
2702        // Handle hardcoded text editing keys
2703        match key.code {
2704            // TODO: DUPLICATED IN COMMAND_EDITOR - Can be removed after full migration
2705            KeyCode::Char('k') if key.modifiers.contains(KeyModifiers::CONTROL) => {
2706                // Kill line - delete from cursor to end of line
2707                self.state_container
2708                    .set_status_message("Ctrl+K pressed - killing to end of line".to_string());
2709                use crate::ui::traits::input_ops::InputBehavior;
2710                InputBehavior::kill_line(self);
2711                Ok(Some(false))
2712            }
2713            // TODO: DUPLICATED IN COMMAND_EDITOR - Can be removed after full migration
2714            KeyCode::Char('k') if key.modifiers.contains(KeyModifiers::ALT) => {
2715                // Alternative: Alt+K for kill line (for terminals that intercept Ctrl+K)
2716                self.state_container
2717                    .set_status_message("Alt+K - killing to end of line".to_string());
2718                use crate::ui::traits::input_ops::InputBehavior;
2719                InputBehavior::kill_line(self);
2720                Ok(Some(false))
2721            }
2722            // TODO: DUPLICATED IN COMMAND_EDITOR - Can be removed after full migration
2723            KeyCode::Char('u') if key.modifiers.contains(KeyModifiers::CONTROL) => {
2724                // Kill line backward - delete from cursor to beginning of line
2725                use crate::ui::traits::input_ops::InputBehavior;
2726                InputBehavior::kill_line_backward(self);
2727                Ok(Some(false))
2728            }
2729            KeyCode::Char('v') if key.modifiers.contains(KeyModifiers::CONTROL) => {
2730                // Paste from system clipboard
2731                self.paste_from_clipboard();
2732                Ok(Some(false))
2733            }
2734            KeyCode::Char('y') if key.modifiers.contains(KeyModifiers::CONTROL) => {
2735                // Yank - paste from kill ring (if we have one)
2736                self.yank();
2737                Ok(Some(false))
2738            }
2739            // TODO: DUPLICATED IN COMMAND_EDITOR - Can be removed after full migration
2740            KeyCode::Left if key.modifiers.contains(KeyModifiers::CONTROL) => {
2741                // Move backward one word
2742                self.move_cursor_word_backward();
2743                Ok(Some(false))
2744            }
2745            // TODO: DUPLICATED IN COMMAND_EDITOR - Can be removed after full migration
2746            KeyCode::Right if key.modifiers.contains(KeyModifiers::CONTROL) => {
2747                // Move forward one word
2748                self.move_cursor_word_forward();
2749                Ok(Some(false))
2750            }
2751            // TODO: DUPLICATED IN COMMAND_EDITOR - Can be removed after full migration
2752            KeyCode::Char('b') if key.modifiers.contains(KeyModifiers::ALT) => {
2753                // Move backward one word (alt+b like in bash)
2754                self.move_cursor_word_backward();
2755                Ok(Some(false))
2756            }
2757            // TODO: DUPLICATED IN COMMAND_EDITOR - Can be removed after full migration
2758            KeyCode::Char('f') if key.modifiers.contains(KeyModifiers::ALT) => {
2759                // Move forward one word (alt+f like in bash)
2760                self.move_cursor_word_forward();
2761                Ok(Some(false))
2762            }
2763            _ => Ok(None), // Not a text editing key we handle
2764        }
2765    }
2766
2767    /// Handle mode transitions and core input processing (Enter, Tab, Down arrow, input)
2768    fn try_handle_mode_transitions(
2769        &mut self,
2770        key: &crossterm::event::KeyEvent,
2771        old_cursor: usize,
2772    ) -> Result<Option<bool>> {
2773        match key.code {
2774            KeyCode::Char('c') if key.modifiers.contains(KeyModifiers::CONTROL) => {
2775                // Ctrl+C - exit application
2776                Ok(Some(true))
2777            }
2778            KeyCode::Char('d') if key.modifiers.contains(KeyModifiers::CONTROL) => {
2779                // Ctrl+D - exit application
2780                Ok(Some(true))
2781            }
2782            KeyCode::Enter => {
2783                // Query execution and special command handling
2784                let query = self.get_input_text().trim().to_string();
2785                debug!(target: "action", "Executing query: {}", query);
2786
2787                if query.is_empty() {
2788                    self.state_container
2789                        .set_status_message("Empty query - please enter a SQL command".to_string());
2790                } else {
2791                    // Check for special commands
2792                    if query == ":help" {
2793                        self.state_container.set_help_visible(true);
2794                        // Use proper mode synchronization
2795                        self.set_mode_via_shadow_state(AppMode::Help, "help_requested");
2796                        self.state_container
2797                            .set_status_message("Help Mode - Press ESC to return".to_string());
2798                    } else if query == ":exit" || query == ":quit" || query == ":q" {
2799                        return Ok(Some(true));
2800                    } else if query == ":tui" {
2801                        // Already in TUI mode
2802                        self.state_container
2803                            .set_status_message("Already in TUI mode".to_string());
2804                    } else {
2805                        self.state_container
2806                            .set_status_message(format!("Processing query: '{query}'"));
2807                        self.execute_query_v2(&query)?;
2808                    }
2809                }
2810                Ok(Some(false))
2811            }
2812            KeyCode::Tab => {
2813                // Tab completion works in both modes
2814                self.apply_completion();
2815                Ok(Some(false))
2816            }
2817            KeyCode::Down => {
2818                debug!(target: "shadow_state", "Down arrow pressed in Command mode. has_dataview={}, edit_mode={:?}",
2819                    self.state_container.has_dataview(),
2820                    self.state_container.get_edit_mode());
2821
2822                if self.state_container.has_dataview()
2823                    && self.state_container.get_edit_mode() == Some(EditMode::SingleLine)
2824                {
2825                    debug!(target: "shadow_state", "Down arrow conditions met, switching to Results via set_mode");
2826                    // Switch to Results mode and restore state
2827                    self.state_container.set_mode(AppMode::Results);
2828                    self.shadow_state
2829                        .borrow_mut()
2830                        .observe_mode_change(AppMode::Results, "down_arrow_to_results");
2831                    // Restore previous position or default to 0
2832                    let row = self.state_container.get_last_results_row().unwrap_or(0);
2833                    self.state_container.set_table_selected_row(Some(row));
2834
2835                    // Restore the exact scroll offset from when we left
2836                    let last_offset = self.state_container.get_last_scroll_offset();
2837                    self.state_container.set_scroll_offset(last_offset);
2838                    Ok(Some(false))
2839                } else {
2840                    debug!(target: "shadow_state", "Down arrow conditions not met, falling through");
2841                    // Fall through to default handling
2842                    Ok(None)
2843                }
2844            }
2845            _ => {
2846                // Fallback input handling and completion
2847                self.handle_input_key(*key);
2848
2849                // Clear completion state when typing other characters
2850                self.state_container.clear_completion();
2851
2852                // Always use single-line completion
2853                self.handle_completion();
2854
2855                // Update horizontal scroll if cursor moved
2856                if self.get_input_cursor() != old_cursor {
2857                    self.update_horizontal_scroll(120); // Assume reasonable terminal width, will be adjusted in render
2858                }
2859
2860                Ok(Some(false))
2861            }
2862        }
2863    }
2864
2865    /// Handle navigation keys specific to Results mode
2866    fn try_handle_results_navigation(
2867        &mut self,
2868        key: &crossterm::event::KeyEvent,
2869    ) -> Result<Option<bool>> {
2870        match key.code {
2871            KeyCode::PageDown | KeyCode::Char('f')
2872                if key.modifiers.contains(KeyModifiers::CONTROL) =>
2873            {
2874                NavigationBehavior::page_down(self);
2875                Ok(Some(false))
2876            }
2877            KeyCode::PageUp | KeyCode::Char('b')
2878                if key.modifiers.contains(KeyModifiers::CONTROL) =>
2879            {
2880                NavigationBehavior::page_up(self);
2881                Ok(Some(false))
2882            }
2883            _ => Ok(None), // Not a navigation key we handle
2884        }
2885    }
2886
2887    /// Handle clipboard/yank operations in Results mode
2888    fn try_handle_results_clipboard(
2889        &mut self,
2890        key: &crossterm::event::KeyEvent,
2891    ) -> Result<Option<bool>> {
2892        match key.code {
2893            KeyCode::Char('y') => {
2894                let selection_mode = self.get_selection_mode();
2895                debug!("'y' key pressed - selection_mode={:?}", selection_mode);
2896                match selection_mode {
2897                    SelectionMode::Cell => {
2898                        // In cell mode, single 'y' yanks the cell directly
2899                        debug!("Yanking cell in cell selection mode");
2900                        self.state_container
2901                            .set_status_message("Yanking cell...".to_string());
2902                        YankBehavior::yank_cell(self);
2903                        // Status message will be set by yank_cell
2904                    }
2905                    SelectionMode::Row => {
2906                        // In row mode, 'y' is handled by chord handler (yy, yc, ya)
2907                        // The chord handler will process the key sequence
2908                        debug!("'y' pressed in row mode - waiting for chord completion");
2909                        self.state_container.set_status_message(
2910                            "Press second key for chord: yy=row, yc=column, ya=all, yv=cell"
2911                                .to_string(),
2912                        );
2913                    }
2914                    SelectionMode::Column => {
2915                        // In column mode, 'y' yanks the current column
2916                        debug!("Yanking column in column selection mode");
2917                        self.state_container
2918                            .set_status_message("Yanking column...".to_string());
2919                        YankBehavior::yank_column(self);
2920                    }
2921                }
2922                Ok(Some(false))
2923            }
2924            _ => Ok(None),
2925        }
2926    }
2927
2928    /// Handle export operations in Results mode
2929    fn try_handle_results_export(
2930        &mut self,
2931        key: &crossterm::event::KeyEvent,
2932    ) -> Result<Option<bool>> {
2933        match key.code {
2934            KeyCode::Char('e') if key.modifiers.contains(KeyModifiers::CONTROL) => {
2935                self.export_to_csv();
2936                Ok(Some(false))
2937            }
2938            KeyCode::Char('j') if key.modifiers.contains(KeyModifiers::CONTROL) => {
2939                self.export_to_json();
2940                Ok(Some(false))
2941            }
2942            _ => Ok(None),
2943        }
2944    }
2945
2946    /// Handle help and mode transitions in Results mode
2947    fn try_handle_results_help(
2948        &mut self,
2949        key: &crossterm::event::KeyEvent,
2950    ) -> Result<Option<bool>> {
2951        match key.code {
2952            KeyCode::F(1) | KeyCode::Char('?') => {
2953                self.state_container.set_help_visible(true);
2954                // Use proper mode synchronization
2955                self.set_mode_via_shadow_state(AppMode::Help, "help_requested");
2956                self.help_widget.on_enter();
2957                Ok(Some(false))
2958            }
2959            _ => Ok(None),
2960        }
2961    }
2962
2963    fn handle_results_input(&mut self, key: crossterm::event::KeyEvent) -> Result<bool> {
2964        // Log all keys in Results mode to debug Escape handling
2965        debug!(
2966            "handle_results_input: Processing key {:?} in Results mode",
2967            key
2968        );
2969
2970        // Check if vim search is active/navigating
2971        let is_vim_navigating = self.vim_search_adapter.borrow().is_navigating();
2972        let vim_is_active = self.vim_search_adapter.borrow().is_active();
2973        let has_search_pattern = !self.state_container.get_search_pattern().is_empty();
2974
2975        debug!(
2976            "Search state check: vim_navigating={}, vim_active={}, has_pattern={}, pattern='{}'",
2977            is_vim_navigating,
2978            vim_is_active,
2979            has_search_pattern,
2980            self.state_container.get_search_pattern()
2981        );
2982
2983        // If Escape is pressed and there's an active search or vim navigation, handle it properly
2984        if key.code == KeyCode::Esc {
2985            info!("ESCAPE KEY DETECTED in Results mode!");
2986
2987            if is_vim_navigating || vim_is_active || has_search_pattern {
2988                info!("Escape pressed with active search - clearing via StateCoordinator");
2989                debug!(
2990                    "Pre-clear state: vim_navigating={}, vim_active={}, pattern='{}'",
2991                    is_vim_navigating,
2992                    vim_is_active,
2993                    self.state_container.get_search_pattern()
2994                );
2995
2996                // Use StateCoordinator to handle ALL search cancellation logic
2997                use crate::ui::state::state_coordinator::StateCoordinator;
2998                StateCoordinator::cancel_search_with_refs(
2999                    &mut self.state_container,
3000                    &self.shadow_state,
3001                    Some(&self.vim_search_adapter),
3002                );
3003
3004                // Verify clearing worked
3005                let post_pattern = self.state_container.get_search_pattern();
3006                let post_vim_active = self.vim_search_adapter.borrow().is_active();
3007                info!(
3008                    "Post-clear state: pattern='{}', vim_active={}",
3009                    post_pattern, post_vim_active
3010                );
3011
3012                self.state_container
3013                    .set_status_message("Search cleared".to_string());
3014                return Ok(false);
3015            }
3016            info!("Escape pressed but no active search to clear");
3017        }
3018
3019        let selection_mode = self.state_container.get_selection_mode();
3020
3021        debug!(
3022            "handle_results_input: key={:?}, selection_mode={:?}",
3023            key, selection_mode
3024        );
3025
3026        // Normalize the key for platform differences
3027        let normalized = self.state_container.normalize_key(key);
3028
3029        // Get the action that will be performed (if any) - for logging purposes
3030        let action_context = self.build_action_context();
3031        let mapped_action = self.key_mapper.map_key(normalized, &action_context);
3032        let action = mapped_action.as_ref().map(|a| format!("{a:?}"));
3033
3034        // Log the key press
3035        if normalized != key {
3036            self.state_container
3037                .log_key_press(key, Some(format!("normalized to {normalized:?}")));
3038        }
3039        self.state_container
3040            .log_key_press(normalized, action.clone());
3041
3042        let normalized_key = normalized;
3043
3044        // Try the new action system first
3045        // Note: Even if chord mode is active, single keys that aren't part of chords
3046        // should still be processed by the action system
3047        let action_context = self.build_action_context();
3048        debug!(
3049            "Action context for key {:?}: mode={:?}",
3050            normalized_key.code, action_context.mode
3051        );
3052        if let Some(action) = self.key_mapper.map_key(normalized_key, &action_context) {
3053            info!(
3054                "✓ Action system: key {:?} -> action {:?}",
3055                normalized_key.code, action
3056            );
3057            if let Ok(result) = self.try_handle_action(action, &action_context) {
3058                match result {
3059                    ActionResult::Handled => {
3060                        debug!("Action handled by new system");
3061                        return Ok(false);
3062                    }
3063                    ActionResult::Exit => {
3064                        debug!("Action requested exit");
3065                        return Ok(true);
3066                    }
3067                    ActionResult::SwitchMode(mode) => {
3068                        debug!("Action requested mode switch to {:?}", mode);
3069                        self.state_container.set_mode(mode);
3070                        return Ok(false);
3071                    }
3072                    ActionResult::Error(err) => {
3073                        warn!("Action error: {}", err);
3074                        self.state_container
3075                            .set_status_message(format!("Error: {err}"));
3076                        return Ok(false);
3077                    }
3078                    ActionResult::NotHandled => {
3079                        // Fall through to existing handling
3080                        debug!("Action not handled, falling back to legacy system");
3081                    }
3082                }
3083            }
3084        }
3085
3086        // Debug uppercase G specifically
3087        if matches!(key.code, KeyCode::Char('G')) {
3088            debug!("Detected uppercase G key press!");
3089        }
3090
3091        // F6 is now available for future use
3092
3093        // F12 is now handled by the action system
3094
3095        // NOTE: Chord handling has been moved to handle_input level
3096        // This ensures chords work correctly before any other key processing
3097
3098        // All keys should now be handled through the action system above
3099        // Any keys that reach here are either:
3100        // 1. Not mapped in the action system yet
3101        // 2. Special cases that need direct handling
3102
3103        // For now, log unmapped keys for debugging
3104        if mapped_action.is_none() {
3105            debug!(
3106                "No action mapping for key {:?} in Results mode",
3107                normalized_key
3108            );
3109        }
3110
3111        // Try Results-specific navigation keys
3112        if let Some(result) = self.try_handle_results_navigation(&normalized_key)? {
3113            return Ok(result);
3114        }
3115
3116        // Try clipboard/yank operations
3117        if let Some(result) = self.try_handle_results_clipboard(&normalized_key)? {
3118            return Ok(result);
3119        }
3120
3121        // Try export operations
3122        if let Some(result) = self.try_handle_results_export(&normalized_key)? {
3123            return Ok(result);
3124        }
3125
3126        // Try help and mode transitions
3127        if let Some(result) = self.try_handle_results_help(&normalized_key)? {
3128            return Ok(result);
3129        }
3130
3131        // All key handling has been migrated to:
3132        // - Action system (handles most keys)
3133        // - try_handle_results_* methods (handles specific Results mode keys)
3134        // This completes the orchestration pattern for Results mode input
3135        Ok(false)
3136    }
3137    // ========== SEARCH OPERATIONS ==========
3138
3139    fn execute_search_action(&mut self, mode: SearchMode, pattern: String) {
3140        debug!(target: "search", "execute_search_action called: mode={:?}, pattern='{}', current_app_mode={:?}, thread={:?}",
3141               mode, pattern, self.shadow_state.borrow().get_mode(), std::thread::current().id());
3142        match mode {
3143            SearchMode::Search => {
3144                debug!(target: "search", "Executing search with pattern: '{}', app_mode={:?}", pattern, self.shadow_state.borrow().get_mode());
3145                debug!(target: "search", "Search: current results count={}",
3146                       self.state_container.get_buffer_dataview().map_or(0, |v| v.source().row_count()));
3147
3148                // Set search pattern in AppStateContainer
3149                self.state_container.start_search(pattern.clone());
3150
3151                self.state_container.set_search_pattern(pattern.clone());
3152                self.perform_search();
3153                let matches_count = self.state_container.search().matches.len();
3154                debug!(target: "search", "After perform_search, app_mode={:?}, matches_found={}",
3155                       self.shadow_state.borrow().get_mode(),
3156                       matches_count);
3157
3158                // CRITICAL: Sync search results to VimSearchManager so 'n' and 'N' work
3159                if matches_count > 0 {
3160                    // Get matches from SearchManager to sync to VimSearchManager
3161                    let matches_for_vim: Vec<(usize, usize)> = {
3162                        let search_manager = self.search_manager.borrow();
3163                        search_manager
3164                            .all_matches()
3165                            .iter()
3166                            .map(|m| (m.row, m.column))
3167                            .collect()
3168                    };
3169
3170                    // Sync to VimSearchManager
3171                    if let Some(dataview) = self.state_container.get_buffer_dataview() {
3172                        info!(target: "search", "Syncing {} matches to VimSearchManager for pattern '{}'",
3173                              matches_for_vim.len(), pattern);
3174                        self.vim_search_adapter
3175                            .borrow_mut()
3176                            .set_search_state_from_external(
3177                                pattern.clone(),
3178                                matches_for_vim,
3179                                dataview,
3180                            );
3181                    }
3182                }
3183
3184                // Navigate to the first match if found (like vim)
3185                if matches_count > 0 {
3186                    // Get the first match position from SearchManager
3187                    let (row, col) = {
3188                        let search_manager = self.search_manager.borrow();
3189                        if let Some(first_match) = search_manager.first_match() {
3190                            (first_match.row, first_match.column)
3191                        } else {
3192                            // Fallback to state_container if SearchManager is empty (shouldn't happen)
3193                            let search_state = self.state_container.search();
3194                            if let Some((row, col, _, _)) = search_state.matches.first() {
3195                                (*row, *col)
3196                            } else {
3197                                (0, 0)
3198                            }
3199                        }
3200                    };
3201
3202                    info!(target: "search", "NAVIGATION START: Moving to first match at data row={}, col={}", row, col);
3203
3204                    // Navigate to the match position
3205                    // Set the row position
3206                    self.state_container.set_table_selected_row(Some(row));
3207                    self.state_container.set_selected_row(Some(row));
3208                    info!(target: "search", "  Set row position to {}", row);
3209
3210                    // Set the column position
3211                    {
3212                        let mut nav = self.state_container.navigation_mut();
3213                        nav.selected_column = col;
3214                    }
3215                    self.state_container.set_current_column_buffer(col);
3216                    info!(target: "search", "  Set column position to {}", col);
3217
3218                    // CRITICAL: Update TableWidgetManager for debounced search navigation
3219                    info!(target: "search", "Updating TableWidgetManager for debounced search to ({}, {})", row, col);
3220                    self.table_widget_manager
3221                        .borrow_mut()
3222                        .on_debounced_search(row, col);
3223
3224                    // Update ViewportManager and ensure match is visible
3225                    {
3226                        let mut viewport_manager_borrow = self.viewport_manager.borrow_mut();
3227                        if let Some(ref mut viewport_manager) = *viewport_manager_borrow {
3228                            // Update the actual viewport to show the first match
3229                            let viewport_height = self.state_container.navigation().viewport_rows;
3230                            let viewport_width = self.state_container.navigation().viewport_columns;
3231                            let current_scroll = self.state_container.navigation().scroll_offset.0;
3232
3233                            info!(target: "search", "  Viewport dimensions: {}x{}, current_scroll: {}",
3234                                  viewport_height, viewport_width, current_scroll);
3235
3236                            // Calculate new scroll offset if needed to show the match
3237                            let new_row_offset = if row < current_scroll {
3238                                info!(target: "search", "  Match is above viewport, scrolling up to row {}", row);
3239                                row // Match is above, scroll up
3240                            } else if row >= current_scroll + viewport_height.saturating_sub(1) {
3241                                let centered = row.saturating_sub(viewport_height / 2);
3242                                info!(target: "search", "  Match is below viewport, centering at row {}", centered);
3243                                centered // Match is below, center it
3244                            } else {
3245                                info!(target: "search", "  Match is already visible, keeping scroll at {}", current_scroll);
3246                                current_scroll // Already visible
3247                            };
3248
3249                            // Calculate column scroll if needed
3250                            let current_col_scroll =
3251                                self.state_container.navigation().scroll_offset.1;
3252                            let new_col_offset = if col < current_col_scroll {
3253                                info!(target: "search", "  Match column {} is left of viewport (scroll={}), scrolling left", col, current_col_scroll);
3254                                col // Match is to the left, scroll left
3255                            } else if col >= current_col_scroll + viewport_width.saturating_sub(1) {
3256                                let centered = col.saturating_sub(viewport_width / 4);
3257                                info!(target: "search", "  Match column {} is right of viewport (scroll={}, width={}), scrolling to {}",
3258                                      col, current_col_scroll, viewport_width, centered);
3259                                centered // Match is to the right, scroll right but keep some context
3260                            } else {
3261                                info!(target: "search", "  Match column {} is visible, keeping scroll at {}", col, current_col_scroll);
3262                                current_col_scroll // Already visible
3263                            };
3264
3265                            // Update viewport to show the match (both row and column)
3266                            viewport_manager.set_viewport(
3267                                new_row_offset,
3268                                new_col_offset,
3269                                viewport_width as u16,
3270                                viewport_height as u16,
3271                            );
3272                            info!(target: "search", "  Set viewport to row_offset={}, col_offset={}", new_row_offset, new_col_offset);
3273
3274                            // Set crosshair to match position (needs viewport-relative coordinates)
3275                            let crosshair_row = row - new_row_offset;
3276                            let crosshair_col = col - new_col_offset;
3277                            viewport_manager.set_crosshair(crosshair_row, crosshair_col);
3278                            info!(target: "search", "  Set crosshair to viewport-relative ({}, {}), absolute was ({}, {})", 
3279                                  crosshair_row, crosshair_col, row, col);
3280
3281                            // Update navigation scroll offset (both row and column)
3282                            let mut nav = self.state_container.navigation_mut();
3283                            nav.scroll_offset.0 = new_row_offset;
3284                            nav.scroll_offset.1 = new_col_offset;
3285                        }
3286                    }
3287
3288                    // Also update the buffer's current match to trigger UI updates
3289                    self.state_container.set_current_match(Some((row, col)));
3290
3291                    // CRITICAL: Force the visual cursor position to update
3292                    // The crosshair is set but we need to ensure the visual cursor moves
3293                    {
3294                        let mut nav = self.state_container.navigation_mut();
3295                        nav.selected_row = row;
3296                        nav.selected_column = col;
3297                    }
3298                    info!(target: "search", "  Forced navigation state to row={}, col={}", row, col);
3299
3300                    // Update status to show we're at match 1 of N
3301                    self.state_container.set_status_message(format!(
3302                        "Match 1/{} at row {}, col {}",
3303                        matches_count,
3304                        row + 1,
3305                        col + 1
3306                    ));
3307                }
3308            }
3309            SearchMode::Filter => {
3310                use crate::ui::state::state_coordinator::StateCoordinator;
3311
3312                // Use StateCoordinator for all state transitions
3313                StateCoordinator::apply_filter_search_with_refs(
3314                    &mut self.state_container,
3315                    &self.shadow_state,
3316                    &pattern,
3317                );
3318
3319                // Apply the actual filter (implementation stays in TUI)
3320                self.apply_filter(&pattern);
3321
3322                debug!(target: "search", "After apply_filter, filtered_count={}",
3323                    self.state_container.get_buffer_dataview().map_or(0, super::super::data::data_view::DataView::row_count));
3324            }
3325            SearchMode::FuzzyFilter => {
3326                use crate::ui::state::state_coordinator::StateCoordinator;
3327
3328                // Use StateCoordinator for all state transitions
3329                StateCoordinator::apply_fuzzy_filter_search_with_refs(
3330                    &mut self.state_container,
3331                    &self.shadow_state,
3332                    &pattern,
3333                );
3334
3335                // Apply the actual fuzzy filter (implementation stays in TUI)
3336                self.apply_fuzzy_filter();
3337
3338                let indices_count = self.state_container.get_fuzzy_filter_indices().len();
3339                debug!(target: "search", "After apply_fuzzy_filter, matched_indices={}", indices_count);
3340            }
3341            SearchMode::ColumnSearch => {
3342                use crate::ui::state::state_coordinator::StateCoordinator;
3343
3344                debug!(target: "search", "Executing column search with pattern: '{}'", pattern);
3345
3346                // Use StateCoordinator for all state transitions
3347                StateCoordinator::apply_column_search_with_refs(
3348                    &mut self.state_container,
3349                    &self.shadow_state,
3350                    &pattern,
3351                );
3352
3353                // Apply the actual column search (implementation stays in TUI)
3354                self.search_columns();
3355
3356                debug!(target: "search", "After search_columns, app_mode={:?}", self.shadow_state.borrow().get_mode());
3357            }
3358        }
3359    }
3360
3361    fn enter_search_mode(&mut self, mode: SearchMode) {
3362        debug!(target: "search", "enter_search_mode called for {:?}, current_mode={:?}, input_text='{}'",
3363               mode, self.shadow_state.borrow().get_mode(), self.state_container.get_input_text());
3364
3365        // Get the SQL text based on the current mode
3366        let current_sql = if self.shadow_state.borrow().is_in_results_mode() {
3367            // In Results mode, use the last executed query
3368            let last_query = self.state_container.get_last_query();
3369            let input_text = self.state_container.get_input_text();
3370            debug!(target: "search", "COLUMN_SEARCH_SAVE_DEBUG: last_query='{}', input_text='{}'", last_query, input_text);
3371
3372            if !last_query.is_empty() {
3373                debug!(target: "search", "Using last_query for search mode: '{}'", last_query);
3374                last_query
3375            } else if !input_text.is_empty() {
3376                // If last_query is empty but we have input_text, use that as fallback
3377                // This handles the case where data is loaded but no query has been executed yet
3378                debug!(target: "search", "No last_query, using input_text as fallback: '{}'", input_text);
3379                input_text
3380            } else {
3381                // This shouldn't happen if we're properly saving queries
3382                warn!(target: "search", "No last_query or input_text found when entering search mode from Results!");
3383                String::new()
3384            }
3385        } else {
3386            // In Command mode, use the current input text
3387            self.get_input_text()
3388        };
3389
3390        let cursor_pos = current_sql.len();
3391
3392        debug!(
3393            "Entering {} mode, saving SQL: '{}', cursor: {}",
3394            mode.title(),
3395            current_sql,
3396            cursor_pos
3397        );
3398
3399        // Initialize the widget with saved state
3400        self.search_modes_widget
3401            .enter_mode(mode.clone(), current_sql, cursor_pos);
3402
3403        // Set the app mode - use sync_mode to ensure all state is synchronized
3404        debug!(target: "mode", "Setting app mode from {:?} to {:?}", self.shadow_state.borrow().get_mode(), mode.to_app_mode());
3405        let trigger = match mode {
3406            SearchMode::ColumnSearch => "backslash_column_search",
3407            SearchMode::Search => "data_search_started",
3408            SearchMode::FuzzyFilter => "fuzzy_filter_started",
3409            SearchMode::Filter => "filter_started",
3410        };
3411        self.sync_mode(mode.to_app_mode(), trigger);
3412
3413        // Also observe the search mode start in shadow state for search-specific tracking
3414        let search_type = match mode {
3415            SearchMode::ColumnSearch => crate::ui::state::shadow_state::SearchType::Column,
3416            SearchMode::Search => crate::ui::state::shadow_state::SearchType::Data,
3417            SearchMode::FuzzyFilter | SearchMode::Filter => {
3418                crate::ui::state::shadow_state::SearchType::Fuzzy
3419            }
3420        };
3421        self.shadow_state
3422            .borrow_mut()
3423            .observe_search_start(search_type, trigger);
3424
3425        // Clear patterns
3426        match mode {
3427            SearchMode::Search => {
3428                // Clear search in AppStateContainer
3429                self.state_container.clear_search();
3430                self.state_container.set_search_pattern(String::new());
3431            }
3432            SearchMode::Filter => {
3433                self.state_container.set_filter_pattern(String::new());
3434                self.state_container.filter_mut().clear();
3435            }
3436            SearchMode::FuzzyFilter => {
3437                self.state_container.set_fuzzy_filter_pattern(String::new());
3438                self.state_container.set_fuzzy_filter_indices(Vec::new());
3439                self.state_container.set_fuzzy_filter_active(false);
3440            }
3441            SearchMode::ColumnSearch => {
3442                // Clear column search in both AppStateContainer and DataView
3443                self.state_container.clear_column_search();
3444                if let Some(dataview) = self.state_container.get_buffer_dataview_mut() {
3445                    dataview.clear_column_search();
3446                }
3447
3448                // All column search state is now managed by AppStateContainer
3449            }
3450        }
3451
3452        // Clear input field for search mode use
3453        self.input = tui_input::Input::default();
3454        // Note: Not syncing here as search modes use input differently
3455    }
3456
3457    fn handle_search_modes_input(&mut self, key: crossterm::event::KeyEvent) -> Result<bool> {
3458        // Safety check: Always allow Ctrl-C to exit regardless of state
3459        if key.code == KeyCode::Char('c') && key.modifiers.contains(KeyModifiers::CONTROL) {
3460            return Ok(true); // Signal to quit
3461        }
3462
3463        // All search modes now use the same SearchModesWidget for consistent debouncing
3464
3465        let action = self.search_modes_widget.handle_key(key);
3466
3467        match action {
3468            SearchModesAction::Continue => {
3469                // No pattern change, nothing to do
3470            }
3471            SearchModesAction::InputChanged(mode, pattern) => {
3472                // Pattern changed, update UI but don't apply filter yet (will be debounced)
3473                self.set_input_text_with_cursor(pattern.clone(), pattern.len());
3474
3475                // Update the stored pattern
3476                match mode {
3477                    SearchMode::Search => {
3478                        self.state_container.set_search_pattern(pattern);
3479                    }
3480                    SearchMode::Filter => {
3481                        self.state_container.set_filter_pattern(pattern.clone());
3482                        let mut filter = self.state_container.filter_mut();
3483                        filter.pattern = pattern.clone();
3484                        filter.is_active = true;
3485                    }
3486                    SearchMode::FuzzyFilter => {
3487                        self.state_container.set_fuzzy_filter_pattern(pattern);
3488                    }
3489                    SearchMode::ColumnSearch => {
3490                        // Pattern is stored in AppStateContainer via start_column_search
3491                    }
3492                }
3493            }
3494            SearchModesAction::ExecuteDebounced(mode, pattern) => {
3495                // Execute the search but DON'T exit the mode - stay in search mode
3496                // This is for debounced typing updates
3497                self.execute_search_action(mode, pattern);
3498                // Don't exit! User is still typing/searching
3499            }
3500            SearchModesAction::Apply(mode, pattern) => {
3501                debug!(target: "search", "Apply action triggered for {:?} with pattern '{}'", mode, pattern);
3502                // Apply the filter/search with the pattern
3503                match mode {
3504                    SearchMode::Search => {
3505                        debug!(target: "search", "Search Apply: Applying search with pattern '{}'", pattern);
3506                        // Use execute_search_action to get the navigation to first match
3507                        self.execute_search_action(SearchMode::Search, pattern);
3508                        debug!(target: "search", "Search Apply: last_query='{}', will restore saved SQL from widget", self.state_container.get_last_query());
3509                        // For search, we always want to exit to Results mode after applying
3510                    }
3511                    SearchMode::Filter => {
3512                        debug!(target: "search", "Filter Apply: Applying filter with pattern '{}'", pattern);
3513                        self.state_container.set_filter_pattern(pattern.clone());
3514                        {
3515                            let mut filter = self.state_container.filter_mut();
3516                            filter.pattern = pattern.clone();
3517                            filter.is_active = true;
3518                        } // filter borrow ends here
3519                        self.apply_filter(&pattern); // Use the actual pattern, not empty string
3520                        debug!(target: "search", "Filter Apply: last_query='{}', will restore saved SQL from widget", self.state_container.get_last_query());
3521                    }
3522                    SearchMode::FuzzyFilter => {
3523                        debug!(target: "search", "FuzzyFilter Apply: Applying filter with pattern '{}'", pattern);
3524                        self.state_container.set_fuzzy_filter_pattern(pattern);
3525                        self.apply_fuzzy_filter();
3526                        debug!(target: "search", "FuzzyFilter Apply: last_query='{}', will restore saved SQL from widget", self.state_container.get_last_query());
3527                    }
3528                    SearchMode::ColumnSearch => {
3529                        // For column search, Apply (Enter key) jumps to the current match and exits
3530
3531                        let column_info = {
3532                            let column_search = self.state_container.column_search();
3533                            if column_search.matching_columns.is_empty() {
3534                                None
3535                            } else {
3536                                let current_match = column_search.current_match;
3537                                Some(column_search.matching_columns[current_match].clone())
3538                            }
3539                        };
3540
3541                        if let Some((col_idx, col_name)) = column_info {
3542                            self.state_container.set_current_column(col_idx);
3543                            self.state_container.set_current_column_buffer(col_idx);
3544
3545                            // Update ViewportManager to ensure the column is visible
3546                            let mut viewport_manager_borrow = self.viewport_manager.borrow_mut();
3547                            if let Some(viewport_manager) = viewport_manager_borrow.as_mut() {
3548                                viewport_manager.set_current_column(col_idx);
3549                            }
3550                            drop(viewport_manager_borrow);
3551
3552                            // CRITICAL: Sync NavigationState with ViewportManager after column navigation
3553                            // This ensures all state systems are consistent (like vim search)
3554                            debug!(target: "column_search_sync", "ColumnSearch Apply: About to call sync_navigation_with_viewport() for column: {}", col_name);
3555                            debug!(target: "column_search_sync", "ColumnSearch Apply: Pre-sync - viewport current_column: {}", 
3556                                if let Ok(vm) = self.viewport_manager.try_borrow() {
3557                                    vm.as_ref().map_or(0, super::viewport_manager::ViewportManager::get_crosshair_col)
3558                                } else { 0 });
3559                            self.sync_navigation_with_viewport();
3560                            debug!(target: "column_search_sync", "ColumnSearch Apply: Post-sync - navigation current_column: {}", 
3561                                self.state_container.navigation().selected_column);
3562                            debug!(target: "column_search_sync", "ColumnSearch Apply: sync_navigation_with_viewport() completed for column: {}", col_name);
3563
3564                            self.state_container
3565                                .set_status_message(format!("Jumped to column: {col_name}"));
3566                        }
3567
3568                        // IMPORTANT: Don't modify input_text when exiting column search!
3569                        // The widget will restore the original SQL that was saved when entering the mode
3570                        debug!(target: "search", "ColumnSearch Apply: Exiting without modifying input_text");
3571                        debug!(target: "search", "ColumnSearch Apply: last_query='{}', will restore saved SQL from widget", self.state_container.get_last_query());
3572                        // Note: Column search state will be cleared by cancel_search_with_refs below
3573                    }
3574                }
3575
3576                // Exit search mode and return to Results
3577                // Try to get saved SQL from widget
3578                let saved_state = self.search_modes_widget.exit_mode();
3579
3580                if let Some((sql, cursor)) = saved_state {
3581                    debug!(target: "search", "Exiting search mode. Original SQL was: '{}', cursor: {}", sql, cursor);
3582                    debug!(target: "buffer", "Returning to Results mode, preserving last_query: '{}'",
3583                           self.state_container.get_last_query());
3584
3585                    // IMPORTANT: Always restore the saved SQL to input_text!
3586                    // This includes empty strings - we need to clear the search term
3587                    debug!(target: "search", "Restoring saved SQL to input_text: '{}'", sql);
3588                    // Use helper to sync all states
3589                    self.set_input_text_with_cursor(sql, cursor);
3590                } else {
3591                    // Widget didn't have saved state - restore appropriate SQL based on mode
3592                    if mode == SearchMode::ColumnSearch {
3593                        // For column search, restore the last executed query or pre-populated query
3594                        let last_query = self.state_container.get_last_query();
3595                        if last_query.is_empty() {
3596                            debug!(target: "search", "Column search: No saved state or last_query, clearing input");
3597                            self.set_input_text(String::new());
3598                        } else {
3599                            debug!(target: "search", "Column search: No saved state, restoring last_query: '{}'", last_query);
3600                            self.set_input_text(last_query);
3601                        }
3602                    } else {
3603                        debug!(target: "search", "No saved state from widget, keeping current SQL");
3604                    }
3605                }
3606
3607                // ALWAYS switch back to Results mode after Apply for all search modes
3608                use crate::ui::state::state_coordinator::StateCoordinator;
3609
3610                // For column search, we cancel completely (no n/N navigation)
3611                // For regular search, we complete but keep pattern for n/N
3612                if mode == SearchMode::ColumnSearch {
3613                    debug!(target: "column_search_sync", "ColumnSearch Apply: Canceling column search completely with cancel_search_with_refs()");
3614
3615                    // Also clear column search in DataView
3616                    if let Some(dataview) = self.state_container.get_buffer_dataview_mut() {
3617                        dataview.clear_column_search();
3618                        debug!(target: "column_search_sync", "ColumnSearch Apply: Cleared column search in DataView");
3619                    }
3620
3621                    StateCoordinator::cancel_search_with_refs(
3622                        &mut self.state_container,
3623                        &self.shadow_state,
3624                        Some(&self.vim_search_adapter),
3625                    );
3626                    // Note: cancel_search_with_refs already switches to Results mode
3627                    debug!(target: "column_search_sync", "ColumnSearch Apply: Column search canceled and mode switched to Results");
3628                } else {
3629                    // For regular search modes, keep pattern for n/N navigation
3630                    debug!(target: "column_search_sync", "Search Apply: About to call StateCoordinator::complete_search_with_refs() for mode: {:?}", mode);
3631                    StateCoordinator::complete_search_with_refs(
3632                        &mut self.state_container,
3633                        &self.shadow_state,
3634                        Some(&self.vim_search_adapter),
3635                        AppMode::Results,
3636                        "search_applied",
3637                    );
3638                    debug!(target: "column_search_sync", "Search Apply: StateCoordinator::complete_search_with_refs() completed - should now be in Results mode");
3639                }
3640
3641                // Show status message
3642                let filter_msg = match mode {
3643                    SearchMode::FuzzyFilter => {
3644                        let query = self.state_container.get_last_query();
3645                        format!(
3646                            "Fuzzy filter applied. Query: '{}'. Press 'f' again to modify.",
3647                            if query.len() > 30 {
3648                                format!("{}...", &query[..30])
3649                            } else {
3650                                query
3651                            }
3652                        )
3653                    }
3654                    SearchMode::Filter => "Filter applied. Press 'F' again to modify.".to_string(),
3655                    SearchMode::Search => {
3656                        let matches = self.state_container.search().matches.len();
3657                        if matches > 0 {
3658                            format!("Found {matches} matches. Use n/N to navigate.")
3659                        } else {
3660                            "No matches found.".to_string()
3661                        }
3662                    }
3663                    SearchMode::ColumnSearch => "Column search complete.".to_string(),
3664                };
3665                self.state_container.set_status_message(filter_msg);
3666            }
3667            SearchModesAction::Cancel => {
3668                // Clear the filter and restore original SQL
3669                let mode = self.shadow_state.borrow().get_mode();
3670                match mode {
3671                    AppMode::FuzzyFilter => {
3672                        // Clear fuzzy filter - must apply empty filter to DataView
3673                        debug!(target: "search", "FuzzyFilter Cancel: Clearing fuzzy filter");
3674                        self.state_container.set_fuzzy_filter_pattern(String::new());
3675                        self.apply_fuzzy_filter(); // This will clear the filter in DataView
3676                        self.state_container.set_fuzzy_filter_indices(Vec::new());
3677                        self.state_container.set_fuzzy_filter_active(false);
3678                    }
3679                    AppMode::Filter => {
3680                        // Clear both local and buffer filter state
3681                        debug!(target: "search", "Filter Cancel: Clearing filter pattern and state");
3682                        self.state_container.filter_mut().clear();
3683                        self.state_container.set_filter_pattern(String::new());
3684                        self.state_container.set_filter_active(false);
3685                        // Re-apply empty filter to restore all results
3686                        self.apply_filter("");
3687                    }
3688                    AppMode::ColumnSearch => {
3689                        // Clear column search state using AppStateContainer
3690                        self.state_container.clear_column_search();
3691                        // The widget will restore the original SQL that was saved when entering the mode
3692                        debug!(target: "search", "ColumnSearch Cancel: Exiting without modifying input_text");
3693                        debug!(target: "search", "ColumnSearch Cancel: last_query='{}', will restore saved SQL from widget", self.state_container.get_last_query());
3694                    }
3695                    _ => {}
3696                }
3697
3698                // Exit mode and restore the saved SQL
3699                if let Some((sql, cursor)) = self.search_modes_widget.exit_mode() {
3700                    debug!(target: "search", "Cancel: Restoring saved SQL: '{}', cursor: {}", sql, cursor);
3701                    if !sql.is_empty() {
3702                        // Use helper to sync all states
3703                        self.set_input_text_with_cursor(sql, cursor);
3704                    }
3705                } else {
3706                    debug!(target: "search", "Cancel: No saved SQL from widget");
3707                }
3708
3709                // Use StateCoordinator to properly cancel search and restore state
3710                // StateCoordinator handles clearing vim search adapter too
3711                use crate::ui::state::state_coordinator::StateCoordinator;
3712                StateCoordinator::cancel_search_with_refs(
3713                    &mut self.state_container,
3714                    &self.shadow_state,
3715                    Some(&self.vim_search_adapter),
3716                );
3717            }
3718            SearchModesAction::NextMatch => {
3719                debug!(target: "search", "NextMatch action, current_mode={:?}, widget_mode={:?}",
3720                       self.shadow_state.borrow().get_mode(), self.search_modes_widget.current_mode());
3721
3722                // Check both shadow state and widget mode for consistency
3723                if self.shadow_state.borrow().is_in_column_search()
3724                    || self.search_modes_widget.current_mode() == Some(SearchMode::ColumnSearch)
3725                {
3726                    debug!(target: "search", "Calling next_column_match");
3727                    // Ensure mode is correctly set
3728                    if !self.shadow_state.borrow().is_in_column_search() {
3729                        debug!(target: "search", "WARNING: Mode mismatch - fixing");
3730                        self.state_container.set_mode(AppMode::ColumnSearch);
3731                        self.shadow_state.borrow_mut().observe_search_start(
3732                            crate::ui::state::shadow_state::SearchType::Column,
3733                            "column_search_mode_fix_next",
3734                        );
3735                    }
3736                    self.next_column_match();
3737                } else {
3738                    debug!(target: "search", "Not in ColumnSearch mode, skipping next_column_match");
3739                }
3740            }
3741            SearchModesAction::PreviousMatch => {
3742                debug!(target: "search", "PreviousMatch action, current_mode={:?}, widget_mode={:?}",
3743                       self.shadow_state.borrow().get_mode(), self.search_modes_widget.current_mode());
3744
3745                // Check both buffer mode and widget mode for consistency
3746                if self.shadow_state.borrow().get_mode() == AppMode::ColumnSearch
3747                    || self.search_modes_widget.current_mode() == Some(SearchMode::ColumnSearch)
3748                {
3749                    debug!(target: "search", "Calling previous_column_match");
3750                    // Ensure mode is correctly set
3751                    if self.shadow_state.borrow().get_mode() != AppMode::ColumnSearch {
3752                        debug!(target: "search", "WARNING: Mode mismatch - fixing");
3753                        self.state_container.set_mode(AppMode::ColumnSearch);
3754                        self.shadow_state.borrow_mut().observe_search_start(
3755                            crate::ui::state::shadow_state::SearchType::Column,
3756                            "column_search_mode_fix_prev",
3757                        );
3758                    }
3759                    self.previous_column_match();
3760                } else {
3761                    debug!(target: "search", "Not in ColumnSearch mode, skipping previous_column_match");
3762                }
3763            }
3764            SearchModesAction::PassThrough => {}
3765        }
3766
3767        // ========== FILTER OPERATIONS ==========
3768
3769        Ok(false)
3770    }
3771
3772    fn handle_help_input(&mut self, key: crossterm::event::KeyEvent) -> Result<bool> {
3773        // Handle help input directly to avoid borrow conflicts
3774
3775        match key.code {
3776            crossterm::event::KeyCode::Esc | crossterm::event::KeyCode::Char('q') => {
3777                self.help_widget.on_exit();
3778                self.state_container.set_help_visible(false);
3779
3780                // Return to Results mode if we have data, otherwise Command mode
3781                let target_mode = if self.state_container.has_dataview() {
3782                    AppMode::Results
3783                } else {
3784                    AppMode::Command
3785                };
3786
3787                // Use proper mode synchronization
3788                self.set_mode_via_shadow_state(target_mode, "escape_from_help");
3789
3790                // Return false to stay in the TUI (not exit)
3791                Ok(false)
3792            }
3793            _ => {
3794                // Delegate other keys to help widget
3795                self.help_widget.handle_key(key);
3796                Ok(false)
3797            }
3798        }
3799    }
3800
3801    // ========== HELP NAVIGATION ==========
3802
3803    fn handle_history_input(&mut self, key: crossterm::event::KeyEvent) -> Result<bool> {
3804        // Handle history input directly to avoid borrow conflicts
3805        use crossterm::event::{KeyCode, KeyModifiers};
3806
3807        let result = match key.code {
3808            KeyCode::Char('c') if key.modifiers.contains(KeyModifiers::CONTROL) => {
3809                crate::ui::input::history_input_handler::HistoryInputResult::Exit
3810            }
3811            KeyCode::Esc => {
3812                // Cancel history search and restore original input
3813                let original_input = self.state_container.cancel_history_search();
3814                if let Some(buffer) = self.state_container.current_buffer_mut() {
3815                    self.shadow_state.borrow_mut().set_mode(
3816                        crate::buffer::AppMode::Command,
3817                        buffer,
3818                        "history_cancelled",
3819                    );
3820                    buffer.set_status_message("History search cancelled".to_string());
3821                }
3822                crate::ui::input::history_input_handler::HistoryInputResult::SwitchToCommand(Some(
3823                    (original_input, 0),
3824                ))
3825            }
3826            KeyCode::Enter => {
3827                // Accept the selected history command
3828                if let Some(command) = self.state_container.accept_history_search() {
3829                    if let Some(buffer) = self.state_container.current_buffer_mut() {
3830                        self.shadow_state.borrow_mut().set_mode(
3831                            crate::buffer::AppMode::Command,
3832                            buffer,
3833                            "history_accepted",
3834                        );
3835                        buffer.set_status_message(
3836                            "Command loaded from history (cursor at start)".to_string(),
3837                        );
3838                    }
3839                    // Return command with cursor at the beginning for better visibility
3840                    crate::ui::input::history_input_handler::HistoryInputResult::SwitchToCommand(
3841                        Some((command, 0)),
3842                    )
3843                } else {
3844                    crate::ui::input::history_input_handler::HistoryInputResult::Continue
3845                }
3846            }
3847            KeyCode::Up => {
3848                self.state_container.history_search_previous();
3849                crate::ui::input::history_input_handler::HistoryInputResult::Continue
3850            }
3851            KeyCode::Down => {
3852                self.state_container.history_search_next();
3853                crate::ui::input::history_input_handler::HistoryInputResult::Continue
3854            }
3855            KeyCode::Char('r') if key.modifiers.contains(KeyModifiers::CONTROL) => {
3856                // Ctrl+R cycles through matches
3857                self.state_container.history_search_next();
3858                crate::ui::input::history_input_handler::HistoryInputResult::Continue
3859            }
3860            KeyCode::Backspace => {
3861                self.state_container.history_search_backspace();
3862                crate::ui::input::history_input_handler::HistoryInputResult::Continue
3863            }
3864            KeyCode::Char(c) => {
3865                self.state_container.history_search_add_char(c);
3866                crate::ui::input::history_input_handler::HistoryInputResult::Continue
3867            }
3868            _ => crate::ui::input::history_input_handler::HistoryInputResult::Continue,
3869        };
3870
3871        // Handle the result
3872        match result {
3873            crate::ui::input::history_input_handler::HistoryInputResult::Exit => return Ok(true),
3874            crate::ui::input::history_input_handler::HistoryInputResult::SwitchToCommand(
3875                input_data,
3876            ) => {
3877                if let Some((text, cursor_pos)) = input_data {
3878                    self.set_input_text_with_cursor(text, cursor_pos);
3879                    // Sync to ensure scroll is reset properly
3880                    self.sync_all_input_states();
3881                }
3882            }
3883            crate::ui::input::history_input_handler::HistoryInputResult::Continue => {
3884                // Update history matches if needed
3885                if crate::ui::input::history_input_handler::key_updates_search(key) {
3886                    self.update_history_matches_in_container();
3887                }
3888            }
3889        }
3890
3891        Ok(false)
3892    }
3893
3894    /// Update history matches in the `AppStateContainer` with schema context
3895    fn update_history_matches_in_container(&mut self) {
3896        // Get current schema columns and data source for better matching
3897        let (current_columns, current_source_str) =
3898            if let Some(dataview) = self.state_container.get_buffer_dataview() {
3899                (
3900                    dataview.column_names(),              // Gets visible columns
3901                    Some(dataview.source().name.clone()), // Gets table name from DataTable
3902                )
3903            } else {
3904                (vec![], None)
3905            };
3906
3907        let current_source = current_source_str.as_deref();
3908        let query = self.state_container.history_search().query.clone();
3909
3910        self.state_container.update_history_search_with_schema(
3911            query,
3912            &current_columns,
3913            current_source,
3914        );
3915    }
3916
3917    fn handle_debug_input(&mut self, key: crossterm::event::KeyEvent) -> Result<bool> {
3918        // Create context and delegate to extracted handler
3919        let mut ctx = crate::ui::input::input_handlers::DebugInputContext {
3920            buffer_manager: self.state_container.buffers_mut(),
3921            debug_widget: &mut self.debug_widget,
3922            shadow_state: &self.shadow_state,
3923        };
3924
3925        let should_quit = crate::ui::input::input_handlers::handle_debug_input(&mut ctx, key)?;
3926
3927        // If the extracted handler didn't handle these special keys, we still do them here
3928        // (until we can extract yank operations too)
3929        if !should_quit {
3930            match key.code {
3931                KeyCode::Char('t') if key.modifiers.contains(KeyModifiers::CONTROL) => {
3932                    // Ctrl+T: "Yank as Test" - capture current session as test case
3933                    self.yank_as_test_case();
3934                }
3935                KeyCode::Char('y') if key.modifiers.contains(KeyModifiers::SHIFT) => {
3936                    // Shift+Y: Yank debug dump with context
3937                    self.yank_debug_with_context();
3938                }
3939                _ => {}
3940            }
3941        }
3942
3943        Ok(should_quit)
3944        // ========== QUERY OPERATIONS ==========
3945    }
3946
3947    fn handle_pretty_query_input(&mut self, key: crossterm::event::KeyEvent) -> Result<bool> {
3948        // Create context and delegate to extracted handler
3949        let mut ctx = crate::ui::input::input_handlers::DebugInputContext {
3950            buffer_manager: self.state_container.buffers_mut(),
3951            debug_widget: &mut self.debug_widget,
3952            shadow_state: &self.shadow_state,
3953        };
3954
3955        crate::ui::input::input_handlers::handle_pretty_query_input(&mut ctx, key)
3956    }
3957
3958    pub fn execute_query_v2(&mut self, query: &str) -> Result<()> {
3959        // Use orchestrator to handle all the query execution logic
3960        let context = self.query_orchestrator.execute_query(
3961            query,
3962            &mut self.state_container,
3963            &self.vim_search_adapter,
3964        );
3965
3966        match context {
3967            Ok(ctx) => {
3968                // Apply the new DataView
3969                self.state_container
3970                    .set_dataview(Some(ctx.result.dataview.clone()));
3971
3972                // Update viewport
3973                self.update_viewport_manager(Some(ctx.result.dataview.clone()));
3974
3975                // Update navigation state
3976                self.state_container
3977                    .update_data_size(ctx.result.stats.row_count, ctx.result.stats.column_count);
3978
3979                // Calculate column widths
3980                self.calculate_optimal_column_widths();
3981
3982                // Update status message
3983                self.state_container
3984                    .set_status_message(ctx.result.status_message());
3985
3986                // Add to history
3987                self.state_container
3988                    .command_history_mut()
3989                    .add_entry_with_schema(
3990                        ctx.query.clone(),
3991                        true,
3992                        Some(ctx.result.stats.execution_time.as_millis() as u64),
3993                        ctx.result.column_names(),
3994                        Some(ctx.result.table_name()),
3995                    )?;
3996
3997                // Switch to results mode - use sync_mode to ensure all state is synchronized
3998                self.sync_mode(AppMode::Results, "execute_query_success");
3999
4000                // Reset table
4001                self.reset_table_state();
4002
4003                Ok(())
4004            }
4005            Err(e) => {
4006                let error_msg = format!("Query error: {e}");
4007                self.state_container.set_status_message(error_msg.clone());
4008
4009                // Add to history as failed
4010                self.state_container
4011                    .command_history_mut()
4012                    .add_entry_with_schema(query.to_string(), false, None, vec![], None)?;
4013
4014                Err(e)
4015            }
4016        }
4017    }
4018
4019    fn handle_completion(&mut self) {
4020        let cursor_pos = self.get_input_cursor();
4021        let query_str = self.get_input_text();
4022        let query = query_str.as_str();
4023
4024        let hybrid_result = self.hybrid_parser.get_completions(query, cursor_pos);
4025        if !hybrid_result.suggestions.is_empty() {
4026            self.state_container.set_status_message(format!(
4027                "Suggestions: {}",
4028                hybrid_result.suggestions.join(", ")
4029            ));
4030        }
4031    }
4032
4033    fn apply_completion(&mut self) {
4034        let cursor_pos = self.get_input_cursor();
4035        let query = self.get_input_text();
4036
4037        // Get the current completion suggestion
4038        let suggestion = match self.get_or_refresh_completion(&query, cursor_pos) {
4039            Some(s) => s,
4040            None => return,
4041        };
4042
4043        // Apply the completion to the text
4044        self.apply_completion_to_input(&query, cursor_pos, &suggestion);
4045    }
4046
4047    /// Get current completion or refresh if context changed
4048    /// Returns None if no completions available
4049    fn get_or_refresh_completion(&mut self, query: &str, cursor_pos: usize) -> Option<String> {
4050        let is_same_context = self
4051            .state_container
4052            .is_same_completion_context(query, cursor_pos);
4053
4054        if !is_same_context {
4055            // New completion context - get fresh suggestions
4056            let hybrid_result = self.hybrid_parser.get_completions(query, cursor_pos);
4057            if hybrid_result.suggestions.is_empty() {
4058                self.state_container
4059                    .set_status_message("No completions available".to_string());
4060                return None;
4061            }
4062
4063            self.state_container
4064                .set_completion_suggestions(hybrid_result.suggestions);
4065        } else if self.state_container.is_completion_active() {
4066            // Cycle to next suggestion
4067            self.state_container.next_completion();
4068        } else {
4069            self.state_container
4070                .set_status_message("No completions available".to_string());
4071            return None;
4072        }
4073
4074        // Get the current suggestion from AppStateContainer
4075        if let Some(sugg) = self.state_container.get_current_completion() {
4076            Some(sugg)
4077        } else {
4078            self.state_container
4079                .set_status_message("No completion selected".to_string());
4080            None
4081        }
4082    }
4083
4084    /// Apply a completion suggestion to the input
4085    fn apply_completion_to_input(&mut self, query: &str, cursor_pos: usize, suggestion: &str) {
4086        let partial_word =
4087            crate::ui::utils::text_operations::extract_partial_word_at_cursor(query, cursor_pos);
4088
4089        if let Some(partial) = partial_word {
4090            self.apply_partial_completion(query, cursor_pos, &partial, suggestion);
4091        } else {
4092            self.apply_full_insertion(query, cursor_pos, suggestion);
4093        }
4094    }
4095
4096    /// Apply completion when we have a partial word to complete
4097    fn apply_partial_completion(
4098        &mut self,
4099        query: &str,
4100        cursor_pos: usize,
4101        partial: &str,
4102        suggestion: &str,
4103    ) {
4104        // Use extracted completion logic
4105        let result = crate::ui::utils::text_operations::apply_completion_to_text(
4106            query, cursor_pos, partial, suggestion,
4107        );
4108
4109        // Use helper to set text and cursor together - this ensures sync
4110        self.set_input_text_with_cursor(result.new_text.clone(), result.new_cursor_position);
4111
4112        // Update completion state for next tab press
4113        self.state_container
4114            .update_completion_context(result.new_text.clone(), result.new_cursor_position);
4115
4116        // Generate status message
4117        let completion = self.state_container.completion();
4118        let suggestion_info = if completion.suggestions.len() > 1 {
4119            format!(
4120                "Completed: {} ({}/{} - Tab for next)",
4121                suggestion,
4122                completion.current_index + 1,
4123                completion.suggestions.len()
4124            )
4125        } else {
4126            format!("Completed: {suggestion}")
4127        };
4128        drop(completion);
4129        self.state_container.set_status_message(suggestion_info);
4130    }
4131
4132    /// Apply completion as a full insertion at cursor position
4133    fn apply_full_insertion(&mut self, query: &str, cursor_pos: usize, suggestion: &str) {
4134        // Just insert the suggestion at cursor position
4135        let before_cursor = &query[..cursor_pos];
4136        let after_cursor = &query[cursor_pos..];
4137        let new_query = format!("{before_cursor}{suggestion}{after_cursor}");
4138
4139        // Special case: if we completed a string method like Contains(''), position cursor inside quotes
4140        let cursor_pos_new = if suggestion.ends_with("('')") {
4141            // Position cursor between the quotes
4142            cursor_pos + suggestion.len() - 2
4143        } else {
4144            cursor_pos + suggestion.len()
4145        };
4146
4147        // Use helper to set text through buffer
4148        self.set_input_text(new_query.clone());
4149
4150        // Set cursor to correct position
4151        if let Some(buffer) = self.state_container.buffers_mut().current_mut() {
4152            buffer.set_input_cursor_position(cursor_pos_new);
4153            // Sync all input states after undo/redo
4154            self.sync_all_input_states();
4155        }
4156
4157        // Update completion state
4158        self.state_container
4159            .update_completion_context(new_query, cursor_pos_new);
4160
4161        self.state_container
4162            .set_status_message(format!("Inserted: {suggestion}"));
4163    }
4164
4165    // Note: expand_asterisk and get_table_columns removed - moved to Buffer and use hybrid_parser directly
4166
4167    // ========== COLUMN INFO ==========
4168
4169    // Helper to get estimated visible rows based on terminal size
4170
4171    fn get_column_count(&self) -> usize {
4172        // Use DataProvider trait for column count (migration step)
4173        if let Some(provider) = self.get_data_provider() {
4174            provider.get_column_count()
4175        } else {
4176            0
4177        }
4178    }
4179
4180    /// Get column count using `DataProvider` trait (new pattern)
4181    /// This demonstrates using the trait-based approach for column information
4182    /// Get column names using `DataProvider` trait
4183    /// Part of the migration to trait-based data access
4184    fn get_column_names_via_provider(&self) -> Vec<String> {
4185        if let Some(provider) = self.get_data_provider() {
4186            provider.get_column_names()
4187        } else {
4188            Vec::new()
4189        }
4190    }
4191
4192    // ========== NAVIGATION METHODS ==========
4193
4194    // ========== COLUMN PIN/HIDE ==========
4195
4196    fn toggle_column_pin_impl(&mut self) {
4197        // Get visual column index from ViewportManager's crosshair
4198        let visual_col_idx = if let Some(ref viewport_manager) = *self.viewport_manager.borrow() {
4199            viewport_manager.get_crosshair_col()
4200        } else {
4201            0
4202        };
4203
4204        // Get column name at visual position
4205        let column_name = if let Some(dataview) = self.state_container.get_buffer_dataview() {
4206            let all_columns = dataview.column_names();
4207            all_columns.get(visual_col_idx).cloned()
4208        } else {
4209            None
4210        };
4211
4212        if let Some(col_name) = column_name {
4213            if let Some(dataview) = self.state_container.get_buffer_dataview_mut() {
4214                // Check if this column name is already pinned
4215                let pinned_names = dataview.get_pinned_column_names();
4216                if pinned_names.contains(&col_name) {
4217                    // Column is already pinned, unpin it
4218                    dataview.unpin_column_by_name(&col_name);
4219                    self.state_container
4220                        .set_status_message(format!("Column '{col_name}' unpinned"));
4221                } else {
4222                    // Try to pin the column by name
4223                    match dataview.pin_column_by_name(&col_name) {
4224                        Ok(()) => {
4225                            self.state_container
4226                                .set_status_message(format!("Column '{col_name}' pinned [P]"));
4227                        }
4228                        Err(e) => {
4229                            self.state_container.set_status_message(e.to_string());
4230                        }
4231                    }
4232                }
4233
4234                // Update ViewportManager with the modified DataView
4235                if let Some(updated_dataview) = self.state_container.get_buffer_dataview() {
4236                    self.update_viewport_manager(Some(updated_dataview.clone()));
4237                }
4238            }
4239        } else {
4240            self.state_container
4241                .set_status_message("No column to pin at current position".to_string());
4242        }
4243    }
4244
4245    fn clear_all_pinned_columns_impl(&mut self) {
4246        if let Some(dataview) = self.state_container.get_buffer_dataview_mut() {
4247            dataview.clear_pinned_columns();
4248        }
4249        self.state_container
4250            .set_status_message("All columns unpinned".to_string());
4251
4252        // Update ViewportManager with the modified DataView
4253        if let Some(updated_dataview) = self.state_container.get_buffer_dataview() {
4254            self.update_viewport_manager(Some(updated_dataview.clone()));
4255        }
4256    }
4257
4258    fn calculate_column_statistics(&mut self) {
4259        use std::time::Instant;
4260
4261        let start_total = Instant::now();
4262
4263        // Collect all data first, then drop the buffer reference before calling analyzer
4264        let (column_name, data_to_analyze) = {
4265            // Get column names using DataProvider trait
4266            let headers = self.get_column_names_via_provider();
4267            if headers.is_empty() {
4268                return;
4269            }
4270
4271            let current_column = self.state_container.get_current_column();
4272            if current_column >= headers.len() {
4273                return;
4274            }
4275
4276            let column_name = headers[current_column].clone();
4277
4278            // Extract column data using DataProvider trait
4279            let data_to_analyze: Vec<String> = if let Some(provider) = self.get_data_provider() {
4280                let row_count = provider.get_row_count();
4281                let mut column_data = Vec::with_capacity(row_count);
4282
4283                for row_idx in 0..row_count {
4284                    if let Some(row) = provider.get_row(row_idx) {
4285                        if current_column < row.len() {
4286                            column_data.push(row[current_column].clone());
4287                        } else {
4288                            // Handle missing column data
4289                            column_data.push(String::new());
4290                        }
4291                    }
4292                }
4293
4294                column_data
4295            } else {
4296                // No data provider available
4297                return;
4298            };
4299
4300            (column_name, data_to_analyze)
4301        };
4302
4303        // Convert to references for the analyzer
4304        let data_refs: Vec<&str> = data_to_analyze
4305            .iter()
4306            .map(std::string::String::as_str)
4307            .collect();
4308
4309        // Use DataAnalyzer to calculate statistics
4310        let analyzer_stats = self
4311            .data_analyzer
4312            .calculate_column_statistics(&column_name, &data_refs);
4313
4314        // Convert from DataAnalyzer's ColumnStatistics to buffer's ColumnStatistics
4315        let stats = ColumnStatistics {
4316            column_name: analyzer_stats.column_name,
4317            column_type: match analyzer_stats.data_type {
4318                data_analyzer::ColumnType::Integer | data_analyzer::ColumnType::Float => {
4319                    ColumnType::Numeric
4320                }
4321                data_analyzer::ColumnType::String
4322                | data_analyzer::ColumnType::Boolean
4323                | data_analyzer::ColumnType::Date => ColumnType::String,
4324                data_analyzer::ColumnType::Mixed => ColumnType::Mixed,
4325                data_analyzer::ColumnType::Unknown => ColumnType::Mixed,
4326            },
4327            total_count: analyzer_stats.total_values,
4328            null_count: analyzer_stats.null_values,
4329            unique_count: analyzer_stats.unique_values,
4330            frequency_map: analyzer_stats.frequency_map.clone(),
4331            // For numeric columns, parse the min/max strings to f64
4332            min: analyzer_stats
4333                .min_value
4334                .as_ref()
4335                .and_then(|s| s.parse::<f64>().ok()),
4336            max: analyzer_stats
4337                .max_value
4338                .as_ref()
4339                .and_then(|s| s.parse::<f64>().ok()),
4340            sum: analyzer_stats.sum_value,
4341            mean: analyzer_stats.avg_value,
4342            median: analyzer_stats.median_value,
4343        };
4344
4345        // Calculate total time
4346        let elapsed = start_total.elapsed();
4347
4348        self.state_container.set_column_stats(Some(stats));
4349
4350        // Show timing in status message
4351        self.state_container.set_status_message(format!(
4352            "Column stats: {:.1}ms for {} values ({} unique)",
4353            elapsed.as_secs_f64() * 1000.0,
4354            data_to_analyze.len(),
4355            analyzer_stats.unique_values
4356        ));
4357
4358        self.state_container.set_mode(AppMode::ColumnStats);
4359        self.shadow_state
4360            .borrow_mut()
4361            .observe_mode_change(AppMode::ColumnStats, "column_stats_requested");
4362    }
4363
4364    fn check_parser_error(&self, query: &str) -> Option<String> {
4365        crate::ui::operations::simple_operations::check_parser_error(query)
4366    }
4367
4368    fn update_viewport_size(&mut self) {
4369        // Update the stored viewport size based on current terminal size
4370        if let Ok((width, height)) = crossterm::terminal::size() {
4371            // Calculate the actual data area height
4372            let data_rows_available = Self::calculate_available_data_rows(height);
4373
4374            // Let ViewportManager handle the calculations
4375            let visible_rows = {
4376                let mut viewport_manager_borrow = self.viewport_manager.borrow_mut();
4377                let viewport_manager = viewport_manager_borrow
4378                    .as_mut()
4379                    .expect("ViewportManager must exist for viewport size update");
4380                viewport_manager.update_terminal_size(width, data_rows_available)
4381            };
4382
4383            // Update buffer's last_visible_rows
4384            self.state_container.set_last_visible_rows(visible_rows);
4385
4386            // Update NavigationState's viewport dimensions
4387            self.state_container
4388                .navigation_mut()
4389                .set_viewport_size(visible_rows, width as usize);
4390
4391            info!(target: "navigation", "update_viewport_size - viewport set to: {}x{} rows", visible_rows, width);
4392        }
4393    }
4394
4395    // ========== SEARCH EXECUTION ==========
4396
4397    // Search and filter functions
4398    fn perform_search(&mut self) {
4399        if let Some(dataview) = self.get_current_data() {
4400            // Convert DataView rows to Vec<Vec<String>> for SearchManager
4401            let data: Vec<Vec<String>> = (0..dataview.row_count())
4402                .filter_map(|i| dataview.get_row(i))
4403                .map(|row| {
4404                    row.values
4405                        .iter()
4406                        .map(std::string::ToString::to_string)
4407                        .collect()
4408                })
4409                .collect();
4410
4411            // Get search pattern from buffer
4412            let pattern = self.state_container.get_search_pattern().to_string();
4413
4414            info!(target: "search", "=== SEARCH START ===");
4415            info!(target: "search", "Pattern: '{}', case_insensitive: {}",
4416                  pattern, self.state_container.is_case_insensitive());
4417            info!(target: "search", "Data dimensions: {} rows x {} columns",
4418                  data.len(), data.first().map_or(0, std::vec::Vec::len));
4419
4420            // Log column names to understand ordering
4421            let column_names = dataview.column_names();
4422            info!(target: "search", "Column names (first 5): {:?}",
4423                  column_names.iter().take(5).collect::<Vec<_>>());
4424
4425            // Log the first few rows of data we're searching
4426            for (i, row) in data.iter().take(10).enumerate() {
4427                info!(target: "search", "  Data row {}: [{}]", i,
4428                      row.iter().take(5).map(|s| format!("'{s}'")).collect::<Vec<_>>().join(", "));
4429            }
4430
4431            // Get visible columns if needed (for now search all columns)
4432            let visible_columns = None;
4433
4434            // Perform search using SearchManager
4435            let match_count = {
4436                let mut search_manager = self.search_manager.borrow_mut();
4437
4438                // IMPORTANT: Clear any previous search results first
4439                search_manager.clear();
4440                info!(target: "search", "Cleared previous search results");
4441
4442                // Update case sensitivity based on current setting
4443                search_manager.set_case_sensitive(!self.state_container.is_case_insensitive());
4444                info!(target: "search", "Set case_sensitive to {}", !self.state_container.is_case_insensitive());
4445
4446                // Perform the search
4447                let count = search_manager.search(&pattern, &data, visible_columns);
4448                info!(target: "search", "SearchManager.search() returned {} matches", count);
4449                count
4450            };
4451
4452            info!(target: "search", "SearchManager found {} matches", match_count);
4453
4454            // Process the matches
4455            if match_count > 0 {
4456                // Get first match for navigation and log details
4457                let (first_row, first_col) = {
4458                    let search_manager = self.search_manager.borrow();
4459                    if let Some(first_match) = search_manager.first_match() {
4460                        info!(target: "search", "FIRST MATCH DETAILS:");
4461                        info!(target: "search", "  Data coordinates: row={}, col={}",
4462                              first_match.row, first_match.column);
4463                        info!(target: "search", "  Matched value: '{}'", first_match.value);
4464                        info!(target: "search", "  Highlight range: {:?}", first_match.highlight_range);
4465
4466                        // Log first 5 matches for debugging
4467                        for (i, m) in search_manager.all_matches().iter().take(5).enumerate() {
4468                            info!(target: "search", "  Match #{}: row={}, col={}, value='{}'",
4469                                  i + 1, m.row, m.column, m.value);
4470                        }
4471
4472                        (first_match.row, first_match.column)
4473                    } else {
4474                        warn!(target: "search", "SearchManager reported matches but first_match() is None!");
4475                        (0, 0)
4476                    }
4477                };
4478
4479                // Update state container and buffer with matches
4480                self.state_container.set_table_selected_row(Some(first_row));
4481
4482                // CRITICAL: Update TableWidgetManager to trigger re-render
4483                info!(target: "search", "Updating TableWidgetManager to navigate to ({}, {})", first_row, first_col);
4484                self.table_widget_manager
4485                    .borrow_mut()
4486                    .navigate_to_search_match(first_row, first_col);
4487
4488                // Log what's actually at the position we're navigating to
4489                if let Some(dataview) = self.get_current_data() {
4490                    if let Some(row_data) = dataview.get_row(first_row) {
4491                        if first_col < row_data.values.len() {
4492                            info!(target: "search", "VALUE AT NAVIGATION TARGET ({}, {}): '{}'",
4493                                  first_row, first_col, row_data.values[first_col]);
4494                        }
4495                    }
4496                }
4497
4498                // Convert matches to buffer format
4499                let buffer_matches: Vec<(usize, usize)> = {
4500                    let search_manager = self.search_manager.borrow();
4501                    search_manager
4502                        .all_matches()
4503                        .iter()
4504                        .map(|m| (m.row, m.column))
4505                        .collect()
4506                };
4507
4508                // Also update AppStateContainer with matches (for compatibility)
4509                // Convert SearchManager matches to state_container format (row_start, col_start, row_end, col_end)
4510                let state_matches: Vec<(usize, usize, usize, usize)> = {
4511                    let search_manager = self.search_manager.borrow();
4512                    search_manager
4513                        .all_matches()
4514                        .iter()
4515                        .map(|m| {
4516                            // For now, treat each match as a single cell
4517                            (m.row, m.column, m.row, m.column)
4518                        })
4519                        .collect()
4520                };
4521                self.state_container.search_mut().matches = state_matches;
4522
4523                self.state_container
4524                    .set_search_matches_with_index(buffer_matches.clone(), 0);
4525                self.state_container
4526                    .set_current_match(Some((first_row, first_col)));
4527                self.state_container
4528                    .set_status_message(format!("Found {match_count} matches"));
4529
4530                info!(target: "search", "Search found {} matches for pattern '{}'", match_count, pattern);
4531            } else {
4532                // Clear search state
4533                self.state_container.search_mut().matches.clear();
4534                self.state_container.clear_search_state();
4535                self.state_container.set_current_match(None);
4536
4537                info!(target: "search", "No matches found for pattern '{}'", pattern);
4538            }
4539        }
4540    }
4541
4542    // --- Vim Search Methods ---
4543
4544    /// Start vim-like forward search (/ key)
4545    fn start_vim_search(&mut self) {
4546        info!(target: "vim_search", "Starting vim search mode");
4547
4548        // Start search mode in VimSearchManager
4549        self.vim_search_adapter.borrow_mut().start_search();
4550
4551        // Observe search start
4552        self.shadow_state.borrow_mut().observe_search_start(
4553            crate::ui::state::shadow_state::SearchType::Vim,
4554            "slash_key_pressed",
4555        );
4556
4557        // Use the existing SearchModesWidget which already has perfect debouncing
4558        self.enter_search_mode(SearchMode::Search);
4559    }
4560
4561    /// Navigate to next vim search match (n key)
4562    fn vim_search_next(&mut self) {
4563        if !self.vim_search_adapter.borrow().is_navigating() {
4564            // Try to resume last search if not currently navigating
4565            let resumed = {
4566                let mut viewport_borrow = self.viewport_manager.borrow_mut();
4567                if let Some(ref mut viewport) = *viewport_borrow {
4568                    if let Some(dataview) = self.state_container.get_buffer_dataview() {
4569                        self.vim_search_adapter
4570                            .borrow_mut()
4571                            .resume_last_search(dataview, viewport)
4572                    } else {
4573                        false
4574                    }
4575                } else {
4576                    false
4577                }
4578            };
4579
4580            if !resumed {
4581                self.state_container
4582                    .set_status_message("No previous search pattern".to_string());
4583                return;
4584            }
4585        }
4586
4587        // Navigate to next match
4588        let result = {
4589            let mut viewport_borrow = self.viewport_manager.borrow_mut();
4590            if let Some(ref mut viewport) = *viewport_borrow {
4591                let search_match = self.vim_search_adapter.borrow_mut().next_match(viewport);
4592                if search_match.is_some() {
4593                    let match_info = self.vim_search_adapter.borrow().get_match_info();
4594                    search_match.map(|m| (m, match_info))
4595                } else {
4596                    None
4597                }
4598            } else {
4599                None
4600            }
4601        }; // Drop viewport_borrow here
4602
4603        // Update selected row AND column AFTER dropping the viewport borrow
4604        if let Some((ref search_match, _)) = result {
4605            // Log what we're updating
4606            info!(target: "search", 
4607                "=== UPDATING TUI STATE FOR VIM SEARCH ===");
4608            info!(target: "search", 
4609                "Setting selected row to: {}", search_match.row);
4610            info!(target: "search", 
4611                "Setting selected column to: {} (visual col)", search_match.col);
4612
4613            // Verify what's actually at this position
4614            if let Some(dataview) = self.state_container.get_buffer_dataview() {
4615                // First, let's verify what row we're actually getting
4616                info!(target: "search",
4617                    "DEBUG: Fetching row {} from dataview with {} total rows",
4618                    search_match.row, dataview.row_count());
4619
4620                if let Some(row_data) = dataview.get_row(search_match.row) {
4621                    // Log ALL values in this row to debug the mismatch
4622                    info!(target: "search",
4623                        "Row {} has {} values, first 5: {:?}",
4624                        search_match.row, row_data.values.len(),
4625                        row_data.values.iter().take(5).map(std::string::ToString::to_string).collect::<Vec<_>>());
4626
4627                    if search_match.col < row_data.values.len() {
4628                        let actual_value = &row_data.values[search_match.col];
4629                        info!(target: "search", 
4630                            "Actual value at row {} col {}: '{}'", 
4631                            search_match.row, search_match.col, actual_value);
4632
4633                        // Check if it actually contains the pattern
4634                        let pattern = self
4635                            .vim_search_adapter
4636                            .borrow()
4637                            .get_pattern()
4638                            .unwrap_or_default();
4639                        let contains_pattern = actual_value
4640                            .to_string()
4641                            .to_lowercase()
4642                            .contains(&pattern.to_lowercase());
4643                        if contains_pattern {
4644                            info!(target: "search", 
4645                                "✓ Confirmed: Cell contains pattern '{}'", pattern);
4646                        } else {
4647                            warn!(target: "search", 
4648                                "WARNING: Cell at ({}, {}) = '{}' does NOT contain pattern '{}'!", 
4649                                search_match.row, search_match.col, actual_value, pattern);
4650                        }
4651                    } else {
4652                        warn!(target: "search", 
4653                            "Column {} is out of bounds for row {} (row has {} values)", 
4654                            search_match.col, search_match.row, row_data.values.len());
4655                    }
4656                } else {
4657                    warn!(target: "search", 
4658                        "Could not get row data for row {}", search_match.row);
4659                }
4660
4661                // Also log display columns to understand the mapping
4662                let display_columns = dataview.get_display_columns();
4663                info!(target: "search", 
4664                    "Display columns mapping (first 10): {:?}", 
4665                    display_columns.iter().take(10).collect::<Vec<_>>());
4666            }
4667
4668            self.state_container
4669                .set_table_selected_row(Some(search_match.row));
4670            self.state_container
4671                .set_selected_row(Some(search_match.row));
4672
4673            // Update the selected column - search_match.col is already in visual coordinates
4674            // Keep everything in visual column indices for consistency
4675            info!(target: "search",
4676                "Setting column to visual index {}",
4677                search_match.col);
4678
4679            // Update all column-related state to the visual column index
4680            self.state_container
4681                .set_current_column_buffer(search_match.col);
4682            self.state_container.navigation_mut().selected_column = search_match.col;
4683
4684            // CRITICAL: Update SelectionState's selected_column too!
4685            self.state_container.select_column(search_match.col);
4686            info!(target: "search", 
4687                "Updated SelectionState column to: {}", search_match.col);
4688
4689            // Log the current state of all column-related fields
4690            info!(target: "search", 
4691                "Column state after update: nav.selected_column={}, buffer.current_column={}, selection.selected_column={}", 
4692                self.state_container.navigation().selected_column,
4693                self.state_container.get_current_column(),
4694                self.state_container.selection().selected_column);
4695
4696            // CRITICAL: Sync NavigationState with ViewportManager after vim search navigation
4697            // ViewportManager has the correct state after navigation, sync it back
4698            self.sync_navigation_with_viewport();
4699
4700            // Also update the buffer scroll offset
4701            let scroll_offset = self.state_container.navigation().scroll_offset;
4702            self.state_container.set_scroll_offset(scroll_offset);
4703
4704            // CRITICAL: Update TableWidgetManager to trigger re-render
4705            info!(target: "search", "Updating TableWidgetManager for vim search navigation to ({}, {})",
4706                  search_match.row, search_match.col);
4707            self.table_widget_manager
4708                .borrow_mut()
4709                .navigate_to(search_match.row, search_match.col);
4710
4711            // Log column state after TableWidgetManager update
4712            info!(target: "search", 
4713                "After TableWidgetManager update: nav.selected_column={}, buffer.current_column={}, selection.selected_column={}", 
4714                self.state_container.navigation().selected_column,
4715                self.state_container.get_current_column(),
4716                self.state_container.selection().selected_column);
4717
4718            // The ViewportManager has already handled all scrolling logic
4719            // Our sync_navigation_with_viewport() call above has updated NavigationState
4720            // No need for additional manual scroll updates
4721        }
4722
4723        // Update status without borrow conflicts
4724        if let Some((search_match, match_info)) = result {
4725            if let Some((current, total)) = match_info {
4726                self.state_container.set_status_message(format!(
4727                    "Match {}/{} at ({}, {})",
4728                    current,
4729                    total,
4730                    search_match.row + 1,
4731                    search_match.col + 1
4732                ));
4733            }
4734
4735            // Final column state logging
4736            info!(target: "search", 
4737                "FINAL vim_search_next state: nav.selected_column={}, buffer.current_column={}, selection.selected_column={}", 
4738                self.state_container.navigation().selected_column,
4739                self.state_container.get_current_column(),
4740                self.state_container.selection().selected_column);
4741
4742            // CRITICAL: Verify what's actually at the final position
4743            if let Some(dataview) = self.state_container.get_buffer_dataview() {
4744                let final_row = self.state_container.navigation().selected_row;
4745                let final_col = self.state_container.navigation().selected_column;
4746
4747                if let Some(row_data) = dataview.get_row(final_row) {
4748                    if final_col < row_data.values.len() {
4749                        let actual_value = &row_data.values[final_col];
4750                        info!(target: "search", 
4751                            "VERIFICATION: Cell at final position ({}, {}) contains: '{}'",
4752                            final_row, final_col, actual_value);
4753
4754                        let pattern = self
4755                            .vim_search_adapter
4756                            .borrow()
4757                            .get_pattern()
4758                            .unwrap_or_default();
4759                        if !actual_value
4760                            .to_string()
4761                            .to_lowercase()
4762                            .contains(&pattern.to_lowercase())
4763                        {
4764                            error!(target: "search",
4765                                "ERROR: Final cell '{}' does NOT contain search pattern '{}'!",
4766                                actual_value, pattern);
4767                        }
4768                    }
4769                }
4770            }
4771        }
4772    }
4773
4774    /// Navigate to previous vim search match (N key)
4775    fn vim_search_previous(&mut self) {
4776        if !self.vim_search_adapter.borrow().is_navigating() {
4777            // Try to resume last search if not currently navigating
4778            let resumed = {
4779                let mut viewport_borrow = self.viewport_manager.borrow_mut();
4780                if let Some(ref mut viewport) = *viewport_borrow {
4781                    if let Some(dataview) = self.state_container.get_buffer_dataview() {
4782                        self.vim_search_adapter
4783                            .borrow_mut()
4784                            .resume_last_search(dataview, viewport)
4785                    } else {
4786                        false
4787                    }
4788                } else {
4789                    false
4790                }
4791            };
4792
4793            if !resumed {
4794                self.state_container
4795                    .set_status_message("No previous search pattern".to_string());
4796                return;
4797            }
4798        }
4799
4800        // Navigate to previous match
4801        let result = {
4802            let mut viewport_borrow = self.viewport_manager.borrow_mut();
4803            if let Some(ref mut viewport) = *viewport_borrow {
4804                let search_match = self
4805                    .vim_search_adapter
4806                    .borrow_mut()
4807                    .previous_match(viewport);
4808                if search_match.is_some() {
4809                    let match_info = self.vim_search_adapter.borrow().get_match_info();
4810                    search_match.map(|m| (m, match_info))
4811                } else {
4812                    None
4813                }
4814            } else {
4815                None
4816            }
4817        }; // Drop viewport_borrow here
4818
4819        // Update selected row AND column AFTER dropping the viewport borrow
4820        if let Some((ref search_match, _)) = result {
4821            self.state_container
4822                .set_table_selected_row(Some(search_match.row));
4823            self.state_container
4824                .set_selected_row(Some(search_match.row));
4825
4826            // Update the selected column - search_match.col is already in visual coordinates
4827            // Keep everything in visual column indices for consistency
4828            info!(target: "search",
4829                "Setting column to visual index {}",
4830                search_match.col);
4831
4832            // Update all column-related state to the visual column index
4833            self.state_container
4834                .set_current_column_buffer(search_match.col);
4835            self.state_container.navigation_mut().selected_column = search_match.col;
4836
4837            // CRITICAL: Update SelectionState's selected_column too!
4838            self.state_container.select_column(search_match.col);
4839            info!(target: "search", 
4840                "Updated SelectionState column to: {}", search_match.col);
4841
4842            // Log the current state of all column-related fields
4843            info!(target: "search", 
4844                "Column state after update: nav.selected_column={}, buffer.current_column={}, selection.selected_column={}", 
4845                self.state_container.navigation().selected_column,
4846                self.state_container.get_current_column(),
4847                self.state_container.selection().selected_column);
4848
4849            // CRITICAL: Sync NavigationState with ViewportManager after vim search navigation
4850            // ViewportManager has the correct state after navigation, sync it back
4851            self.sync_navigation_with_viewport();
4852
4853            // Also update the buffer scroll offset
4854            let scroll_offset = self.state_container.navigation().scroll_offset;
4855            self.state_container.set_scroll_offset(scroll_offset);
4856
4857            // CRITICAL: Update TableWidgetManager to trigger re-render
4858            info!(target: "search", "Updating TableWidgetManager for vim search navigation to ({}, {})",
4859                  search_match.row, search_match.col);
4860            self.table_widget_manager
4861                .borrow_mut()
4862                .navigate_to(search_match.row, search_match.col);
4863
4864            // Log column state after TableWidgetManager update
4865            info!(target: "search", 
4866                "After TableWidgetManager update: nav.selected_column={}, buffer.current_column={}, selection.selected_column={}", 
4867                self.state_container.navigation().selected_column,
4868                self.state_container.get_current_column(),
4869                self.state_container.selection().selected_column);
4870
4871            // The ViewportManager has already handled all scrolling logic
4872            // Our sync_navigation_with_viewport() call above has updated NavigationState
4873            // No need for additional manual scroll updates
4874        }
4875
4876        // Update status without borrow conflicts
4877        if let Some((search_match, match_info)) = result {
4878            if let Some((current, total)) = match_info {
4879                self.state_container.set_status_message(format!(
4880                    "Match {}/{} at ({}, {})",
4881                    current,
4882                    total,
4883                    search_match.row + 1,
4884                    search_match.col + 1
4885                ));
4886            }
4887        }
4888        // ========== FILTER EXECUTION ==========
4889    }
4890
4891    fn apply_filter(&mut self, pattern: &str) {
4892        use std::sync::atomic::{AtomicUsize, Ordering};
4893
4894        // Simple re-entrancy detection without macros
4895        static FILTER_DEPTH: AtomicUsize = AtomicUsize::new(0);
4896        let depth = FILTER_DEPTH.fetch_add(1, Ordering::SeqCst);
4897        if depth > 0 {
4898            eprintln!(
4899                "WARNING: apply_filter re-entrancy detected! depth={}, pattern='{}', thread={:?}",
4900                depth,
4901                pattern,
4902                std::thread::current().id()
4903            );
4904        }
4905
4906        info!(
4907            "Applying filter: '{}' on thread {:?}",
4908            pattern,
4909            std::thread::current().id()
4910        );
4911
4912        // Delegate state coordination to StateCoordinator
4913        use crate::ui::state::state_coordinator::StateCoordinator;
4914        let _rows_after =
4915            StateCoordinator::apply_text_filter_with_refs(&mut self.state_container, pattern);
4916
4917        // Update ViewportManager with the filtered DataView
4918        // Sync the dataview to both managers
4919        self.sync_dataview_to_managers();
4920
4921        // Decrement re-entrancy counter
4922        FILTER_DEPTH.fetch_sub(1, Ordering::SeqCst);
4923    }
4924    fn search_columns(&mut self) {
4925        // Safety: Prevent infinite recursion with a static counter
4926        static SEARCH_DEPTH: std::sync::atomic::AtomicUsize =
4927            std::sync::atomic::AtomicUsize::new(0);
4928        let depth = SEARCH_DEPTH.fetch_add(1, std::sync::atomic::Ordering::SeqCst);
4929
4930        // Guard against excessive recursion
4931        if depth > 10 {
4932            error!(target: "search", "Column search depth exceeded limit, aborting to prevent infinite loop");
4933            SEARCH_DEPTH.store(0, std::sync::atomic::Ordering::SeqCst);
4934            return;
4935        }
4936
4937        // Create a guard that will decrement on drop
4938        struct DepthGuard;
4939        impl Drop for DepthGuard {
4940            fn drop(&mut self) {
4941                SEARCH_DEPTH.fetch_sub(1, std::sync::atomic::Ordering::SeqCst);
4942            }
4943        }
4944        let _guard = DepthGuard;
4945
4946        let pattern = self.state_container.column_search().pattern.clone();
4947        debug!(target: "search", "search_columns called with pattern: '{}', depth: {}", pattern, depth);
4948
4949        if pattern.is_empty() {
4950            debug!(target: "search", "Pattern is empty, skipping column search");
4951            return;
4952        }
4953
4954        // Update DataView's column search and get matches
4955        let matching_columns = if let Some(dataview) =
4956            self.state_container.get_buffer_dataview_mut()
4957        {
4958            dataview.search_columns(&pattern);
4959
4960            // Get matching columns from DataView
4961            let matches = dataview.get_matching_columns().to_vec();
4962            debug!(target: "search", "DataView found {} matching columns", matches.len());
4963            if !matches.is_empty() {
4964                for (idx, (col_idx, col_name)) in matches.iter().enumerate() {
4965                    debug!(target: "search", "  Match {}: '{}' at visual index {}", idx + 1, col_name, col_idx);
4966                }
4967            }
4968
4969            // Also sync with AppStateContainer for compatibility
4970            let columns: Vec<(String, usize)> = matches
4971                .iter()
4972                .map(|(idx, name)| (name.clone(), *idx))
4973                .collect();
4974            self.state_container
4975                .update_column_search_matches(&columns, &pattern);
4976
4977            matches
4978        } else {
4979            debug!(target: "search", "No DataView available for column search");
4980            Vec::new()
4981        };
4982
4983        if matching_columns.is_empty() {
4984            let status_msg = format!("No columns matching '{pattern}'");
4985            debug!(target: "search", "Setting status: {}", status_msg);
4986            self.state_container.set_status_message(status_msg);
4987        } else {
4988            // Move to first match - the index from DataView is already a VISUAL index
4989            let first_match_visual_idx = matching_columns[0].0;
4990            let first_match_name = &matching_columns[0].1;
4991
4992            // Convert visual index to DataTable index for Buffer/AppStateContainer (legacy compatibility)
4993            let datatable_idx = if let Some(dataview) = self.state_container.get_buffer_dataview() {
4994                let display_columns = dataview.get_display_columns();
4995                if first_match_visual_idx < display_columns.len() {
4996                    display_columns[first_match_visual_idx]
4997                } else {
4998                    first_match_visual_idx // Fallback
4999                }
5000            } else {
5001                first_match_visual_idx
5002            };
5003
5004            self.state_container.set_current_column(datatable_idx);
5005            self.state_container
5006                .set_current_column_buffer(datatable_idx);
5007
5008            // Update viewport to show the first match using ViewportManager
5009            // ViewportManager expects VISUAL index
5010            {
5011                let mut viewport_manager_borrow = self.viewport_manager.borrow_mut();
5012                if let Some(viewport_manager) = viewport_manager_borrow.as_mut() {
5013                    let viewport_changed =
5014                        viewport_manager.set_current_column(first_match_visual_idx);
5015
5016                    // Sync navigation state with updated viewport
5017                    if viewport_changed {
5018                        let new_viewport = viewport_manager.viewport_cols().clone();
5019                        let pinned_count =
5020                            if let Some(dv) = self.state_container.get_buffer_dataview() {
5021                                dv.get_pinned_columns().len()
5022                            } else {
5023                                0
5024                            };
5025                        let scrollable_offset = new_viewport.start.saturating_sub(pinned_count);
5026                        self.state_container.navigation_mut().scroll_offset.1 = scrollable_offset;
5027
5028                        debug!(target: "navigation",
5029                            "Column search initial: Jumped to column {} '{}', viewport adjusted to {:?}",
5030                            first_match_visual_idx, first_match_name, new_viewport);
5031                    }
5032                }
5033            }
5034
5035            debug!(target: "search", "Setting current column to visual index {} ('{}')",
5036                   first_match_visual_idx, first_match_name);
5037            let status_msg = format!(
5038                "Found {} columns matching '{}'. Tab/Shift-Tab to navigate.",
5039                matching_columns.len(),
5040                pattern
5041            );
5042            debug!(target: "search", "Setting status: {}", status_msg);
5043            self.state_container.set_status_message(status_msg);
5044
5045            // Column search matches are now managed by AppStateContainer
5046        }
5047
5048        // Matching columns are now stored in AppStateContainer
5049    }
5050
5051    fn next_column_match(&mut self) {
5052        // Use DataView's column search navigation
5053        // Extract all needed data first to avoid borrow conflicts
5054        let column_match_data =
5055            if let Some(dataview) = self.state_container.get_buffer_dataview_mut() {
5056                if let Some(visual_idx) = dataview.next_column_match() {
5057                    // Get the column name and match info
5058                    let matching_columns = dataview.get_matching_columns();
5059                    let current_match_index = dataview.current_column_match_index();
5060                    let current_match = current_match_index + 1;
5061                    let total_matches = matching_columns.len();
5062                    let col_name = matching_columns
5063                        .get(current_match_index)
5064                        .map(|(_, name)| name.clone())
5065                        .unwrap_or_default();
5066
5067                    // Convert visual index to DataTable index for Buffer/AppStateContainer
5068                    // (they still use DataTable indices for now)
5069                    let display_columns = dataview.get_display_columns();
5070                    let datatable_idx = if visual_idx < display_columns.len() {
5071                        display_columns[visual_idx]
5072                    } else {
5073                        visual_idx // Fallback
5074                    };
5075
5076                    Some((
5077                        visual_idx,
5078                        datatable_idx,
5079                        col_name,
5080                        current_match,
5081                        total_matches,
5082                        current_match_index,
5083                    ))
5084                } else {
5085                    None
5086                }
5087            } else {
5088                None
5089            };
5090
5091        // Now process the match data without holding dataview reference
5092        if let Some((
5093            visual_idx,
5094            datatable_idx,
5095            col_name,
5096            current_match,
5097            total_matches,
5098            current_match_index,
5099        )) = column_match_data
5100        {
5101            // Update both AppStateContainer and Buffer with DataTable index (for legacy compatibility)
5102            self.state_container.set_current_column(datatable_idx);
5103            self.state_container
5104                .set_current_column_buffer(datatable_idx);
5105
5106            // Update viewport to show the column using ViewportManager
5107            // ViewportManager's set_current_column now expects VISUAL index
5108            {
5109                let mut viewport_manager_borrow = self.viewport_manager.borrow_mut();
5110                if let Some(viewport_manager) = viewport_manager_borrow.as_mut() {
5111                    viewport_manager.set_current_column(visual_idx);
5112                }
5113            }
5114
5115            // Always sync navigation state with updated viewport (like vim search)
5116            debug!(target: "column_search_sync", "next_column_match: About to call sync_navigation_with_viewport() - visual_idx: {}, datatable_idx: {}", visual_idx, datatable_idx);
5117            debug!(target: "column_search_sync", "next_column_match: Pre-sync - viewport current_column: {}", 
5118                if let Ok(vm) = self.viewport_manager.try_borrow() {
5119                    vm.as_ref().map_or(0, super::viewport_manager::ViewportManager::get_crosshair_col)
5120                } else { 0 });
5121            self.sync_navigation_with_viewport();
5122            debug!(target: "column_search_sync", "next_column_match: Post-sync - navigation current_column: {}", 
5123                self.state_container.navigation().selected_column);
5124            debug!(target: "column_search_sync", "next_column_match: sync_navigation_with_viewport() completed");
5125
5126            debug!(target: "navigation",
5127                "Column search: Jumped to visual column {} (datatable: {}) '{}', synced with viewport",
5128                visual_idx, datatable_idx, col_name);
5129
5130            // CRITICAL: Update AppStateContainer's column_search.current_match
5131            // This ensures Enter key will jump to the correct column
5132            {
5133                let mut column_search = self.state_container.column_search_mut();
5134                column_search.current_match = current_match_index;
5135                debug!(target: "column_search_sync", "next_column_match: Updated AppStateContainer column_search.current_match to {}", column_search.current_match);
5136            }
5137
5138            self.state_container.set_status_message(format!(
5139                "Column {current_match}/{total_matches}: {col_name} - Tab/Shift-Tab to navigate"
5140            ));
5141        }
5142    }
5143
5144    fn previous_column_match(&mut self) {
5145        // Use DataView's column search navigation
5146        // Extract all needed data first to avoid borrow conflicts
5147        let column_match_data =
5148            if let Some(dataview) = self.state_container.get_buffer_dataview_mut() {
5149                if let Some(visual_idx) = dataview.prev_column_match() {
5150                    // Get the column name and match info
5151                    let matching_columns = dataview.get_matching_columns();
5152                    let current_match_index = dataview.current_column_match_index();
5153                    let current_match = current_match_index + 1;
5154                    let total_matches = matching_columns.len();
5155                    let col_name = matching_columns
5156                        .get(current_match_index)
5157                        .map(|(_, name)| name.clone())
5158                        .unwrap_or_default();
5159
5160                    // Convert visual index to DataTable index for Buffer/AppStateContainer
5161                    // (they still use DataTable indices for now)
5162                    let display_columns = dataview.get_display_columns();
5163                    let datatable_idx = if visual_idx < display_columns.len() {
5164                        display_columns[visual_idx]
5165                    } else {
5166                        visual_idx // Fallback
5167                    };
5168
5169                    Some((
5170                        visual_idx,
5171                        datatable_idx,
5172                        col_name,
5173                        current_match,
5174                        total_matches,
5175                        current_match_index,
5176                    ))
5177                } else {
5178                    None
5179                }
5180            } else {
5181                None
5182            };
5183
5184        // Now process the match data without holding dataview reference
5185        if let Some((
5186            visual_idx,
5187            datatable_idx,
5188            col_name,
5189            current_match,
5190            total_matches,
5191            current_match_index,
5192        )) = column_match_data
5193        {
5194            // Update both AppStateContainer and Buffer with DataTable index (for legacy compatibility)
5195            self.state_container.set_current_column(datatable_idx);
5196            self.state_container
5197                .set_current_column_buffer(datatable_idx);
5198
5199            // Update viewport to show the column using ViewportManager
5200            // ViewportManager's set_current_column now expects VISUAL index
5201            {
5202                let mut viewport_manager_borrow = self.viewport_manager.borrow_mut();
5203                if let Some(viewport_manager) = viewport_manager_borrow.as_mut() {
5204                    viewport_manager.set_current_column(visual_idx);
5205                }
5206            }
5207
5208            // Always sync navigation state with updated viewport (like vim search)
5209            debug!(target: "column_search_sync", "previous_column_match: About to call sync_navigation_with_viewport() - visual_idx: {}, datatable_idx: {}", visual_idx, datatable_idx);
5210            debug!(target: "column_search_sync", "previous_column_match: Pre-sync - viewport current_column: {}", 
5211                if let Ok(vm) = self.viewport_manager.try_borrow() {
5212                    vm.as_ref().map_or(0, super::viewport_manager::ViewportManager::get_crosshair_col)
5213                } else { 0 });
5214            self.sync_navigation_with_viewport();
5215            debug!(target: "column_search_sync", "previous_column_match: Post-sync - navigation current_column: {}", 
5216                self.state_container.navigation().selected_column);
5217            debug!(target: "column_search_sync", "previous_column_match: sync_navigation_with_viewport() completed");
5218
5219            debug!(target: "navigation",
5220                "Column search (prev): Jumped to visual column {} (datatable: {}) '{}', synced with viewport",
5221                visual_idx, datatable_idx, col_name);
5222
5223            // CRITICAL: Update AppStateContainer's column_search.current_match
5224            // This ensures Enter key will jump to the correct column
5225            {
5226                let mut column_search = self.state_container.column_search_mut();
5227                column_search.current_match = current_match_index;
5228                debug!(target: "column_search_sync", "previous_column_match: Updated AppStateContainer column_search.current_match to {}", column_search.current_match);
5229            }
5230
5231            self.state_container.set_status_message(format!(
5232                "Column {current_match}/{total_matches}: {col_name} - Tab/Shift-Tab to navigate"
5233            ));
5234        }
5235    }
5236
5237    fn apply_fuzzy_filter(&mut self) {
5238        info!(
5239            "apply_fuzzy_filter called on thread {:?}",
5240            std::thread::current().id()
5241        );
5242
5243        // Delegate all state coordination to StateCoordinator
5244        use crate::ui::state::state_coordinator::StateCoordinator;
5245        let (_match_count, indices) = StateCoordinator::apply_fuzzy_filter_with_refs(
5246            &mut self.state_container,
5247            &self.viewport_manager,
5248        );
5249
5250        // Update fuzzy filter indices for compatibility
5251        self.state_container.set_fuzzy_filter_indices(indices);
5252
5253        // Update ViewportManager with the filtered DataView
5254        // Sync the dataview to both managers
5255        self.sync_dataview_to_managers();
5256    }
5257
5258    fn toggle_sort_current_column(&mut self) {
5259        // Get visual column index from ViewportManager's crosshair
5260        let visual_col_idx = if let Some(ref viewport_manager) = *self.viewport_manager.borrow() {
5261            viewport_manager.get_crosshair_col()
5262        } else {
5263            0
5264        };
5265
5266        if let Some(dataview) = self.state_container.get_buffer_dataview_mut() {
5267            // DataView.toggle_sort expects VISIBLE column index
5268            // Get column name for display
5269            let column_names = dataview.column_names();
5270            let col_name = column_names
5271                .get(visual_col_idx)
5272                .cloned()
5273                .unwrap_or_else(|| format!("Column {visual_col_idx}"));
5274
5275            debug!(
5276                "toggle_sort_current_column: visual_idx={}, column_name={}",
5277                visual_col_idx, col_name
5278            );
5279
5280            if let Err(e) = dataview.toggle_sort(visual_col_idx) {
5281                self.state_container
5282                    .set_status_message(format!("Sort error: {e}"));
5283            } else {
5284                // Get the new sort state for status message
5285                let sort_state = dataview.get_sort_state();
5286                let message = match sort_state.order {
5287                    crate::data::data_view::SortOrder::Ascending => {
5288                        format!("Sorted '{col_name}' ascending ↑")
5289                    }
5290                    crate::data::data_view::SortOrder::Descending => {
5291                        format!("Sorted '{col_name}' descending ↓")
5292                    }
5293                    crate::data::data_view::SortOrder::None => {
5294                        format!("Cleared sort on '{col_name}'")
5295                    }
5296                };
5297                self.state_container.set_status_message(message);
5298
5299                // Update ViewportManager with the sorted DataView to keep them in sync
5300                if let Some(updated_dataview) = self.state_container.get_buffer_dataview() {
5301                    // Update TableWidgetManager with the sorted dataview as well
5302                    self.table_widget_manager
5303                        .borrow_mut()
5304                        .set_dataview(Arc::new(updated_dataview.clone()));
5305
5306                    let mut viewport_manager_borrow = self.viewport_manager.borrow_mut();
5307                    if let Some(ref mut viewport_manager) = *viewport_manager_borrow {
5308                        viewport_manager.set_dataview(Arc::new(updated_dataview.clone()));
5309                        debug!("Updated ViewportManager with sorted DataView");
5310                    }
5311                }
5312            }
5313        } else {
5314            // Could not find display position in DataTable
5315            self.state_container
5316                .set_status_message("Error: Invalid column position".to_string());
5317        }
5318    }
5319
5320    fn get_current_data(&self) -> Option<&DataView> {
5321        self.state_container.get_buffer_dataview()
5322    }
5323
5324    fn get_row_count(&self) -> usize {
5325        // Check if fuzzy filter is active first (most specific filter)
5326        if self.state_container.is_fuzzy_filter_active() {
5327            // Return the count of fuzzy filtered indices
5328            self.state_container.get_fuzzy_filter_indices().len()
5329        } else if let Some(dataview) = self.state_container.get_buffer_dataview() {
5330            // Return count from WHERE clause or other filters
5331            dataview.row_count()
5332        } else if let Some(provider) = self.get_data_provider() {
5333            // Use DataProvider trait for data access (migration step)
5334            provider.get_row_count()
5335        } else {
5336            0
5337        }
5338    }
5339
5340    /// Helper to sync dataview to both `ViewportManager` and `TableWidgetManager`
5341    fn sync_dataview_to_managers(&self) {
5342        if let Some(dataview) = self.state_container.get_buffer_dataview() {
5343            let arc_dataview = Arc::new(dataview.clone());
5344
5345            // Update ViewportManager
5346            if let Some(ref mut viewport_manager) = *self.viewport_manager.borrow_mut() {
5347                viewport_manager.set_dataview(arc_dataview.clone());
5348                debug!(
5349                    "Updated ViewportManager with DataView (row_count={})",
5350                    arc_dataview.row_count()
5351                );
5352            }
5353
5354            // Update TableWidgetManager
5355            self.table_widget_manager
5356                .borrow_mut()
5357                .set_dataview(arc_dataview);
5358            debug!("Updated TableWidgetManager with DataView");
5359        }
5360    }
5361
5362    pub fn reset_table_state(&mut self) {
5363        // Delegate all state reset coordination to StateCoordinator
5364        use crate::ui::state::state_coordinator::StateCoordinator;
5365        StateCoordinator::reset_table_state_with_refs(
5366            &mut self.state_container,
5367            &self.viewport_manager,
5368        );
5369    }
5370
5371    fn update_parser_for_current_buffer(&mut self) {
5372        // Sync input states
5373        self.sync_all_input_states();
5374
5375        // Delegate parser update to StateCoordinator
5376        use crate::ui::state::state_coordinator::StateCoordinator;
5377        StateCoordinator::update_parser_with_refs(&self.state_container, &mut self.hybrid_parser);
5378    }
5379
5380    /// Synchronize all state after buffer switch
5381    /// This should be called after any buffer switch operation to ensure:
5382    /// 1. Viewport is restored from the new buffer
5383    /// 2. Parser schema is updated with the new buffer's columns
5384    fn sync_after_buffer_switch(&mut self) {
5385        // Restore viewport state from new buffer
5386        self.restore_viewport_from_current_buffer();
5387
5388        // Update parser schema for the new buffer
5389        self.update_parser_for_current_buffer();
5390    }
5391
5392    /// Update `ViewportManager` when `DataView` changes
5393    fn update_viewport_manager(&mut self, dataview: Option<DataView>) {
5394        if let Some(dv) = dataview {
5395            // Get current column position to preserve it
5396            let current_column = self.state_container.get_current_column();
5397
5398            // Create new ViewportManager with the new DataView
5399            let mut new_viewport_manager = ViewportManager::new(Arc::new(dv));
5400
5401            // Update terminal size from current terminal
5402            if let Ok((width, height)) = crossterm::terminal::size() {
5403                // Calculate the actual data area height
5404                let data_rows_available = Self::calculate_available_data_rows(height);
5405                new_viewport_manager.update_terminal_size(width, data_rows_available);
5406                debug!(
5407                    "Updated new ViewportManager terminal size: {}x{} (data rows)",
5408                    width, data_rows_available
5409                );
5410            }
5411
5412            // Set the current column position to ensure proper viewport initialization
5413            // This is crucial for SELECT queries that subset columns
5414            if current_column < new_viewport_manager.dataview().column_count() {
5415                new_viewport_manager.set_current_column(current_column);
5416            } else {
5417                // If current column is out of bounds, reset to first column
5418                new_viewport_manager.set_current_column(0);
5419                self.state_container.set_current_column_buffer(0);
5420            }
5421
5422            *self.viewport_manager.borrow_mut() = Some(new_viewport_manager);
5423            debug!(
5424                "ViewportManager updated with new DataView, current_column={}",
5425                current_column
5426            );
5427        } else {
5428            // Clear ViewportManager if no DataView
5429            *self.viewport_manager.borrow_mut() = None;
5430            debug!("ViewportManager cleared (no DataView)");
5431        }
5432    }
5433
5434    pub fn calculate_optimal_column_widths(&mut self) {
5435        // Delegate to ViewportManager for optimal column width calculations
5436        let widths_from_viewport = {
5437            let mut viewport_opt = self.viewport_manager.borrow_mut();
5438            (*viewport_opt)
5439                .as_mut()
5440                .map(super::viewport_manager::ViewportManager::calculate_optimal_column_widths)
5441        };
5442
5443        if let Some(widths) = widths_from_viewport {
5444            self.state_container.set_column_widths(widths);
5445        }
5446    }
5447
5448    /// Centralized method for setting status messages
5449    /// Ensures consistent logging and state synchronization
5450    pub fn set_status_message(&mut self, message: impl Into<String>) {
5451        let msg = message.into();
5452        debug!("Status: {}", msg);
5453        self.state_container.set_status_message(msg.clone());
5454        // Future: Could also sync to state_container if needed
5455        // self.state_container.set_status(msg);
5456    }
5457
5458    /// Set error status message with consistent formatting
5459    fn set_error_status(&mut self, context: &str, error: impl std::fmt::Display) {
5460        let msg = format!("{context}: {error}");
5461        debug!("Error status: {}", msg);
5462        self.set_status_message(msg);
5463    }
5464
5465    fn export_to_csv(&mut self) {
5466        let result = {
5467            let ctx = crate::ui::operations::data_export_operations::DataExportContext {
5468                data_provider: self.get_data_provider(),
5469            };
5470            crate::ui::operations::data_export_operations::export_to_csv(&ctx)
5471        };
5472
5473        match result {
5474            crate::ui::operations::data_export_operations::ExportResult::Success(message) => {
5475                self.set_status_message(message);
5476            }
5477            crate::ui::operations::data_export_operations::ExportResult::Error(error) => {
5478                self.set_error_status("Export failed", error);
5479            }
5480        }
5481    }
5482
5483    // ========== YANK OPERATIONS ==========
5484
5485    // Yank operations are provided by the YankBehavior trait in traits/yank_ops.rs
5486    // The trait provides: yank_cell, yank_row, yank_column, yank_all, yank_query,
5487    // yank_as_test_case, and yank_debug_with_context
5488
5489    fn paste_from_clipboard(&mut self) {
5490        // Paste from system clipboard into the current input field
5491        match self.state_container.read_from_clipboard() {
5492            Ok(text) => {
5493                let mode = self.shadow_state.borrow().get_mode();
5494                match mode {
5495                    AppMode::Command => {
5496                        // Always use single-line mode paste
5497                        // Get current cursor position
5498                        let cursor_pos = self.get_input_cursor();
5499                        let current_value = self.get_input_text();
5500
5501                        // Insert at cursor position
5502                        let mut new_value = String::new();
5503                        new_value.push_str(&current_value[..cursor_pos]);
5504                        new_value.push_str(&text);
5505                        new_value.push_str(&current_value[cursor_pos..]);
5506
5507                        self.set_input_text_with_cursor(new_value, cursor_pos + text.len());
5508
5509                        self.state_container
5510                            .set_status_message(format!("Pasted {} characters", text.len()));
5511                    }
5512                    AppMode::Filter
5513                    | AppMode::FuzzyFilter
5514                    | AppMode::Search
5515                    | AppMode::ColumnSearch => {
5516                        // For search/filter modes, append to current pattern
5517                        let cursor_pos = self.get_input_cursor();
5518                        let current_value = self.get_input_text();
5519
5520                        let mut new_value = String::new();
5521                        new_value.push_str(&current_value[..cursor_pos]);
5522                        new_value.push_str(&text);
5523                        new_value.push_str(&current_value[cursor_pos..]);
5524
5525                        self.set_input_text_with_cursor(new_value, cursor_pos + text.len());
5526
5527                        // Update the appropriate filter/search state (reuse the mode we already have)
5528                        match mode {
5529                            AppMode::Filter => {
5530                                let pattern = self.get_input_text();
5531                                self.state_container.filter_mut().pattern = pattern.clone();
5532                                self.apply_filter(&pattern);
5533                            }
5534                            AppMode::FuzzyFilter => {
5535                                let input_text = self.get_input_text();
5536                                self.state_container.set_fuzzy_filter_pattern(input_text);
5537                                self.apply_fuzzy_filter();
5538                            }
5539                            AppMode::Search => {
5540                                let search_text = self.get_input_text();
5541                                self.state_container.set_search_pattern(search_text);
5542                                // TODO: self.search_in_results();
5543                            }
5544                            AppMode::ColumnSearch => {
5545                                let input_text = self.get_input_text();
5546                                self.state_container.start_column_search(input_text);
5547                                // Column search pattern is now in AppStateContainer
5548                            }
5549                            _ => {}
5550                        }
5551                    }
5552                    _ => {
5553                        self.state_container
5554                            .set_status_message("Paste not available in this mode".to_string());
5555                    }
5556                }
5557            }
5558            Err(e) => {
5559                self.state_container
5560                    .set_status_message(format!("Failed to paste: {e}"));
5561            }
5562        }
5563    }
5564
5565    fn export_to_json(&mut self) {
5566        // TODO: Handle filtered data in future DataView implementation
5567        let result = {
5568            let ctx = crate::ui::operations::data_export_operations::DataExportContext {
5569                data_provider: self.get_data_provider(),
5570            };
5571            crate::ui::operations::data_export_operations::export_to_json(&ctx)
5572        };
5573
5574        match result {
5575            crate::ui::operations::data_export_operations::ExportResult::Success(message) => {
5576                self.set_status_message(message);
5577            }
5578            crate::ui::operations::data_export_operations::ExportResult::Error(error) => {
5579                self.set_error_status("Export failed", error);
5580            }
5581        }
5582    }
5583
5584    fn get_horizontal_scroll_offset(&self) -> u16 {
5585        // Delegate to cursor_manager (incremental refactoring)
5586        let (horizontal, _vertical) = self.cursor_manager.scroll_offsets();
5587        horizontal
5588    }
5589
5590    fn update_horizontal_scroll(&mut self, terminal_width: u16) {
5591        let inner_width = terminal_width.saturating_sub(3) as usize; // Account for borders + 1 char padding
5592        let cursor_pos = self.get_input_cursor();
5593
5594        // Update cursor_manager scroll (incremental refactoring)
5595        self.cursor_manager
5596            .update_horizontal_scroll(cursor_pos, terminal_width.saturating_sub(3));
5597
5598        // Update scroll state in container
5599        let mut scroll = self.state_container.scroll_mut();
5600        if cursor_pos < scroll.input_scroll_offset as usize {
5601            scroll.input_scroll_offset = cursor_pos as u16;
5602        }
5603        // If cursor is after the scroll window, scroll right
5604        else if cursor_pos >= scroll.input_scroll_offset as usize + inner_width {
5605            scroll.input_scroll_offset = (cursor_pos + 1).saturating_sub(inner_width) as u16;
5606        }
5607    }
5608
5609    fn get_cursor_token_position(&self) -> (usize, usize) {
5610        let ctx = crate::ui::operations::simple_operations::TextNavigationContext {
5611            query: &self.get_input_text(),
5612            cursor_pos: self.get_input_cursor(),
5613        };
5614        crate::ui::operations::simple_operations::get_cursor_token_position(&ctx)
5615    }
5616
5617    fn get_token_at_cursor(&self) -> Option<String> {
5618        let ctx = crate::ui::operations::simple_operations::TextNavigationContext {
5619            query: &self.get_input_text(),
5620            cursor_pos: self.get_input_cursor(),
5621        };
5622        crate::ui::operations::simple_operations::get_token_at_cursor(&ctx)
5623    }
5624
5625    /// Debug method to dump current buffer state (disabled to prevent TUI corruption)
5626    #[allow(dead_code)]
5627    fn ui(&mut self, f: &mut Frame) {
5628        // Always use single-line mode input height
5629        let input_height = INPUT_AREA_HEIGHT;
5630
5631        // Always show tab bar for consistent layout
5632        let buffer_count = self.state_container.buffers().all_buffers().len();
5633        let tab_bar_height = 2; // Always reserve space for tab bar
5634
5635        let chunks = Layout::default()
5636            .direction(Direction::Vertical)
5637            .constraints(
5638                [
5639                    Constraint::Length(tab_bar_height),    // Tab bar (always shown)
5640                    Constraint::Length(input_height),      // Command input area
5641                    Constraint::Min(0),                    // Results
5642                    Constraint::Length(STATUS_BAR_HEIGHT), // Status bar
5643                ]
5644                .as_ref(),
5645            )
5646            .split(f.area());
5647
5648        // Always render tab bar (even with single buffer)
5649        if buffer_count > 0 {
5650            let buffer_names: Vec<String> = self
5651                .state_container
5652                .buffers()
5653                .all_buffers()
5654                .iter()
5655                .map(super::super::buffer::BufferAPI::get_name)
5656                .collect();
5657            let current_index = self.state_container.buffers().current_index();
5658
5659            let tab_widget = TabBarWidget::new(current_index, buffer_names);
5660            tab_widget.render(f, chunks[0]);
5661        }
5662
5663        // Fixed chunk indices since tab bar is always present
5664        let input_chunk_idx = 1;
5665        let results_chunk_idx = 2;
5666        let status_chunk_idx = 3;
5667
5668        // Update horizontal scroll based on actual terminal width
5669        self.update_horizontal_scroll(chunks[input_chunk_idx].width);
5670
5671        // Command input area
5672        // Get the current input text length and cursor position for display
5673        let input_text_for_count = self.get_input_text();
5674        let char_count = input_text_for_count.len();
5675        let cursor_pos = self.get_input_cursor();
5676        let char_count_display = if char_count > 0 {
5677            format!(" [{cursor_pos}/{char_count} chars]")
5678        } else {
5679            String::new()
5680        };
5681
5682        let scroll_offset = self.get_horizontal_scroll_offset();
5683        let scroll_indicator = if scroll_offset > 0 {
5684            " ◀ " // Indicate text is scrolled (text hidden to the left)
5685        } else {
5686            ""
5687        };
5688
5689        let input_title = match self.shadow_state.borrow().get_mode() {
5690            AppMode::Command => format!("SQL Query{char_count_display}{scroll_indicator}"),
5691            AppMode::Results => format!(
5692                "SQL Query (Results Mode - Press ↑ to edit){char_count_display}{scroll_indicator}"
5693            ),
5694            AppMode::Search => format!("Search Pattern{char_count_display}{scroll_indicator}"),
5695            AppMode::Filter => format!("Filter Pattern{char_count_display}{scroll_indicator}"),
5696            AppMode::FuzzyFilter => {
5697                format!("Fuzzy Filter{char_count_display}{scroll_indicator}")
5698            }
5699            AppMode::ColumnSearch => {
5700                format!("Column Search{char_count_display}{scroll_indicator}")
5701            }
5702            AppMode::Help => "Help".to_string(),
5703            AppMode::History => {
5704                let query = self.state_container.history_search().query.clone();
5705                format!("History Search: '{query}' (Esc to cancel)")
5706            }
5707            AppMode::Debug => "Parser Debug (F5)".to_string(),
5708            AppMode::PrettyQuery => "Pretty Query View (F6)".to_string(),
5709            AppMode::JumpToRow => format!("Jump to row: {}", self.get_jump_to_row_input()),
5710            AppMode::ColumnStats => "Column Statistics (S to close)".to_string(),
5711        };
5712
5713        let input_block = Block::default().borders(Borders::ALL).title(input_title);
5714
5715        // Check if we should use the search modes widget for rendering
5716        let use_search_widget = matches!(
5717            self.shadow_state.borrow().get_mode(),
5718            AppMode::Search | AppMode::Filter | AppMode::FuzzyFilter | AppMode::ColumnSearch
5719        ) && self.search_modes_widget.is_active();
5720
5721        if use_search_widget {
5722            // Let the search modes widget render the input field with debounce indicator
5723            self.search_modes_widget.render(f, chunks[input_chunk_idx]);
5724        } else {
5725            // Always get input text through the buffer API for consistency
5726            let input_text_string = self.get_input_text();
5727
5728            // Debug log to track rendering issues
5729            trace!(target: "render", "Rendering input: text='{}', mode={:?}, cursor={}",
5730                   if input_text_string.len() > 50 {
5731                       format!("{}...", &input_text_string[..50])
5732                   } else {
5733                       input_text_string.clone()
5734                   },
5735                   self.shadow_state.borrow().get_mode(),
5736                   self.get_input_cursor());
5737
5738            // Get history search query if in history mode
5739            let history_query_string = if self.shadow_state.borrow().is_in_history_mode() {
5740                self.state_container.history_search().query.clone()
5741            } else {
5742                String::new()
5743            };
5744
5745            let input_text = match self.shadow_state.borrow().get_mode() {
5746                AppMode::History => &history_query_string,
5747                _ => &input_text_string,
5748            };
5749
5750            let input_paragraph = match self.shadow_state.borrow().get_mode() {
5751                AppMode::Command => {
5752                    match self.state_container.get_edit_mode() {
5753                        Some(EditMode::SingleLine) => {
5754                            // Use syntax highlighting for SQL command input with horizontal scrolling
5755                            let highlighted_line =
5756                                self.sql_highlighter.simple_sql_highlight(input_text);
5757                            Paragraph::new(Text::from(vec![highlighted_line]))
5758                                .block(input_block)
5759                                .scroll((0, self.get_horizontal_scroll_offset()))
5760                        }
5761                        Some(EditMode::MultiLine) => {
5762                            // MultiLine mode is no longer supported, always use single-line
5763                            let highlighted_line =
5764                                self.sql_highlighter.simple_sql_highlight(input_text);
5765                            Paragraph::new(Text::from(vec![highlighted_line]))
5766                                .block(input_block)
5767                                .scroll((0, self.get_horizontal_scroll_offset()))
5768                        }
5769                        None => {
5770                            // Default to single-line mode
5771                            let highlighted_line =
5772                                self.sql_highlighter.simple_sql_highlight(input_text);
5773                            Paragraph::new(Text::from(vec![highlighted_line]))
5774                                .block(input_block)
5775                                .scroll((0, self.get_horizontal_scroll_offset()))
5776                        }
5777                    }
5778                }
5779                _ => {
5780                    // Plain text for other modes
5781                    Paragraph::new(input_text.as_str())
5782                        .block(input_block)
5783                        .style(match self.shadow_state.borrow().get_mode() {
5784                            AppMode::Results => Style::default().fg(Color::DarkGray),
5785                            AppMode::Search => Style::default().fg(Color::Yellow),
5786                            AppMode::Filter => Style::default().fg(Color::Cyan),
5787                            AppMode::FuzzyFilter => Style::default().fg(Color::Magenta),
5788                            AppMode::ColumnSearch => Style::default().fg(Color::Green),
5789                            AppMode::Help => Style::default().fg(Color::DarkGray),
5790                            AppMode::History => Style::default().fg(Color::Magenta),
5791                            AppMode::Debug => Style::default().fg(Color::Yellow),
5792                            AppMode::PrettyQuery => Style::default().fg(Color::Green),
5793                            AppMode::JumpToRow => Style::default().fg(Color::Magenta),
5794                            AppMode::ColumnStats => Style::default().fg(Color::Cyan),
5795                            _ => Style::default(),
5796                        })
5797                        .scroll((0, self.get_horizontal_scroll_offset()))
5798                }
5799            };
5800
5801            // Render the input paragraph (single-line mode)
5802            f.render_widget(input_paragraph, chunks[input_chunk_idx]);
5803        }
5804        let results_area = chunks[results_chunk_idx];
5805
5806        // Set cursor position for input modes (skip if search widget is handling it)
5807        if !use_search_widget {
5808            match self.shadow_state.borrow().get_mode() {
5809                AppMode::Command => {
5810                    // Always use single-line cursor handling
5811                    // Calculate cursor position with horizontal scrolling
5812                    let inner_width = chunks[input_chunk_idx].width.saturating_sub(2) as usize;
5813                    let cursor_pos = self.get_visual_cursor().1; // Get column position for single-line
5814                    let scroll_offset = self.get_horizontal_scroll_offset() as usize;
5815
5816                    // Calculate visible cursor position
5817                    if cursor_pos >= scroll_offset && cursor_pos < scroll_offset + inner_width {
5818                        let visible_pos = cursor_pos - scroll_offset;
5819                        f.set_cursor_position((
5820                            chunks[input_chunk_idx].x + visible_pos as u16 + 1,
5821                            chunks[input_chunk_idx].y + 1,
5822                        ));
5823                    }
5824                }
5825                AppMode::Search => {
5826                    f.set_cursor_position((
5827                        chunks[input_chunk_idx].x + self.get_input_cursor() as u16 + 1,
5828                        chunks[input_chunk_idx].y + 1,
5829                    ));
5830                }
5831                AppMode::Filter => {
5832                    f.set_cursor_position((
5833                        chunks[input_chunk_idx].x + self.get_input_cursor() as u16 + 1,
5834                        chunks[input_chunk_idx].y + 1,
5835                    ));
5836                }
5837                AppMode::FuzzyFilter => {
5838                    f.set_cursor_position((
5839                        chunks[input_chunk_idx].x + self.get_input_cursor() as u16 + 1,
5840                        chunks[input_chunk_idx].y + 1,
5841                    ));
5842                }
5843                AppMode::ColumnSearch => {
5844                    f.set_cursor_position((
5845                        chunks[input_chunk_idx].x + self.get_input_cursor() as u16 + 1,
5846                        chunks[input_chunk_idx].y + 1,
5847                    ));
5848                }
5849                AppMode::JumpToRow => {
5850                    f.set_cursor_position((
5851                        chunks[input_chunk_idx].x + self.get_jump_to_row_input().len() as u16 + 1,
5852                        chunks[input_chunk_idx].y + 1,
5853                    ));
5854                }
5855                AppMode::History => {
5856                    let query_len = self.state_container.history_search().query.len();
5857                    f.set_cursor_position((
5858                        chunks[input_chunk_idx].x + query_len as u16 + 1,
5859                        chunks[input_chunk_idx].y + 1,
5860                    ));
5861                }
5862                _ => {}
5863            }
5864        }
5865
5866        // Results area - render based on mode to reduce complexity
5867        let mode = self.shadow_state.borrow().get_mode();
5868        match mode {
5869            AppMode::Help => self.render_help(f, results_area),
5870            AppMode::History => self.render_history(f, results_area),
5871            AppMode::Debug => self.render_debug(f, results_area),
5872            AppMode::PrettyQuery => self.render_pretty_query(f, results_area),
5873            AppMode::ColumnStats => self.render_column_stats(f, results_area),
5874            _ if self.state_container.has_dataview() => {
5875                // Calculate viewport using DataView
5876                // V50: Render using DataProvider which works with DataTable
5877                if let Some(provider) = self.get_data_provider() {
5878                    self.render_table_with_provider(f, results_area, provider.as_ref());
5879                }
5880            }
5881            _ => {
5882                // Simple placeholder - reduced text to improve rendering speed
5883                let placeholder = Paragraph::new("Enter SQL query and press Enter\n\nTip: Use Tab for completion, Ctrl+R for history")
5884                    .block(Block::default().borders(Borders::ALL).title("Results"))
5885                    .style(Style::default().fg(Color::DarkGray));
5886                f.render_widget(placeholder, results_area);
5887            }
5888        }
5889
5890        // Render mode-specific status line
5891        self.render_status_line(f, chunks[status_chunk_idx]);
5892        // ========== RENDERING ==========
5893    }
5894
5895    /// Add mode styling and indicator to status spans
5896    fn add_mode_styling(&self, spans: &mut Vec<Span>) -> (Style, Color) {
5897        // Determine the mode color
5898        let (status_style, mode_color) = match self.shadow_state.borrow().get_mode() {
5899            AppMode::Command => (Style::default().fg(Color::Green), Color::Green),
5900            AppMode::Results => (Style::default().fg(Color::Blue), Color::Blue),
5901            AppMode::Search => (Style::default().fg(Color::Yellow), Color::Yellow),
5902            AppMode::Filter => (Style::default().fg(Color::Cyan), Color::Cyan),
5903            AppMode::FuzzyFilter => (Style::default().fg(Color::Magenta), Color::Magenta),
5904            AppMode::ColumnSearch => (Style::default().fg(Color::Green), Color::Green),
5905            AppMode::Help => (Style::default().fg(Color::Magenta), Color::Magenta),
5906            AppMode::History => (Style::default().fg(Color::Magenta), Color::Magenta),
5907            AppMode::Debug => (Style::default().fg(Color::Yellow), Color::Yellow),
5908            AppMode::PrettyQuery => (Style::default().fg(Color::Green), Color::Green),
5909            AppMode::JumpToRow => (Style::default().fg(Color::Magenta), Color::Magenta),
5910            AppMode::ColumnStats => (Style::default().fg(Color::Cyan), Color::Cyan),
5911        };
5912
5913        let mode_indicator = match self.shadow_state.borrow().get_mode() {
5914            AppMode::Command => "CMD",
5915            AppMode::Results => "NAV",
5916            AppMode::Search => "SEARCH",
5917            AppMode::Filter => "FILTER",
5918            AppMode::FuzzyFilter => "FUZZY",
5919            AppMode::ColumnSearch => "COL",
5920            AppMode::Help => "HELP",
5921            AppMode::History => "HISTORY",
5922            AppMode::Debug => "DEBUG",
5923            AppMode::PrettyQuery => "PRETTY",
5924            AppMode::JumpToRow => "JUMP",
5925            AppMode::ColumnStats => "STATS",
5926        };
5927
5928        // Mode indicator with color
5929        spans.push(Span::styled(
5930            format!("[{mode_indicator}]"),
5931            Style::default().fg(mode_color).add_modifier(Modifier::BOLD),
5932        ));
5933
5934        (status_style, mode_color)
5935    }
5936
5937    /// Add data source display to status spans
5938    fn add_data_source_display(&self, _spans: &mut Vec<Span>) {
5939        // Skip showing data source in status line since tab bar shows file names
5940        // This avoids redundancy like "[trades.csv] [1/2] trades.csv"
5941    }
5942
5943    /// Add buffer information to status spans
5944    fn add_buffer_information(&self, spans: &mut Vec<Span>) {
5945        let index = self.state_container.buffers().current_index();
5946        let total = self.state_container.buffers().all_buffers().len();
5947
5948        // Show buffer indicator if multiple buffers
5949        if total > 1 {
5950            spans.push(Span::raw(" "));
5951            spans.push(Span::styled(
5952                format!("[{}/{}]", index + 1, total),
5953                Style::default().fg(Color::Yellow),
5954            ));
5955        }
5956
5957        // Show table name from current query (simplified)
5958        // Since tab bar shows file names, we just show the table being queried
5959        if let Some(buffer) = self.state_container.buffers().current() {
5960            let query = buffer.get_input_text();
5961            // Simple extraction of table name from "SELECT ... FROM table" pattern
5962            if let Some(from_pos) = query.to_uppercase().find(" FROM ") {
5963                let after_from = &query[from_pos + 6..];
5964                // Take the first word after FROM as the table name
5965                if let Some(table_name) = after_from.split_whitespace().next() {
5966                    // Clean up the table name (remove quotes, etc.)
5967                    let clean_name = table_name
5968                        .trim_matches('"')
5969                        .trim_matches('\'')
5970                        .trim_matches('`');
5971
5972                    spans.push(Span::raw(" "));
5973                    spans.push(Span::styled(
5974                        clean_name.to_string(),
5975                        Style::default().fg(Color::Cyan),
5976                    ));
5977                }
5978            }
5979        }
5980    }
5981
5982    /// Add mode-specific information to status spans
5983    fn add_mode_specific_info(&self, spans: &mut Vec<Span>, mode_color: Color, area: Rect) {
5984        match self.shadow_state.borrow().get_mode() {
5985            AppMode::Command => {
5986                // In command mode, show editing-related info
5987                if !self.get_input_text().trim().is_empty() {
5988                    let (token_pos, total_tokens) = self.get_cursor_token_position();
5989                    spans.push(Span::raw(" | "));
5990                    spans.push(Span::styled(
5991                        format!("Token {token_pos}/{total_tokens}"),
5992                        Style::default().fg(Color::DarkGray),
5993                    ));
5994
5995                    // Show current token if available
5996                    if let Some(token) = self.get_token_at_cursor() {
5997                        spans.push(Span::raw(" "));
5998                        spans.push(Span::styled(
5999                            format!("[{token}]"),
6000                            Style::default().fg(Color::Cyan),
6001                        ));
6002                    }
6003
6004                    // Check for parser errors
6005                    if let Some(error_msg) = self.check_parser_error(&self.get_input_text()) {
6006                        spans.push(Span::raw(" | "));
6007                        spans.push(Span::styled(
6008                            format!("{} {}", self.config.display.icons.warning, error_msg),
6009                            Style::default().fg(Color::Red).add_modifier(Modifier::BOLD),
6010                        ));
6011                    }
6012                }
6013            }
6014            AppMode::Results => {
6015                // Extract this separately due to its size
6016                self.add_results_mode_info(spans, area);
6017            }
6018            AppMode::Search | AppMode::Filter | AppMode::FuzzyFilter | AppMode::ColumnSearch => {
6019                // Show the pattern being typed - always use input for consistency
6020                let pattern = self.get_input_text();
6021                if !pattern.is_empty() {
6022                    spans.push(Span::raw(" | Pattern: "));
6023                    spans.push(Span::styled(pattern, Style::default().fg(mode_color)));
6024                }
6025            }
6026            _ => {}
6027        }
6028    }
6029
6030    /// Add Results mode specific information (restored critical navigation info)
6031    fn add_results_mode_info(&self, spans: &mut Vec<Span>, area: Rect) {
6032        let total_rows = self.get_row_count();
6033        if total_rows > 0 {
6034            // Get selected row directly from navigation state (0-indexed) and add 1 for display
6035            let selected = self.state_container.navigation().selected_row + 1;
6036            spans.push(Span::raw(" | "));
6037
6038            // Show selection mode
6039            let selection_mode = self.get_selection_mode();
6040            let mode_text = match selection_mode {
6041                SelectionMode::Cell => "CELL",
6042                SelectionMode::Row => "ROW",
6043                SelectionMode::Column => "COL",
6044            };
6045            spans.push(Span::styled(
6046                format!("[{mode_text}]"),
6047                Style::default()
6048                    .fg(Color::Cyan)
6049                    .add_modifier(Modifier::BOLD),
6050            ));
6051
6052            spans.push(Span::raw(" "));
6053            spans.push(Span::styled(
6054                format!("Row {selected}/{total_rows}"),
6055                Style::default().fg(Color::White),
6056            ));
6057
6058            // Add cursor coordinates (x,y) - column and row position
6059            // Use ViewportManager's visual column position (1-based for display)
6060            let visual_col_display =
6061                if let Some(ref viewport_manager) = *self.viewport_manager.borrow() {
6062                    viewport_manager.get_crosshair_col() + 1
6063                } else {
6064                    1
6065                };
6066            spans.push(Span::raw(" "));
6067            spans.push(Span::styled(
6068                format!("({visual_col_display},{selected})"),
6069                Style::default().fg(Color::DarkGray),
6070            ));
6071
6072            // Add actual terminal cursor position if we can calculate it
6073            if let Some(ref mut viewport_manager) = *self.viewport_manager.borrow_mut() {
6074                let available_width = area.width.saturating_sub(TABLE_BORDER_WIDTH);
6075                // Use ViewportManager's crosshair column position
6076                let visual_col = viewport_manager.get_crosshair_col();
6077                if let Some(x_pos) =
6078                    viewport_manager.get_column_x_position(visual_col, available_width)
6079                {
6080                    // Add 2 for left border and padding, add 3 for header rows
6081                    let terminal_x = x_pos + 2;
6082                    let terminal_y = (selected as u16)
6083                        .saturating_sub(self.state_container.get_scroll_offset().0 as u16)
6084                        + 3;
6085                    spans.push(Span::raw(" "));
6086                    spans.push(Span::styled(
6087                        format!("[{terminal_x}x{terminal_y}]"),
6088                        Style::default().fg(Color::DarkGray),
6089                    ));
6090                }
6091            }
6092
6093            // Column information
6094            if let Some(dataview) = self.state_container.get_buffer_dataview() {
6095                let headers = dataview.column_names();
6096
6097                // Get ViewportManager's crosshair position (visual coordinates)
6098                // and use it to get the correct column name
6099                let (visual_row, visual_col) =
6100                    if let Some(ref viewport_manager) = *self.viewport_manager.borrow() {
6101                        (
6102                            viewport_manager.get_crosshair_row(),
6103                            viewport_manager.get_crosshair_col(),
6104                        )
6105                    } else {
6106                        (0, 0)
6107                    };
6108
6109                // Use ViewportManager's visual column index to get the correct column name
6110                if visual_col < headers.len() {
6111                    spans.push(Span::raw(" | Col: "));
6112                    spans.push(Span::styled(
6113                        headers[visual_col].clone(),
6114                        Style::default().fg(Color::Cyan),
6115                    ));
6116
6117                    // Show ViewportManager's crosshair position and viewport size
6118                    let viewport_info =
6119                        if let Some(ref viewport_manager) = *self.viewport_manager.borrow() {
6120                            let viewport_rows = viewport_manager.get_viewport_rows();
6121                            let viewport_height = viewport_rows.end - viewport_rows.start;
6122                            format!("[V:{visual_row},{visual_col} @ {viewport_height}r]")
6123                        } else {
6124                            format!("[V:{visual_row},{visual_col}]")
6125                        };
6126                    spans.push(Span::raw(" "));
6127                    spans.push(Span::styled(
6128                        viewport_info,
6129                        Style::default().fg(Color::Magenta),
6130                    ));
6131                }
6132            }
6133        }
6134    }
6135
6136    fn render_status_line(&self, f: &mut Frame, area: Rect) {
6137        let mut spans = Vec::new();
6138
6139        // Add mode styling and indicator
6140        let (status_style, mode_color) = self.add_mode_styling(&mut spans);
6141
6142        // Add data source display
6143        self.add_data_source_display(&mut spans);
6144
6145        // Add buffer information
6146        self.add_buffer_information(&mut spans);
6147
6148        // Add mode-specific information
6149        self.add_mode_specific_info(&mut spans, mode_color, area);
6150
6151        // Add query source indicator
6152        self.add_query_source_indicator(&mut spans);
6153
6154        // Add case sensitivity indicator
6155        self.add_case_sensitivity_indicator(&mut spans);
6156
6157        // Add column packing mode indicator
6158        self.add_column_packing_indicator(&mut spans);
6159
6160        // Add status message
6161        self.add_status_message(&mut spans);
6162
6163        // Determine help text based on current mode
6164        let help_text = self.get_help_text_for_mode();
6165
6166        self.add_global_indicators(&mut spans);
6167
6168        // Add shadow state display
6169        self.add_shadow_state_display(&mut spans);
6170
6171        self.add_help_text_display(&mut spans, help_text, area);
6172
6173        let status_line = Line::from(spans);
6174        let status = Paragraph::new(status_line)
6175            .block(Block::default().borders(Borders::ALL))
6176            .style(status_style);
6177        f.render_widget(status, area);
6178    }
6179
6180    /// Build a `TableRenderContext` with all data needed for rendering
6181    /// This collects all the scattered data into a single struct
6182    fn build_table_context(
6183        &self,
6184        area: Rect,
6185        provider: &dyn DataProvider,
6186    ) -> crate::ui::rendering::table_render_context::TableRenderContext {
6187        use crate::ui::rendering::table_render_context::TableRenderContextBuilder;
6188
6189        let row_count = provider.get_row_count();
6190        let available_width = area.width.saturating_sub(TABLE_BORDER_WIDTH);
6191        // The area passed here is already the table area
6192        // The Table widget itself handles borders and header
6193        let available_height = area.height;
6194
6195        // Get headers from ViewportManager (single source of truth)
6196        let headers = {
6197            let viewport_manager = self.viewport_manager.borrow();
6198            let viewport_manager = viewport_manager
6199                .as_ref()
6200                .expect("ViewportManager must exist");
6201            viewport_manager.get_column_names_ordered()
6202        };
6203
6204        // Update ViewportManager with current terminal dimensions
6205        {
6206            let mut viewport_opt = self.viewport_manager.borrow_mut();
6207            if let Some(ref mut viewport_manager) = *viewport_opt {
6208                // Calculate data rows for the table area
6209                let data_rows = Self::calculate_table_data_rows(available_height);
6210                viewport_manager.update_terminal_size(available_width, data_rows);
6211                let _ = viewport_manager.get_column_widths(); // Trigger recalculation
6212            }
6213        }
6214
6215        // Get structured column information from ViewportManager
6216        let (pinned_visual_positions, crosshair_column_position, _) = {
6217            let mut viewport_manager_borrow = self.viewport_manager.borrow_mut();
6218            let viewport_manager = viewport_manager_borrow
6219                .as_mut()
6220                .expect("ViewportManager must exist for rendering");
6221            let info = viewport_manager.get_visible_columns_info(available_width);
6222
6223            // info.0 = visible_indices (all visible column source indices)
6224            // info.1 = pinned_visible (pinned column source indices)
6225            // info.2 = scrollable_visible (scrollable column source indices)
6226            let visible_indices = &info.0;
6227            let pinned_source_indices = &info.1;
6228
6229            // Convert pinned source indices to visual positions
6230            // The TableRenderContext expects visual positions (0-based positions in the visible array)
6231            let mut pinned_visual_positions = Vec::new();
6232            for &source_idx in pinned_source_indices {
6233                if let Some(visual_pos) = visible_indices.iter().position(|&x| x == source_idx) {
6234                    pinned_visual_positions.push(visual_pos);
6235                }
6236            }
6237
6238            // Get the crosshair's viewport-relative position for rendering
6239            // The viewport manager stores crosshair in absolute coordinates
6240            // but we need viewport-relative for rendering
6241            let crosshair_column_position =
6242                if let Some((_, col_pos)) = viewport_manager.get_crosshair_viewport_position() {
6243                    col_pos
6244                } else {
6245                    // Crosshair is outside viewport, default to 0
6246                    0
6247                };
6248
6249            let crosshair_visual = viewport_manager.get_crosshair_col();
6250
6251            (
6252                pinned_visual_positions,
6253                crosshair_column_position,
6254                crosshair_visual,
6255            )
6256        };
6257
6258        // Calculate row viewport
6259        let row_viewport_start = self
6260            .state_container
6261            .navigation()
6262            .scroll_offset
6263            .0
6264            .min(row_count.saturating_sub(1));
6265        let row_viewport_end = (row_viewport_start + available_height as usize).min(row_count);
6266        let visible_row_indices: Vec<usize> = (row_viewport_start..row_viewport_end).collect();
6267
6268        // Get the visual display data from ViewportManager
6269        let (column_headers, data_to_display, column_widths_visual) = {
6270            let mut viewport_manager_borrow = self.viewport_manager.borrow_mut();
6271            if let Some(ref mut viewport_manager) = *viewport_manager_borrow {
6272                viewport_manager.get_visual_display(available_width, &visible_row_indices)
6273            } else {
6274                // Fallback
6275                let visible_rows = provider
6276                    .get_visible_rows(row_viewport_start, row_viewport_end - row_viewport_start);
6277                let widths = vec![15u16; headers.len()];
6278                (headers.clone(), visible_rows, widths)
6279            }
6280        };
6281
6282        // Get sort state
6283        let sort_state = self
6284            .buffer()
6285            .get_dataview()
6286            .map(|dv| dv.get_sort_state().clone());
6287
6288        // Get filter info
6289        let fuzzy_filter_pattern = if self.state_container.is_fuzzy_filter_active() {
6290            let pattern = self.state_container.get_fuzzy_filter_pattern();
6291            if pattern.is_empty() {
6292                None
6293            } else {
6294                Some(pattern)
6295            }
6296        } else {
6297            None
6298        };
6299
6300        // Build the context
6301        let selected_row = self.state_container.navigation().selected_row;
6302        let selected_col = crosshair_column_position;
6303
6304        // Log what we're passing to the renderer
6305        trace!(target: "search", "Building TableRenderContext: selected_row={}, selected_col={}, mode={:?}",
6306               selected_row, selected_col, self.state_container.get_selection_mode());
6307
6308        TableRenderContextBuilder::new()
6309            .row_count(row_count)
6310            .visible_rows(visible_row_indices.clone(), data_to_display)
6311            .columns(column_headers, column_widths_visual)
6312            .pinned_columns(pinned_visual_positions)
6313            .selection(
6314                selected_row,
6315                selected_col,
6316                self.state_container.get_selection_mode(),
6317            )
6318            .row_viewport(row_viewport_start..row_viewport_end)
6319            .sort_state(sort_state)
6320            .display_options(
6321                self.state_container.is_show_row_numbers(),
6322                self.shadow_state.borrow().get_mode(),
6323            )
6324            .filter(
6325                fuzzy_filter_pattern,
6326                self.state_container.is_case_insensitive(),
6327            )
6328            .dimensions(available_width, available_height)
6329            .build()
6330    }
6331
6332    /// New trait-based table rendering method
6333    /// This uses `DataProvider` trait instead of directly accessing `QueryResponse`
6334    fn render_table_with_provider(&self, f: &mut Frame, area: Rect, provider: &dyn DataProvider) {
6335        // Build the context with all data needed for rendering
6336        let context = self.build_table_context(area, provider);
6337
6338        // Use the pure table renderer
6339        crate::ui::rendering::table_renderer::render_table(f, area, &context);
6340    }
6341
6342    fn render_help(&mut self, f: &mut Frame, area: Rect) {
6343        // Delegate to the help widget which handles tabs, scrolling, and sections
6344        self.help_widget.render(f, area);
6345    }
6346
6347    fn render_help_two_column(&self, f: &mut Frame, area: Rect) {
6348        // Create two-column layout
6349        let chunks = Layout::default()
6350            .direction(Direction::Horizontal)
6351            .constraints([Constraint::Percentage(50), Constraint::Percentage(50)])
6352            .split(area);
6353
6354        // Get help content from HelpText module
6355        let left_content = HelpText::left_column();
6356        let right_content = HelpText::right_column();
6357
6358        // Calculate visible area for scrolling
6359        let visible_height = area.height.saturating_sub(2) as usize; // Account for borders
6360        let left_total_lines = left_content.len();
6361        let right_total_lines = right_content.len();
6362        let max_lines = left_total_lines.max(right_total_lines);
6363
6364        // Apply scroll offset (from state container or local)
6365        let scroll_offset = { self.state_container.help_scroll_offset() as usize };
6366
6367        // Get visible portions with scrolling
6368        let left_visible: Vec<Line> = left_content
6369            .into_iter()
6370            .skip(scroll_offset)
6371            .take(visible_height)
6372            .collect();
6373
6374        let right_visible: Vec<Line> = right_content
6375            .into_iter()
6376            .skip(scroll_offset)
6377            .take(visible_height)
6378            .collect();
6379
6380        // Create scroll indicator in title
6381        let scroll_indicator = if max_lines > visible_height {
6382            format!(
6383                " (↓/↑ to scroll, {}/{})",
6384                scroll_offset + 1,
6385                max_lines.saturating_sub(visible_height) + 1
6386            )
6387        } else {
6388            String::new()
6389        };
6390
6391        // Render left column
6392        let left_paragraph = Paragraph::new(Text::from(left_visible))
6393            .block(
6394                Block::default()
6395                    .borders(Borders::ALL)
6396                    .title(format!("Help - Commands{scroll_indicator}")),
6397            )
6398            .style(Style::default());
6399
6400        // Render right column
6401        let right_paragraph = Paragraph::new(Text::from(right_visible))
6402            .block(
6403                Block::default()
6404                    .borders(Borders::ALL)
6405                    .title("Help - Navigation & Features"),
6406            )
6407            .style(Style::default());
6408
6409        f.render_widget(left_paragraph, chunks[0]);
6410        f.render_widget(right_paragraph, chunks[1]);
6411    }
6412
6413    fn render_debug(&self, f: &mut Frame, area: Rect) {
6414        <Self as DebugContext>::render_debug(self, f, area);
6415    }
6416
6417    fn render_pretty_query(&self, f: &mut Frame, area: Rect) {
6418        <Self as DebugContext>::render_pretty_query(self, f, area);
6419    }
6420
6421    fn render_history(&self, f: &mut Frame, area: Rect) {
6422        // Get history state from AppStateContainer
6423        let history_search = self.state_container.history_search();
6424        let matches_empty = history_search.matches.is_empty();
6425        let search_query_empty = history_search.query.is_empty();
6426
6427        if matches_empty {
6428            let no_history = if search_query_empty {
6429                "No command history found.\nExecute some queries to build history."
6430            } else {
6431                "No matches found for your search.\nTry a different search term."
6432            };
6433
6434            let placeholder = Paragraph::new(no_history)
6435                .block(
6436                    Block::default()
6437                        .borders(Borders::ALL)
6438                        .title("Command History"),
6439                )
6440                .style(Style::default().fg(Color::DarkGray));
6441            f.render_widget(placeholder, area);
6442            return;
6443        }
6444
6445        // Split the area to show selected command details
6446        let chunks = Layout::default()
6447            .direction(Direction::Vertical)
6448            .constraints([
6449                Constraint::Percentage(50), // History list - 50% of space
6450                Constraint::Percentage(50), // Selected command preview - 50% of space
6451            ])
6452            .split(area);
6453
6454        self.render_history_list(f, chunks[0]);
6455        self.render_selected_command_preview(f, chunks[1]);
6456    }
6457
6458    fn render_history_list(&self, f: &mut Frame, area: Rect) {
6459        // Get history data from AppStateContainer
6460        let history_search = self.state_container.history_search();
6461        let matches = history_search.matches.clone();
6462        let selected_index = history_search.selected_index;
6463        let match_count = matches.len();
6464
6465        // Create more compact history list - just show essential info
6466        let history_items: Vec<Line> = matches
6467            .iter()
6468            .enumerate()
6469            .map(|(i, history_match)| {
6470                let entry = &history_match.entry;
6471                let is_selected = i == selected_index;
6472
6473                let success_indicator = if entry.success { "✓" } else { "✗" };
6474                let time_ago = {
6475                    let elapsed = chrono::Utc::now() - entry.timestamp;
6476                    if elapsed.num_days() > 0 {
6477                        format!("{}d", elapsed.num_days())
6478                    } else if elapsed.num_hours() > 0 {
6479                        format!("{}h", elapsed.num_hours())
6480                    } else if elapsed.num_minutes() > 0 {
6481                        format!("{}m", elapsed.num_minutes())
6482                    } else {
6483                        "now".to_string()
6484                    }
6485                };
6486
6487                // Use more space for the command, less for metadata
6488                let terminal_width = area.width as usize;
6489                let metadata_space = 15; // Reduced metadata: " ✓ 2x 1h"
6490                let available_for_command = terminal_width.saturating_sub(metadata_space).max(50);
6491
6492                let command_text = if entry.command.len() > available_for_command {
6493                    format!(
6494                        "{}…",
6495                        &entry.command[..available_for_command.saturating_sub(1)]
6496                    )
6497                } else {
6498                    entry.command.clone()
6499                };
6500
6501                let line_text = format!(
6502                    "{} {} {} {}x {}",
6503                    if is_selected { "►" } else { " " },
6504                    command_text,
6505                    success_indicator,
6506                    entry.execution_count,
6507                    time_ago
6508                );
6509
6510                let mut style = Style::default();
6511                if is_selected {
6512                    style = style.bg(Color::DarkGray).add_modifier(Modifier::BOLD);
6513                }
6514                if !entry.success {
6515                    style = style.fg(Color::Red);
6516                }
6517
6518                // Highlight matching characters for fuzzy search
6519                if !history_match.indices.is_empty() && is_selected {
6520                    style = style.fg(Color::Yellow);
6521                }
6522
6523                Line::from(line_text).style(style)
6524            })
6525            .collect();
6526
6527        let history_paragraph = Paragraph::new(history_items)
6528            .block(Block::default().borders(Borders::ALL).title(format!(
6529                "History ({match_count} matches) - j/k to navigate, Enter to select"
6530            )))
6531            .wrap(ratatui::widgets::Wrap { trim: false });
6532
6533        f.render_widget(history_paragraph, area);
6534    }
6535
6536    fn render_selected_command_preview(&self, f: &mut Frame, area: Rect) {
6537        // Get the selected match from AppStateContainer
6538        let history_search = self.state_container.history_search();
6539        let selected_match = history_search
6540            .matches
6541            .get(history_search.selected_index)
6542            .cloned();
6543
6544        if let Some(selected_match) = selected_match {
6545            let entry = &selected_match.entry;
6546
6547            // Pretty format the SQL command - adjust compactness based on available space
6548            use crate::recursive_parser::format_sql_pretty_compact;
6549
6550            // Calculate how many columns we can fit per line
6551            let available_width = area.width.saturating_sub(6) as usize; // Account for indentation and borders
6552            let avg_col_width = 15; // Assume average column name is ~15 chars
6553            let cols_per_line = (available_width / avg_col_width).max(3).min(12); // Between 3-12 columns per line
6554
6555            let mut pretty_lines = format_sql_pretty_compact(&entry.command, cols_per_line);
6556
6557            // If too many lines for the area, use a more compact format
6558            let max_lines = area.height.saturating_sub(2) as usize; // Account for borders
6559            if pretty_lines.len() > max_lines && cols_per_line < 12 {
6560                // Try with more columns per line
6561                pretty_lines = format_sql_pretty_compact(&entry.command, 15);
6562            }
6563
6564            // Convert to Text with syntax highlighting
6565            let mut highlighted_lines = Vec::new();
6566            for line in pretty_lines {
6567                highlighted_lines.push(self.sql_highlighter.simple_sql_highlight(&line));
6568            }
6569
6570            let preview_text = Text::from(highlighted_lines);
6571
6572            let duration_text = entry
6573                .duration_ms
6574                .map_or_else(|| "?ms".to_string(), |d| format!("{d}ms"));
6575
6576            let success_text = if entry.success {
6577                "✓ Success"
6578            } else {
6579                "✗ Failed"
6580            };
6581
6582            let preview = Paragraph::new(preview_text)
6583                .block(Block::default().borders(Borders::ALL).title(format!(
6584                    "Pretty SQL Preview: {} | {} | Used {}x",
6585                    success_text, duration_text, entry.execution_count
6586                )))
6587                .scroll((0, 0)); // Allow scrolling if needed
6588
6589            f.render_widget(preview, area);
6590        } else {
6591            let empty_preview = Paragraph::new("No command selected")
6592                .block(Block::default().borders(Borders::ALL).title("Preview"))
6593                .style(Style::default().fg(Color::DarkGray));
6594            f.render_widget(empty_preview, area);
6595        }
6596    }
6597
6598    fn handle_column_stats_input(&mut self, key: crossterm::event::KeyEvent) -> Result<bool> {
6599        // Create context and delegate to extracted handler
6600        let mut ctx = crate::ui::input::input_handlers::StatsInputContext {
6601            buffer_manager: self.state_container.buffers_mut(),
6602            stats_widget: &mut self.stats_widget,
6603            shadow_state: &self.shadow_state,
6604        };
6605
6606        let result = crate::ui::input::input_handlers::handle_column_stats_input(&mut ctx, key)?;
6607
6608        // Check if mode changed to Results (happens when stats view is closed)
6609        if self.shadow_state.borrow().get_mode() == AppMode::Results {
6610            self.shadow_state
6611                .borrow_mut()
6612                .observe_mode_change(AppMode::Results, "column_stats_closed");
6613        }
6614
6615        Ok(result)
6616    }
6617
6618    fn handle_jump_to_row_input(&mut self, key: crossterm::event::KeyEvent) -> Result<bool> {
6619        match key.code {
6620            KeyCode::Enter => {
6621                // Use NavigationBehavior trait's complete_jump_to_row method
6622                let input = self.get_jump_to_row_input();
6623                self.complete_jump_to_row(&input);
6624            }
6625            _ => {
6626                // Use InputBehavior trait's process_jump_to_row_key for other keys
6627                self.process_jump_to_row_key(key);
6628            }
6629        }
6630        Ok(false)
6631    }
6632
6633    fn render_column_stats(&self, f: &mut Frame, area: Rect) {
6634        // Delegate to the stats widget
6635        self.stats_widget.render(
6636            f,
6637            area,
6638            self.state_container
6639                .current_buffer()
6640                .expect("Buffer should exist"),
6641        );
6642    }
6643
6644    // === Editor Widget Helper Methods ===
6645    // ========== QUERY EXECUTION ==========
6646
6647    // These methods handle the actions returned by the editor widget
6648
6649    fn handle_execute_query(&mut self) -> Result<bool> {
6650        use crate::ui::state::state_coordinator::StateCoordinator;
6651
6652        let query = self.get_input_text().trim().to_string();
6653        debug!(target: "action", "Executing query: {}", query);
6654
6655        // Use StateCoordinator to handle special commands and state changes
6656        let should_exit = StateCoordinator::handle_execute_query_with_refs(
6657            &mut self.state_container,
6658            &self.shadow_state,
6659            &query,
6660        )?;
6661
6662        if should_exit {
6663            return Ok(true);
6664        }
6665
6666        // If not a special command and not empty, execute the SQL query
6667        if !query.is_empty() && !query.starts_with(':') {
6668            if let Err(e) = self.execute_query_v2(&query) {
6669                self.state_container
6670                    .set_status_message(format!("Error executing query: {e}"));
6671            }
6672            // Don't clear input - preserve query for editing
6673        }
6674
6675        Ok(false) // Continue running, don't exit
6676    }
6677
6678    fn handle_buffer_action(&mut self, action: BufferAction) -> Result<bool> {
6679        match action {
6680            BufferAction::NextBuffer => {
6681                let message = self
6682                    .buffer_handler
6683                    .next_buffer(self.state_container.buffers_mut());
6684                debug!("{}", message);
6685                // Sync all state after buffer switch
6686                self.sync_after_buffer_switch();
6687                Ok(false)
6688            }
6689            BufferAction::PreviousBuffer => {
6690                let message = self
6691                    .buffer_handler
6692                    .previous_buffer(self.state_container.buffers_mut());
6693                debug!("{}", message);
6694                // Sync all state after buffer switch
6695                self.sync_after_buffer_switch();
6696                Ok(false)
6697            }
6698            BufferAction::QuickSwitch => {
6699                let message = self
6700                    .buffer_handler
6701                    .quick_switch(self.state_container.buffers_mut());
6702                debug!("{}", message);
6703                // Sync all state after buffer switch
6704                self.sync_after_buffer_switch();
6705                Ok(false)
6706            }
6707            BufferAction::NewBuffer => {
6708                let message = self
6709                    .buffer_handler
6710                    .new_buffer(self.state_container.buffers_mut(), &self.config);
6711                debug!("{}", message);
6712                Ok(false)
6713            }
6714            BufferAction::CloseBuffer => {
6715                let (success, message) = self
6716                    .buffer_handler
6717                    .close_buffer(self.state_container.buffers_mut());
6718                debug!("{}", message);
6719                Ok(!success) // Exit if we couldn't close (only one left)
6720            }
6721            BufferAction::ListBuffers => {
6722                let buffer_list = self
6723                    .buffer_handler
6724                    .list_buffers(self.state_container.buffers());
6725                // For now, just log the list - later we can show a popup
6726                for line in &buffer_list {
6727                    debug!("{}", line);
6728                }
6729                Ok(false)
6730            }
6731            BufferAction::SwitchToBuffer(buffer_index) => {
6732                let message = self
6733                    .buffer_handler
6734                    .switch_to_buffer(self.state_container.buffers_mut(), buffer_index);
6735                debug!("{}", message);
6736
6737                // Sync all state after buffer switch
6738                self.sync_after_buffer_switch();
6739
6740                Ok(false)
6741            }
6742        }
6743    }
6744
6745    fn handle_expand_asterisk(&mut self) -> Result<bool> {
6746        if let Some(buffer) = self.state_container.buffers_mut().current_mut() {
6747            if buffer.expand_asterisk(&self.hybrid_parser) {
6748                // Sync for rendering if needed
6749                if buffer.get_edit_mode() == EditMode::SingleLine {
6750                    let text = buffer.get_input_text();
6751                    let cursor = buffer.get_input_cursor_position();
6752                    self.set_input_text_with_cursor(text, cursor);
6753                }
6754            }
6755        }
6756        Ok(false)
6757    }
6758
6759    pub(crate) fn toggle_debug_mode(&mut self) {
6760        // Use the DebugContext trait which has all the logic
6761        DebugContext::toggle_debug_mode(self);
6762    }
6763
6764    // ==================== Debug Helper Methods ====================
6765    // These are kept in the TUI to avoid regressions from moving data access
6766
6767    /// Generate the parser debug section
6768    pub(crate) fn debug_generate_parser_info(&self, query: &str) -> String {
6769        self.hybrid_parser
6770            .get_detailed_debug_info(query, query.len())
6771    }
6772
6773    fn debug_generate_navigation_state(&self) -> String {
6774        let mut debug_info = String::new();
6775        debug_info.push_str("\n========== NAVIGATION DEBUG ==========\n");
6776        let current_column = self.state_container.get_current_column();
6777        let scroll_offset = self.state_container.get_scroll_offset();
6778        let nav_state = self.state_container.navigation();
6779
6780        debug_info.push_str(&format!("Buffer Column Position: {current_column}\n"));
6781        debug_info.push_str(&format!(
6782            "Buffer Scroll Offset: row={}, col={}\n",
6783            scroll_offset.0, scroll_offset.1
6784        ));
6785        debug_info.push_str(&format!(
6786            "NavigationState Column: {}\n",
6787            nav_state.selected_column
6788        ));
6789        debug_info.push_str(&format!(
6790            "NavigationState Row: {:?}\n",
6791            nav_state.selected_row
6792        ));
6793        debug_info.push_str(&format!(
6794            "NavigationState Scroll Offset: row={}, col={}\n",
6795            nav_state.scroll_offset.0, nav_state.scroll_offset.1
6796        ));
6797
6798        // Show if synchronization is correct
6799        if current_column != nav_state.selected_column {
6800            debug_info.push_str(&format!(
6801                "⚠️  WARNING: Column mismatch! Buffer={}, Nav={}\n",
6802                current_column, nav_state.selected_column
6803            ));
6804        }
6805        if scroll_offset.1 != nav_state.scroll_offset.1 {
6806            debug_info.push_str(&format!(
6807                "⚠️  WARNING: Scroll column mismatch! Buffer={}, Nav={}\n",
6808                scroll_offset.1, nav_state.scroll_offset.1
6809            ));
6810        }
6811
6812        debug_info.push_str("\n--- Navigation Flow ---\n");
6813        debug_info.push_str(
6814            "(Enable RUST_LOG=sql_cli::ui::viewport_manager=debug,navigation=debug to see flow)\n",
6815        );
6816
6817        // Show pinned column info for navigation context
6818        if let Some(dataview) = self.state_container.get_buffer_dataview() {
6819            let pinned_count = dataview.get_pinned_columns().len();
6820            let pinned_names = dataview.get_pinned_column_names();
6821            debug_info.push_str(&format!("Pinned Column Count: {pinned_count}\n"));
6822            if !pinned_names.is_empty() {
6823                debug_info.push_str(&format!("Pinned Column Names: {pinned_names:?}\n"));
6824            }
6825            debug_info.push_str(&format!("First Scrollable Column: {pinned_count}\n"));
6826
6827            // Show if current column is in pinned or scrollable area
6828            if current_column < pinned_count {
6829                debug_info.push_str(&format!(
6830                    "Current Position: PINNED area (column {current_column})\n"
6831                ));
6832            } else {
6833                debug_info.push_str(&format!(
6834                    "Current Position: SCROLLABLE area (column {}, scrollable index {})\n",
6835                    current_column,
6836                    current_column - pinned_count
6837                ));
6838            }
6839
6840            // Show display column order
6841            let display_columns = dataview.get_display_columns();
6842            debug_info.push_str("\n--- COLUMN ORDERING ---\n");
6843            debug_info.push_str(&format!(
6844                "Display column order (first 10): {:?}\n",
6845                &display_columns[..display_columns.len().min(10)]
6846            ));
6847            if display_columns.len() > 10 {
6848                debug_info.push_str(&format!(
6849                    "... and {} more columns\n",
6850                    display_columns.len() - 10
6851                ));
6852            }
6853
6854            // Find current column in display order
6855            if let Some(display_idx) = display_columns
6856                .iter()
6857                .position(|&idx| idx == current_column)
6858            {
6859                debug_info.push_str(&format!(
6860                    "Current column {} is at display index {}/{}\n",
6861                    current_column,
6862                    display_idx,
6863                    display_columns.len()
6864                ));
6865
6866                // Show what happens on next move
6867                if display_idx + 1 < display_columns.len() {
6868                    let next_col = display_columns[display_idx + 1];
6869                    debug_info.push_str(&format!(
6870                        "Next 'l' press should move to column {} (display index {})\n",
6871                        next_col,
6872                        display_idx + 1
6873                    ));
6874                } else {
6875                    debug_info.push_str("Next 'l' press should wrap to first column\n");
6876                }
6877            } else {
6878                debug_info.push_str(&format!(
6879                    "WARNING: Current column {current_column} not found in display order!\n"
6880                ));
6881            }
6882        }
6883        debug_info.push_str("==========================================\n");
6884        debug_info
6885    }
6886
6887    fn debug_generate_column_search_state(&self) -> String {
6888        let mut debug_info = String::new();
6889        let show_column_search = self.shadow_state.borrow().get_mode() == AppMode::ColumnSearch
6890            || !self.state_container.column_search().pattern.is_empty();
6891        if show_column_search {
6892            let column_search = self.state_container.column_search();
6893            debug_info.push_str("\n========== COLUMN SEARCH STATE ==========\n");
6894            debug_info.push_str(&format!("Pattern: '{}'\n", column_search.pattern));
6895            debug_info.push_str(&format!(
6896                "Matching Columns: {} found\n",
6897                column_search.matching_columns.len()
6898            ));
6899            if !column_search.matching_columns.is_empty() {
6900                debug_info.push_str("Matches:\n");
6901                for (idx, (col_idx, col_name)) in column_search.matching_columns.iter().enumerate()
6902                {
6903                    let marker = if idx == column_search.current_match {
6904                        " <--"
6905                    } else {
6906                        ""
6907                    };
6908                    debug_info
6909                        .push_str(&format!("  [{idx}] {col_name} (index {col_idx}){marker}\n"));
6910                }
6911            }
6912            debug_info.push_str(&format!(
6913                "Current Match Index: {}\n",
6914                column_search.current_match
6915            ));
6916            debug_info.push_str(&format!(
6917                "Current Column: {}\n",
6918                self.state_container.get_current_column()
6919            ));
6920            debug_info.push_str("==========================================\n");
6921        }
6922        debug_info
6923    }
6924
6925    pub(crate) fn debug_generate_trace_logs(&self) -> String {
6926        let mut debug_info = String::from("\n========== TRACE LOGS ==========\n");
6927        debug_info.push_str("(Most recent at bottom, last 100 entries)\n");
6928
6929        if let Some(ref log_buffer) = self.log_buffer {
6930            let recent_logs = log_buffer.get_recent(100);
6931            for entry in recent_logs {
6932                debug_info.push_str(&entry.format_for_display());
6933                debug_info.push('\n');
6934            }
6935            debug_info.push_str(&format!("Total log entries: {}\n", log_buffer.len()));
6936        } else {
6937            debug_info.push_str("Log buffer not initialized\n");
6938        }
6939        debug_info.push_str("================================\n");
6940
6941        debug_info
6942    }
6943
6944    /// Generate the state change logs debug section
6945    pub(crate) fn debug_generate_state_logs(&self) -> String {
6946        let mut debug_info = String::new();
6947
6948        if let Some(ref debug_service) = self.debug_service {
6949            debug_info.push_str("\n========== STATE CHANGE LOGS ==========\n");
6950            debug_info.push_str("(Most recent at bottom, from DebugService)\n");
6951            let debug_entries = debug_service.get_entries();
6952            let recent = debug_entries.iter().rev().take(50).rev();
6953            for entry in recent {
6954                debug_info.push_str(&format!(
6955                    "[{}] {:?} [{}]: {}\n",
6956                    entry.timestamp, entry.level, entry.component, entry.message
6957                ));
6958            }
6959            debug_info.push_str(&format!(
6960                "Total state change entries: {}\n",
6961                debug_entries.len()
6962            ));
6963            debug_info.push_str("================================\n");
6964        } else {
6965            debug_info.push_str("\n========== STATE CHANGE LOGS ==========\n");
6966            debug_info.push_str("DebugService not available (service_container is None)\n");
6967            debug_info.push_str("================================\n");
6968        }
6969
6970        debug_info
6971    }
6972
6973    /// Extract time in milliseconds from a timing string
6974    pub(crate) fn debug_extract_timing(&self, s: &str) -> Option<f64> {
6975        crate::ui::rendering::ui_layout_utils::extract_timing_from_debug_string(s)
6976    }
6977
6978    fn show_pretty_query(&mut self) {
6979        if let Some(buffer) = self.state_container.buffers_mut().current_mut() {
6980            self.shadow_state.borrow_mut().set_mode(
6981                AppMode::PrettyQuery,
6982                buffer,
6983                "pretty_query_show",
6984            );
6985            let query = buffer.get_input_text();
6986            self.debug_widget.generate_pretty_sql(&query);
6987        }
6988    }
6989
6990    /// Add global indicators like key sequence display
6991    fn add_global_indicators(&self, spans: &mut Vec<Span>) {
6992        if self.key_sequence_renderer.has_content() {
6993            let key_display = self.key_sequence_renderer.get_display();
6994            if !key_display.is_empty() {
6995                spans.push(Span::raw(" | Keys: "));
6996                spans.push(Span::styled(
6997                    key_display,
6998                    Style::default()
6999                        .fg(Color::Cyan)
7000                        .add_modifier(Modifier::ITALIC),
7001                ));
7002            }
7003        }
7004    }
7005
7006    fn add_query_source_indicator(&self, spans: &mut Vec<Span>) {
7007        if let Some(source) = self.state_container.get_last_query_source() {
7008            spans.push(Span::raw(" | "));
7009            let (icon, label, color) = match source.as_str() {
7010                "cache" => (
7011                    &self.config.display.icons.cache,
7012                    "CACHE".to_string(),
7013                    Color::Cyan,
7014                ),
7015                "file" | "FileDataSource" => (
7016                    &self.config.display.icons.file,
7017                    "FILE".to_string(),
7018                    Color::Green,
7019                ),
7020                "SqlServerDataSource" => (
7021                    &self.config.display.icons.database,
7022                    "SQL".to_string(),
7023                    Color::Blue,
7024                ),
7025                "PublicApiDataSource" => (
7026                    &self.config.display.icons.api,
7027                    "API".to_string(),
7028                    Color::Yellow,
7029                ),
7030                _ => (
7031                    &self.config.display.icons.api,
7032                    source.clone(),
7033                    Color::Magenta,
7034                ),
7035            };
7036            spans.push(Span::raw(format!("{icon} ")));
7037            spans.push(Span::styled(label, Style::default().fg(color)));
7038        }
7039    }
7040
7041    fn add_case_sensitivity_indicator(&self, spans: &mut Vec<Span>) {
7042        let case_insensitive = self.state_container.is_case_insensitive();
7043        if case_insensitive {
7044            spans.push(Span::raw(" | "));
7045            let icon = self.config.display.icons.case_insensitive.clone();
7046            spans.push(Span::styled(
7047                format!("{icon} CASE"),
7048                Style::default().fg(Color::Cyan),
7049            ));
7050        }
7051    }
7052
7053    fn add_column_packing_indicator(&self, spans: &mut Vec<Span>) {
7054        if let Some(ref viewport_manager) = *self.viewport_manager.borrow() {
7055            let packing_mode = viewport_manager.get_packing_mode();
7056            spans.push(Span::raw(" | "));
7057            let (text, color) = match packing_mode {
7058                ColumnPackingMode::DataFocus => ("DATA", Color::Cyan),
7059                ColumnPackingMode::HeaderFocus => ("HEADER", Color::Yellow),
7060                ColumnPackingMode::Balanced => ("BALANCED", Color::Green),
7061            };
7062            spans.push(Span::styled(text, Style::default().fg(color)));
7063        }
7064    }
7065
7066    fn add_status_message(&self, spans: &mut Vec<Span>) {
7067        let status_msg = self.state_container.get_buffer_status_message();
7068        if !status_msg.is_empty() {
7069            spans.push(Span::raw(" | "));
7070            spans.push(Span::styled(
7071                status_msg,
7072                Style::default()
7073                    .fg(Color::Yellow)
7074                    .add_modifier(Modifier::BOLD),
7075            ));
7076        }
7077    }
7078
7079    fn get_help_text_for_mode(&self) -> &str {
7080        match self.shadow_state.borrow().get_mode() {
7081            AppMode::Command => "Enter:Run | Tab:Complete | ↓:Results | F1:Help",
7082            AppMode::Results => match self.get_selection_mode() {
7083                SelectionMode::Cell => "v:Row mode | y:Yank cell | ↑:Edit | F1:Help",
7084                SelectionMode::Row => "v:Cell mode | y:Yank | f:Filter | ↑:Edit | F1:Help",
7085                SelectionMode::Column => "v:Cell mode | y:Yank col | ↑:Edit | F1:Help",
7086            },
7087            AppMode::Search | AppMode::Filter | AppMode::FuzzyFilter | AppMode::ColumnSearch => {
7088                "Enter:Apply | Esc:Cancel"
7089            }
7090            AppMode::Help | AppMode::Debug | AppMode::PrettyQuery | AppMode::ColumnStats => {
7091                "Esc:Close"
7092            }
7093            AppMode::History => "Enter:Select | Esc:Cancel",
7094            AppMode::JumpToRow => "Enter:Jump | Esc:Cancel",
7095        }
7096    }
7097
7098    fn add_shadow_state_display(&self, spans: &mut Vec<Span>) {
7099        let shadow_display = self.shadow_state.borrow().status_display();
7100        spans.push(Span::raw(" "));
7101        spans.push(Span::styled(
7102            shadow_display,
7103            Style::default().fg(Color::Cyan),
7104        ));
7105    }
7106
7107    /// Add right-aligned help text if space allows
7108    fn add_help_text_display<'a>(&self, spans: &mut Vec<Span<'a>>, help_text: &'a str, area: Rect) {
7109        let current_length: usize = spans.iter().map(|s| s.content.len()).sum();
7110        let available_width = area.width.saturating_sub(TABLE_BORDER_WIDTH) as usize;
7111        let help_length = help_text.len();
7112
7113        if current_length + help_length + 3 < available_width {
7114            let padding = available_width - current_length - help_length - 3;
7115            spans.push(Span::raw(" ".repeat(padding)));
7116            spans.push(Span::raw(" | "));
7117            spans.push(Span::styled(
7118                help_text,
7119                Style::default().fg(Color::DarkGray),
7120            ));
7121        }
7122    }
7123}
7124
7125// Implement ActionHandlerContext trait for EnhancedTuiApp
7126impl ActionHandlerContext for EnhancedTuiApp {
7127    // Navigation methods - delegate to trait implementations
7128    fn previous_row(&mut self) {
7129        <Self as NavigationBehavior>::previous_row(self);
7130
7131        // Update TableWidgetManager for rendering
7132        let current_row = self.state_container.navigation().selected_row;
7133        let current_col = self.state_container.navigation().selected_column;
7134        self.table_widget_manager
7135            .borrow_mut()
7136            .navigate_to(current_row, current_col);
7137        info!(target: "navigation", "previous_row: Updated TableWidgetManager to ({}, {})", current_row, current_col);
7138    }
7139
7140    fn next_row(&mut self) {
7141        info!(target: "navigation", "next_row called - calling NavigationBehavior::next_row");
7142        <Self as NavigationBehavior>::next_row(self);
7143
7144        // Update TableWidgetManager for rendering
7145        let current_row = self.state_container.navigation().selected_row;
7146        let current_col = self.state_container.navigation().selected_column;
7147        self.table_widget_manager
7148            .borrow_mut()
7149            .navigate_to(current_row, current_col);
7150        info!(target: "navigation", "next_row: Updated TableWidgetManager to ({}, {})", current_row, current_col);
7151    }
7152
7153    fn move_column_left(&mut self) {
7154        <Self as ColumnBehavior>::move_column_left(self);
7155
7156        // Update TableWidgetManager for rendering
7157        let current_row = self.state_container.navigation().selected_row;
7158        let current_col = self.state_container.navigation().selected_column;
7159        self.table_widget_manager
7160            .borrow_mut()
7161            .navigate_to(current_row, current_col);
7162        info!(target: "navigation", "move_column_left: Updated TableWidgetManager to ({}, {})", current_row, current_col);
7163    }
7164
7165    fn move_column_right(&mut self) {
7166        <Self as ColumnBehavior>::move_column_right(self);
7167
7168        // Update TableWidgetManager for rendering
7169        let current_row = self.state_container.navigation().selected_row;
7170        let current_col = self.state_container.navigation().selected_column;
7171        self.table_widget_manager
7172            .borrow_mut()
7173            .navigate_to(current_row, current_col);
7174        info!(target: "navigation", "move_column_right: Updated TableWidgetManager to ({}, {})", current_row, current_col);
7175    }
7176
7177    fn page_up(&mut self) {
7178        <Self as NavigationBehavior>::page_up(self);
7179    }
7180
7181    fn page_down(&mut self) {
7182        <Self as NavigationBehavior>::page_down(self);
7183    }
7184
7185    fn goto_first_row(&mut self) {
7186        use crate::ui::state::state_coordinator::StateCoordinator;
7187
7188        // Always perform the normal goto first row behavior
7189        <Self as NavigationBehavior>::goto_first_row(self);
7190
7191        // Use StateCoordinator to handle vim search coordination
7192        StateCoordinator::goto_first_row_with_refs(
7193            &mut self.state_container,
7194            Some(&self.vim_search_adapter),
7195            Some(&self.viewport_manager),
7196        );
7197    }
7198
7199    fn goto_last_row(&mut self) {
7200        use crate::ui::state::state_coordinator::StateCoordinator;
7201
7202        // Perform the normal goto last row behavior
7203        <Self as NavigationBehavior>::goto_last_row(self);
7204
7205        // Use StateCoordinator for additional coordination
7206        StateCoordinator::goto_last_row_with_refs(&mut self.state_container);
7207    }
7208
7209    fn goto_first_column(&mut self) {
7210        <Self as ColumnBehavior>::goto_first_column(self);
7211    }
7212
7213    fn goto_last_column(&mut self) {
7214        <Self as ColumnBehavior>::goto_last_column(self);
7215    }
7216
7217    fn goto_row(&mut self, row: usize) {
7218        use crate::ui::state::state_coordinator::StateCoordinator;
7219
7220        // Perform the normal goto line behavior
7221        <Self as NavigationBehavior>::goto_line(self, row + 1); // Convert to 1-indexed
7222
7223        // Use StateCoordinator for additional coordination
7224        StateCoordinator::goto_row_with_refs(&mut self.state_container, row);
7225    }
7226
7227    fn goto_column(&mut self, col: usize) {
7228        // For now, implement basic column navigation
7229        // TODO: Implement proper goto_column functionality
7230        let current_col = self.state_container.get_current_column();
7231        if col < current_col {
7232            for _ in 0..(current_col - col) {
7233                <Self as ColumnBehavior>::move_column_left(self);
7234            }
7235        } else if col > current_col {
7236            for _ in 0..(col - current_col) {
7237                <Self as ColumnBehavior>::move_column_right(self);
7238            }
7239        }
7240    }
7241
7242    // Mode and UI state
7243    fn set_mode(&mut self, mode: AppMode) {
7244        // Use the proper synchronization method that updates both buffer and shadow_state
7245        self.set_mode_via_shadow_state(mode, "action_handler");
7246    }
7247
7248    fn get_mode(&self) -> AppMode {
7249        self.shadow_state.borrow().get_mode()
7250    }
7251
7252    fn set_status_message(&mut self, message: String) {
7253        self.state_container.set_status_message(message);
7254    }
7255
7256    // Column operations - delegate to trait implementations
7257    fn toggle_column_pin(&mut self) {
7258        // Call the existing toggle_column_pin implementation directly
7259        self.toggle_column_pin_impl();
7260    }
7261
7262    fn hide_current_column(&mut self) {
7263        <Self as ColumnBehavior>::hide_current_column(self);
7264    }
7265
7266    fn unhide_all_columns(&mut self) {
7267        <Self as ColumnBehavior>::unhide_all_columns(self);
7268    }
7269
7270    fn clear_all_pinned_columns(&mut self) {
7271        // Call the existing clear_all_pinned_columns implementation directly
7272        self.clear_all_pinned_columns_impl();
7273    }
7274
7275    // Export operations
7276    fn export_to_csv(&mut self) {
7277        // For now, just set a status message - actual implementation will be added later
7278        self.state_container
7279            .set_status_message("CSV export not yet implemented".to_string());
7280    }
7281
7282    fn export_to_json(&mut self) {
7283        // For now, just set a status message - actual implementation will be added later
7284        self.state_container
7285            .set_status_message("JSON export not yet implemented".to_string());
7286    }
7287
7288    // Yank operations - delegate to trait implementations
7289    fn yank_cell(&mut self) {
7290        YankBehavior::yank_cell(self);
7291    }
7292
7293    fn yank_row(&mut self) {
7294        YankBehavior::yank_row(self);
7295    }
7296
7297    fn yank_column(&mut self) {
7298        YankBehavior::yank_column(self);
7299    }
7300
7301    fn yank_all(&mut self) {
7302        YankBehavior::yank_all(self);
7303    }
7304
7305    fn yank_query(&mut self) {
7306        YankBehavior::yank_query(self);
7307    }
7308
7309    // Toggle operations
7310    fn toggle_selection_mode(&mut self) {
7311        self.state_container.toggle_selection_mode();
7312        let new_mode = self.state_container.get_selection_mode();
7313        let msg = match new_mode {
7314            SelectionMode::Cell => "Cell mode - Navigate to select individual cells",
7315            SelectionMode::Row => "Row mode - Navigate to select rows",
7316            SelectionMode::Column => "Column mode - Navigate to select columns",
7317        };
7318        self.state_container.set_status_message(msg.to_string());
7319    }
7320
7321    fn toggle_row_numbers(&mut self) {
7322        let current = self.state_container.is_show_row_numbers();
7323        self.state_container.set_show_row_numbers(!current);
7324        let message = if current {
7325            "Row numbers: OFF".to_string()
7326        } else {
7327            "Row numbers: ON (showing line numbers)".to_string()
7328        };
7329        self.state_container.set_status_message(message);
7330        // Recalculate column widths with new mode
7331        self.calculate_optimal_column_widths();
7332    }
7333
7334    fn toggle_compact_mode(&mut self) {
7335        let current_mode = self.state_container.is_compact_mode();
7336        self.state_container.set_compact_mode(!current_mode);
7337        let message = if current_mode {
7338            "Compact mode disabled"
7339        } else {
7340            "Compact mode enabled"
7341        };
7342        self.state_container.set_status_message(message.to_string());
7343    }
7344
7345    fn toggle_case_insensitive(&mut self) {
7346        let current = self.state_container.is_case_insensitive();
7347        self.state_container.set_case_insensitive(!current);
7348        self.state_container.set_status_message(format!(
7349            "Case-insensitive string comparisons: {}",
7350            if current { "OFF" } else { "ON" }
7351        ));
7352    }
7353
7354    fn toggle_key_indicator(&mut self) {
7355        let enabled = !self.key_indicator.enabled;
7356        self.key_indicator.set_enabled(enabled);
7357        self.key_sequence_renderer.set_enabled(enabled);
7358        self.state_container.set_status_message(format!(
7359            "Key press indicator {}",
7360            if enabled { "enabled" } else { "disabled" }
7361        ));
7362    }
7363
7364    // Clear operations
7365    fn clear_filter(&mut self) {
7366        // Check if we have an active filter to clear
7367        if let Some(dataview) = self.state_container.get_buffer_dataview() {
7368            if dataview.has_filter() {
7369                // Clear the filter
7370                if let Some(dataview_mut) = self.state_container.get_buffer_dataview_mut() {
7371                    dataview_mut.clear_filter();
7372                    self.state_container
7373                        .set_status_message("Filter cleared".to_string());
7374                }
7375
7376                // Update ViewportManager after clearing filter
7377                // Sync the dataview to both managers
7378                self.sync_dataview_to_managers();
7379            } else {
7380                self.state_container
7381                    .set_status_message("No active filter to clear".to_string());
7382            }
7383        } else {
7384            self.state_container
7385                .set_status_message("No data loaded".to_string());
7386        }
7387    }
7388
7389    fn clear_line(&mut self) {
7390        self.state_container.clear_line();
7391    }
7392
7393    // Mode operations
7394    fn start_search(&mut self) {
7395        self.start_vim_search();
7396    }
7397
7398    fn start_column_search(&mut self) {
7399        self.enter_search_mode(SearchMode::ColumnSearch);
7400    }
7401
7402    fn start_filter(&mut self) {
7403        self.enter_search_mode(SearchMode::Filter);
7404    }
7405
7406    fn start_fuzzy_filter(&mut self) {
7407        self.enter_search_mode(SearchMode::FuzzyFilter);
7408    }
7409
7410    fn exit_current_mode(&mut self) {
7411        // Handle escape based on current mode.
7412        // All transitions must go through set_mode_via_shadow_state so that
7413        // shadow_state and the buffer mode stay in sync. Updating only
7414        // state_container.set_mode(...) leaves shadow_state stale, which
7415        // causes rendering (which reads shadow_state) to diverge from key
7416        // dispatch (which reads state_container) — e.g. grey input box while
7417        // j/k get appended to the query.
7418        let mode = self.shadow_state.borrow().get_mode();
7419        match mode {
7420            AppMode::Results => {
7421                // VimSearchAdapter now handles Escape in Results mode when search is active
7422                // If we get here, it means search wasn't active, so switch to Command mode
7423                self.set_mode_via_shadow_state(AppMode::Command, "escape_from_results");
7424            }
7425            AppMode::Command => {
7426                self.set_mode_via_shadow_state(AppMode::Results, "escape_from_command");
7427            }
7428            AppMode::Help => {
7429                self.set_mode_via_shadow_state(AppMode::Results, "escape_from_help");
7430            }
7431            AppMode::JumpToRow => {
7432                self.set_mode_via_shadow_state(AppMode::Results, "escape_from_jump_to_row");
7433                <Self as InputBehavior>::clear_jump_to_row_input(self);
7434                // Clear jump-to-row state (can mutate directly now)
7435                self.state_container.jump_to_row_mut().is_active = false;
7436                self.state_container
7437                    .set_status_message("Jump to row cancelled".to_string());
7438            }
7439            _ => {
7440                // For any other mode, return to Results
7441                self.set_mode_via_shadow_state(AppMode::Results, "escape_to_results");
7442            }
7443        }
7444    }
7445
7446    fn toggle_debug_mode(&mut self) {
7447        // Use the DebugContext trait
7448        <Self as DebugContext>::toggle_debug_mode(self);
7449    }
7450
7451    // Column arrangement operations
7452    fn move_current_column_left(&mut self) {
7453        <Self as ColumnBehavior>::move_current_column_left(self);
7454    }
7455
7456    fn move_current_column_right(&mut self) {
7457        <Self as ColumnBehavior>::move_current_column_right(self);
7458    }
7459
7460    // Search navigation
7461    fn next_search_match(&mut self) {
7462        self.vim_search_next();
7463    }
7464
7465    fn previous_search_match(&mut self) {
7466        if self.vim_search_adapter.borrow().is_active()
7467            || self.vim_search_adapter.borrow().get_pattern().is_some()
7468        {
7469            self.vim_search_previous();
7470        }
7471    }
7472
7473    // Statistics and display
7474    fn show_column_statistics(&mut self) {
7475        self.calculate_column_statistics();
7476    }
7477
7478    fn cycle_column_packing(&mut self) {
7479        let message = {
7480            let mut viewport_manager_borrow = self.viewport_manager.borrow_mut();
7481            let viewport_manager = viewport_manager_borrow
7482                .as_mut()
7483                .expect("ViewportManager must exist");
7484            let new_mode = viewport_manager.cycle_packing_mode();
7485            format!("Column packing: {}", new_mode.display_name())
7486        };
7487        self.state_container.set_status_message(message);
7488    }
7489
7490    // Viewport navigation
7491    fn navigate_to_viewport_top(&mut self) {
7492        let result = {
7493            let mut viewport_manager_borrow = self.viewport_manager.borrow_mut();
7494            (*viewport_manager_borrow)
7495                .as_mut()
7496                .map(super::viewport_manager::ViewportManager::navigate_to_viewport_top)
7497        };
7498
7499        if let Some(result) = result {
7500            // ViewportManager has updated, sync NavigationState from it
7501            self.sync_navigation_with_viewport();
7502
7503            // Update buffer's selected row
7504            self.state_container
7505                .set_selected_row(Some(result.row_position));
7506
7507            // Update buffer's scroll offset
7508            if result.viewport_changed {
7509                let scroll_offset = self.state_container.navigation().scroll_offset;
7510                self.state_container.set_scroll_offset(scroll_offset);
7511            }
7512        }
7513    }
7514
7515    fn navigate_to_viewport_middle(&mut self) {
7516        let result = {
7517            let mut viewport_manager_borrow = self.viewport_manager.borrow_mut();
7518            (*viewport_manager_borrow)
7519                .as_mut()
7520                .map(super::viewport_manager::ViewportManager::navigate_to_viewport_middle)
7521        };
7522
7523        if let Some(result) = result {
7524            // ViewportManager has updated, sync NavigationState from it
7525            self.sync_navigation_with_viewport();
7526
7527            // Update buffer's selected row
7528            self.state_container
7529                .set_selected_row(Some(result.row_position));
7530
7531            // Update buffer's scroll offset
7532            if result.viewport_changed {
7533                let scroll_offset = self.state_container.navigation().scroll_offset;
7534                self.state_container.set_scroll_offset(scroll_offset);
7535            }
7536        }
7537    }
7538
7539    fn navigate_to_viewport_bottom(&mut self) {
7540        let result = {
7541            let mut viewport_manager_borrow = self.viewport_manager.borrow_mut();
7542            (*viewport_manager_borrow)
7543                .as_mut()
7544                .map(super::viewport_manager::ViewportManager::navigate_to_viewport_bottom)
7545        };
7546
7547        if let Some(result) = result {
7548            // ViewportManager has updated, sync NavigationState from it
7549            self.sync_navigation_with_viewport();
7550
7551            // Update buffer's selected row
7552            self.state_container
7553                .set_selected_row(Some(result.row_position));
7554
7555            // Update buffer's scroll offset
7556            if result.viewport_changed {
7557                let scroll_offset = self.state_container.navigation().scroll_offset;
7558                self.state_container.set_scroll_offset(scroll_offset);
7559            }
7560        }
7561    }
7562
7563    // Input and text editing methods
7564    fn move_input_cursor_left(&mut self) {
7565        self.state_container.move_input_cursor_left();
7566    }
7567
7568    fn move_input_cursor_right(&mut self) {
7569        self.state_container.move_input_cursor_right();
7570    }
7571
7572    fn move_input_cursor_home(&mut self) {
7573        self.state_container.set_input_cursor_position(0);
7574    }
7575
7576    fn move_input_cursor_end(&mut self) {
7577        let text_len = self.state_container.get_input_text().chars().count();
7578        self.state_container.set_input_cursor_position(text_len);
7579    }
7580
7581    fn backspace(&mut self) {
7582        self.state_container.backspace();
7583    }
7584
7585    fn delete(&mut self) {
7586        self.state_container.delete();
7587    }
7588
7589    fn undo(&mut self) {
7590        self.state_container.perform_undo();
7591    }
7592
7593    fn redo(&mut self) {
7594        self.state_container.perform_redo();
7595    }
7596
7597    fn start_jump_to_row(&mut self) {
7598        self.state_container.set_mode(AppMode::JumpToRow);
7599        self.shadow_state
7600            .borrow_mut()
7601            .observe_mode_change(AppMode::JumpToRow, "jump_to_row_requested");
7602        <Self as InputBehavior>::clear_jump_to_row_input(self);
7603
7604        // Set jump-to-row state as active (can mutate directly now)
7605        self.state_container.jump_to_row_mut().is_active = true;
7606
7607        self.state_container
7608            .set_status_message("Enter row number (1-based):".to_string());
7609    }
7610
7611    fn clear_jump_to_row_input(&mut self) {
7612        <Self as InputBehavior>::clear_jump_to_row_input(self);
7613    }
7614
7615    fn toggle_cursor_lock(&mut self) {
7616        // Toggle cursor lock in ViewportManager
7617        let is_locked = {
7618            let mut viewport_manager_borrow = self.viewport_manager.borrow_mut();
7619            if let Some(ref mut viewport_manager) = *viewport_manager_borrow {
7620                viewport_manager.toggle_cursor_lock();
7621                Some(viewport_manager.is_cursor_locked())
7622            } else {
7623                None
7624            }
7625        };
7626
7627        if let Some(is_locked) = is_locked {
7628            let msg = if is_locked {
7629                "Cursor lock ON - cursor stays in viewport position while scrolling"
7630            } else {
7631                "Cursor lock OFF"
7632            };
7633            self.state_container.set_status_message(msg.to_string());
7634
7635            // Log for shadow state learning (not tracking as state change yet)
7636            info!(target: "shadow_state",
7637                "Cursor lock toggled: {} (in {:?} mode)",
7638                if is_locked { "ON" } else { "OFF" },
7639                self.shadow_state.borrow().get_mode()
7640            );
7641        }
7642    }
7643
7644    fn toggle_viewport_lock(&mut self) {
7645        // Toggle viewport lock in ViewportManager
7646        let is_locked = {
7647            let mut viewport_manager_borrow = self.viewport_manager.borrow_mut();
7648            if let Some(ref mut viewport_manager) = *viewport_manager_borrow {
7649                viewport_manager.toggle_viewport_lock();
7650                Some(viewport_manager.is_viewport_locked())
7651            } else {
7652                None
7653            }
7654        };
7655
7656        if let Some(is_locked) = is_locked {
7657            let msg = if is_locked {
7658                "Viewport lock ON - navigation constrained to current viewport"
7659            } else {
7660                "Viewport lock OFF"
7661            };
7662            self.state_container.set_status_message(msg.to_string());
7663
7664            // Log for shadow state learning (not tracking as state change yet)
7665            info!(target: "shadow_state",
7666                "Viewport lock toggled: {} (in {:?} mode)",
7667                if is_locked { "ON" } else { "OFF" },
7668                self.shadow_state.borrow().get_mode()
7669            );
7670        }
7671    }
7672
7673    // Debug and development operations
7674    fn show_debug_info(&mut self) {
7675        <Self as DebugContext>::toggle_debug_mode(self);
7676    }
7677
7678    fn show_pretty_query(&mut self) {
7679        self.show_pretty_query();
7680    }
7681
7682    fn show_help(&mut self) {
7683        self.state_container.set_help_visible(true);
7684        self.set_mode_via_shadow_state(AppMode::Help, "help_requested");
7685        self.help_widget.on_enter();
7686    }
7687
7688    // Text editing operations
7689    fn kill_line(&mut self) {
7690        use crate::ui::traits::input_ops::InputBehavior;
7691        InputBehavior::kill_line(self);
7692        let message = if self.state_container.is_kill_ring_empty() {
7693            "Kill line - nothing to kill".to_string()
7694        } else {
7695            let kill_ring = self.state_container.get_kill_ring();
7696            format!(
7697                "Killed to end of line - {} chars in kill ring",
7698                kill_ring.len()
7699            )
7700        };
7701        self.state_container.set_status_message(message);
7702    }
7703
7704    fn kill_line_backward(&mut self) {
7705        use crate::ui::traits::input_ops::InputBehavior;
7706        InputBehavior::kill_line_backward(self);
7707        let message = if self.state_container.is_kill_ring_empty() {
7708            "Kill line backward - nothing to kill".to_string()
7709        } else {
7710            let kill_ring = self.state_container.get_kill_ring();
7711            format!(
7712                "Killed to beginning of line - {} chars in kill ring",
7713                kill_ring.len()
7714            )
7715        };
7716        self.state_container.set_status_message(message);
7717    }
7718
7719    fn delete_word_backward(&mut self) {
7720        use crate::ui::traits::input_ops::InputBehavior;
7721        InputBehavior::delete_word_backward(self);
7722    }
7723
7724    fn delete_word_forward(&mut self) {
7725        use crate::ui::traits::input_ops::InputBehavior;
7726        InputBehavior::delete_word_forward(self);
7727    }
7728
7729    fn jump_to_prev_token(&mut self) {
7730        use crate::ui::traits::input_ops::InputBehavior;
7731        InputBehavior::jump_to_prev_token(self);
7732    }
7733
7734    fn jump_to_next_token(&mut self) {
7735        use crate::ui::traits::input_ops::InputBehavior;
7736        InputBehavior::jump_to_next_token(self);
7737    }
7738
7739    fn expand_asterisk(&mut self) {
7740        if let Some(buffer) = self.state_container.buffers_mut().current_mut() {
7741            if buffer.expand_asterisk(&self.hybrid_parser) {
7742                // Sync for rendering if needed
7743                if buffer.get_edit_mode() == EditMode::SingleLine {
7744                    let text = buffer.get_input_text();
7745                    let cursor = buffer.get_input_cursor_position();
7746                    self.set_input_text_with_cursor(text, cursor);
7747                }
7748            }
7749        }
7750    }
7751
7752    fn expand_asterisk_visible(&mut self) {
7753        if let Some(buffer) = self.state_container.buffers_mut().current_mut() {
7754            if buffer.expand_asterisk_visible() {
7755                // Sync for rendering if needed
7756                if buffer.get_edit_mode() == EditMode::SingleLine {
7757                    let text = buffer.get_input_text();
7758                    let cursor = buffer.get_input_cursor_position();
7759                    self.set_input_text_with_cursor(text, cursor);
7760                }
7761            }
7762        }
7763    }
7764
7765    fn previous_history_command(&mut self) {
7766        let history_entries = self
7767            .state_container
7768            .command_history()
7769            .get_navigation_entries();
7770        let history_commands: Vec<String> =
7771            history_entries.iter().map(|e| e.command.clone()).collect();
7772
7773        if let Some(buffer) = self.state_container.buffers_mut().current_mut() {
7774            if buffer.navigate_history_up(&history_commands) {
7775                self.sync_all_input_states();
7776                self.state_container
7777                    .set_status_message("Previous command from history".to_string());
7778            }
7779        }
7780    }
7781
7782    fn next_history_command(&mut self) {
7783        let history_entries = self
7784            .state_container
7785            .command_history()
7786            .get_navigation_entries();
7787        let history_commands: Vec<String> =
7788            history_entries.iter().map(|e| e.command.clone()).collect();
7789
7790        if let Some(buffer) = self.state_container.buffers_mut().current_mut() {
7791            if buffer.navigate_history_down(&history_commands) {
7792                self.sync_all_input_states();
7793                self.state_container
7794                    .set_status_message("Next command from history".to_string());
7795            }
7796        }
7797    }
7798}
7799
7800// Implement NavigationBehavior trait for EnhancedTuiApp
7801impl NavigationBehavior for EnhancedTuiApp {
7802    fn viewport_manager(&self) -> &RefCell<Option<ViewportManager>> {
7803        &self.viewport_manager
7804    }
7805
7806    fn buffer_mut(&mut self) -> &mut dyn BufferAPI {
7807        self.state_container
7808            .current_buffer_mut()
7809            .expect("Buffer should exist")
7810    }
7811
7812    fn buffer(&self) -> &dyn BufferAPI {
7813        self.state_container
7814            .current_buffer()
7815            .expect("Buffer should exist")
7816    }
7817
7818    fn state_container(&self) -> &AppStateContainer {
7819        &self.state_container
7820    }
7821
7822    fn state_container_mut(&mut self) -> &mut AppStateContainer {
7823        &mut self.state_container
7824    }
7825
7826    fn get_row_count(&self) -> usize {
7827        self.get_row_count()
7828    }
7829
7830    fn set_mode_with_sync(&mut self, mode: AppMode, trigger: &str) {
7831        // Use the existing method that properly synchronizes both buffer and shadow_state
7832        self.set_mode_via_shadow_state(mode, trigger);
7833    }
7834}
7835
7836// Implement ColumnBehavior trait for EnhancedTuiApp
7837impl ColumnBehavior for EnhancedTuiApp {
7838    fn viewport_manager(&self) -> &RefCell<Option<ViewportManager>> {
7839        &self.viewport_manager
7840    }
7841
7842    fn buffer_mut(&mut self) -> &mut dyn BufferAPI {
7843        self.state_container
7844            .current_buffer_mut()
7845            .expect("Buffer should exist")
7846    }
7847
7848    fn buffer(&self) -> &dyn BufferAPI {
7849        self.state_container
7850            .current_buffer()
7851            .expect("Buffer should exist")
7852    }
7853
7854    fn state_container(&self) -> &AppStateContainer {
7855        &self.state_container
7856    }
7857
7858    fn is_in_results_mode(&self) -> bool {
7859        self.shadow_state.borrow().is_in_results_mode()
7860    }
7861}
7862
7863impl InputBehavior for EnhancedTuiApp {
7864    fn buffer_manager(&mut self) -> &mut BufferManager {
7865        self.state_container.buffers_mut()
7866    }
7867
7868    fn cursor_manager(&mut self) -> &mut CursorManager {
7869        &mut self.cursor_manager
7870    }
7871
7872    fn set_input_text_with_cursor(&mut self, text: String, cursor: usize) {
7873        self.set_input_text_with_cursor(text, cursor);
7874    }
7875
7876    fn state_container(&self) -> &AppStateContainer {
7877        &self.state_container
7878    }
7879
7880    fn state_container_mut(&mut self) -> &mut AppStateContainer {
7881        &mut self.state_container
7882    }
7883
7884    fn buffer_mut(&mut self) -> &mut dyn BufferAPI {
7885        self.state_container
7886            .current_buffer_mut()
7887            .expect("Buffer should exist")
7888    }
7889
7890    fn set_mode_with_sync(&mut self, mode: AppMode, trigger: &str) {
7891        // Use the existing method that properly synchronizes both buffer and shadow_state
7892        self.set_mode_via_shadow_state(mode, trigger);
7893    }
7894}
7895
7896impl YankBehavior for EnhancedTuiApp {
7897    fn buffer(&self) -> &dyn BufferAPI {
7898        self.state_container
7899            .current_buffer()
7900            .expect("Buffer should exist")
7901    }
7902
7903    fn buffer_mut(&mut self) -> &mut dyn BufferAPI {
7904        self.state_container
7905            .current_buffer_mut()
7906            .expect("Buffer should exist")
7907    }
7908
7909    fn state_container(&self) -> &AppStateContainer {
7910        &self.state_container
7911    }
7912
7913    fn set_status_message(&mut self, message: String) {
7914        self.state_container.set_status_message(message);
7915    }
7916
7917    fn set_error_status(&mut self, prefix: &str, error: anyhow::Error) {
7918        self.set_error_status(prefix, error);
7919    }
7920}
7921
7922impl BufferManagementBehavior for EnhancedTuiApp {
7923    fn buffer_manager(&mut self) -> &mut BufferManager {
7924        self.state_container.buffers_mut()
7925    }
7926
7927    fn buffer_handler(&mut self) -> &mut BufferHandler {
7928        &mut self.buffer_handler
7929    }
7930
7931    fn buffer(&self) -> &dyn BufferAPI {
7932        self.state_container
7933            .current_buffer()
7934            .expect("Buffer should exist")
7935    }
7936
7937    fn buffer_mut(&mut self) -> &mut dyn BufferAPI {
7938        self.state_container
7939            .current_buffer_mut()
7940            .expect("Buffer should exist")
7941    }
7942
7943    fn config(&self) -> &Config {
7944        &self.config
7945    }
7946
7947    fn cursor_manager(&mut self) -> &mut CursorManager {
7948        &mut self.cursor_manager
7949    }
7950
7951    fn set_input_text_with_cursor(&mut self, text: String, cursor: usize) {
7952        self.set_input_text_with_cursor(text, cursor);
7953    }
7954
7955    fn next_buffer(&mut self) -> String {
7956        // Save current viewport state to current buffer before switching
7957        self.save_viewport_to_current_buffer();
7958
7959        let result = self
7960            .buffer_handler
7961            .next_buffer(self.state_container.buffers_mut());
7962
7963        // Sync all state after buffer switch
7964        self.sync_after_buffer_switch();
7965
7966        result
7967    }
7968
7969    fn previous_buffer(&mut self) -> String {
7970        // Save current viewport state to current buffer before switching
7971        self.save_viewport_to_current_buffer();
7972
7973        let result = self
7974            .buffer_handler
7975            .previous_buffer(self.state_container.buffers_mut());
7976
7977        // Sync all state after buffer switch
7978        self.sync_after_buffer_switch();
7979
7980        result
7981    }
7982
7983    fn quick_switch_buffer(&mut self) -> String {
7984        // Save current viewport state to current buffer before switching
7985        self.save_viewport_to_current_buffer();
7986
7987        let result = self
7988            .buffer_handler
7989            .quick_switch(self.state_container.buffers_mut());
7990
7991        // Sync all state after buffer switch
7992        self.sync_after_buffer_switch();
7993
7994        result
7995    }
7996
7997    fn close_buffer(&mut self) -> (bool, String) {
7998        self.buffer_handler
7999            .close_buffer(self.state_container.buffers_mut())
8000    }
8001
8002    fn switch_to_buffer(&mut self, index: usize) -> String {
8003        // Save current viewport state to current buffer before switching
8004        self.save_viewport_to_current_buffer();
8005
8006        // Switch buffer
8007        let result = self
8008            .buffer_handler
8009            .switch_to_buffer(self.state_container.buffers_mut(), index);
8010
8011        // Sync all state after buffer switch
8012        self.sync_after_buffer_switch();
8013
8014        result
8015    }
8016
8017    fn buffer_count(&self) -> usize {
8018        self.state_container.buffers().all_buffers().len()
8019    }
8020
8021    fn current_buffer_index(&self) -> usize {
8022        self.state_container.buffers().current_index()
8023    }
8024}
8025
8026pub fn run_enhanced_tui_multi(api_url: &str, data_files: Vec<&str>) -> Result<()> {
8027    // Set up panic hook to restore terminal on panic
8028    let original_hook = std::panic::take_hook();
8029    std::panic::set_hook(Box::new(move |panic_info| {
8030        // Restore terminal
8031        let _ = disable_raw_mode();
8032        let _ = execute!(
8033            io::stdout(),
8034            LeaveAlternateScreen,
8035            DisableMouseCapture,
8036            crossterm::cursor::Show
8037        );
8038
8039        // Call the original panic hook
8040        original_hook(panic_info);
8041    }));
8042
8043    let app = if data_files.is_empty() {
8044        EnhancedTuiApp::new(api_url)
8045    } else {
8046        // Use ApplicationOrchestrator for clean data source separation
8047        use crate::services::ApplicationOrchestrator;
8048
8049        // Get config for orchestrator setup
8050        let config = Config::default();
8051        let orchestrator = ApplicationOrchestrator::new(
8052            config.behavior.case_insensitive_default,
8053            config.behavior.hide_empty_columns,
8054        );
8055
8056        // Load the first file through the orchestrator
8057        let mut app = orchestrator.create_tui_with_file(data_files[0])?;
8058
8059        // Load additional files into separate buffers
8060        for file_path in data_files.iter().skip(1) {
8061            if let Err(e) = orchestrator.load_additional_file(&mut app, file_path) {
8062                app.state_container
8063                    .set_status_message(format!("Error loading {file_path}: {e}"));
8064                continue;
8065            }
8066        }
8067
8068        // Switch back to the first buffer if we loaded multiple
8069        if data_files.len() > 1 {
8070            app.state_container.buffers_mut().switch_to(0);
8071            // Sync all state after buffer switch
8072            app.sync_after_buffer_switch();
8073            app.state_container.set_status_message(format!(
8074                "Loaded {} files into separate buffers. Use Alt+Tab to switch.",
8075                data_files.len()
8076            ));
8077        } else if data_files.len() == 1 {
8078            // Even for single file, ensure parser is initialized
8079            app.update_parser_for_current_buffer();
8080        }
8081
8082        app
8083    };
8084
8085    let result = app.run();
8086
8087    // Restore the original panic hook
8088    let _ = std::panic::take_hook(); // Drop our hook
8089                                     // Note: We can't restore the original hook here as it was moved into the closure
8090
8091    result
8092}
8093
8094pub fn run_enhanced_tui(api_url: &str, data_file: Option<&str>) -> Result<()> {
8095    // For backward compatibility, convert single file to vec and call multi version
8096    let files = if let Some(file) = data_file {
8097        vec![file]
8098    } else {
8099        vec![]
8100    };
8101    run_enhanced_tui_multi(api_url, files)
8102}