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 = StateCoordinator::apply_text_filter_with_refs(
4915            &mut self.state_container,
4916            &self.viewport_manager,
4917            pattern,
4918        );
4919
4920        // Update ViewportManager with the filtered DataView
4921        // Sync the dataview to both managers
4922        self.sync_dataview_to_managers();
4923
4924        // Decrement re-entrancy counter
4925        FILTER_DEPTH.fetch_sub(1, Ordering::SeqCst);
4926    }
4927    fn search_columns(&mut self) {
4928        // Safety: Prevent infinite recursion with a static counter
4929        static SEARCH_DEPTH: std::sync::atomic::AtomicUsize =
4930            std::sync::atomic::AtomicUsize::new(0);
4931        let depth = SEARCH_DEPTH.fetch_add(1, std::sync::atomic::Ordering::SeqCst);
4932
4933        // Guard against excessive recursion
4934        if depth > 10 {
4935            error!(target: "search", "Column search depth exceeded limit, aborting to prevent infinite loop");
4936            SEARCH_DEPTH.store(0, std::sync::atomic::Ordering::SeqCst);
4937            return;
4938        }
4939
4940        // Create a guard that will decrement on drop
4941        struct DepthGuard;
4942        impl Drop for DepthGuard {
4943            fn drop(&mut self) {
4944                SEARCH_DEPTH.fetch_sub(1, std::sync::atomic::Ordering::SeqCst);
4945            }
4946        }
4947        let _guard = DepthGuard;
4948
4949        let pattern = self.state_container.column_search().pattern.clone();
4950        debug!(target: "search", "search_columns called with pattern: '{}', depth: {}", pattern, depth);
4951
4952        if pattern.is_empty() {
4953            debug!(target: "search", "Pattern is empty, skipping column search");
4954            return;
4955        }
4956
4957        // Update DataView's column search and get matches
4958        let matching_columns = if let Some(dataview) =
4959            self.state_container.get_buffer_dataview_mut()
4960        {
4961            dataview.search_columns(&pattern);
4962
4963            // Get matching columns from DataView
4964            let matches = dataview.get_matching_columns().to_vec();
4965            debug!(target: "search", "DataView found {} matching columns", matches.len());
4966            if !matches.is_empty() {
4967                for (idx, (col_idx, col_name)) in matches.iter().enumerate() {
4968                    debug!(target: "search", "  Match {}: '{}' at visual index {}", idx + 1, col_name, col_idx);
4969                }
4970            }
4971
4972            // Also sync with AppStateContainer for compatibility
4973            let columns: Vec<(String, usize)> = matches
4974                .iter()
4975                .map(|(idx, name)| (name.clone(), *idx))
4976                .collect();
4977            self.state_container
4978                .update_column_search_matches(&columns, &pattern);
4979
4980            matches
4981        } else {
4982            debug!(target: "search", "No DataView available for column search");
4983            Vec::new()
4984        };
4985
4986        if matching_columns.is_empty() {
4987            let status_msg = format!("No columns matching '{pattern}'");
4988            debug!(target: "search", "Setting status: {}", status_msg);
4989            self.state_container.set_status_message(status_msg);
4990        } else {
4991            // Move to first match - the index from DataView is already a VISUAL index
4992            let first_match_visual_idx = matching_columns[0].0;
4993            let first_match_name = &matching_columns[0].1;
4994
4995            // Convert visual index to DataTable index for Buffer/AppStateContainer (legacy compatibility)
4996            let datatable_idx = if let Some(dataview) = self.state_container.get_buffer_dataview() {
4997                let display_columns = dataview.get_display_columns();
4998                if first_match_visual_idx < display_columns.len() {
4999                    display_columns[first_match_visual_idx]
5000                } else {
5001                    first_match_visual_idx // Fallback
5002                }
5003            } else {
5004                first_match_visual_idx
5005            };
5006
5007            self.state_container.set_current_column(datatable_idx);
5008            self.state_container
5009                .set_current_column_buffer(datatable_idx);
5010
5011            // Update viewport to show the first match using ViewportManager
5012            // ViewportManager expects VISUAL index
5013            {
5014                let mut viewport_manager_borrow = self.viewport_manager.borrow_mut();
5015                if let Some(viewport_manager) = viewport_manager_borrow.as_mut() {
5016                    let viewport_changed =
5017                        viewport_manager.set_current_column(first_match_visual_idx);
5018
5019                    // Sync navigation state with updated viewport
5020                    if viewport_changed {
5021                        let new_viewport = viewport_manager.viewport_cols().clone();
5022                        let pinned_count =
5023                            if let Some(dv) = self.state_container.get_buffer_dataview() {
5024                                dv.get_pinned_columns().len()
5025                            } else {
5026                                0
5027                            };
5028                        let scrollable_offset = new_viewport.start.saturating_sub(pinned_count);
5029                        self.state_container.navigation_mut().scroll_offset.1 = scrollable_offset;
5030
5031                        debug!(target: "navigation",
5032                            "Column search initial: Jumped to column {} '{}', viewport adjusted to {:?}",
5033                            first_match_visual_idx, first_match_name, new_viewport);
5034                    }
5035                }
5036            }
5037
5038            debug!(target: "search", "Setting current column to visual index {} ('{}')",
5039                   first_match_visual_idx, first_match_name);
5040            let status_msg = format!(
5041                "Found {} columns matching '{}'. Tab/Shift-Tab to navigate.",
5042                matching_columns.len(),
5043                pattern
5044            );
5045            debug!(target: "search", "Setting status: {}", status_msg);
5046            self.state_container.set_status_message(status_msg);
5047
5048            // Column search matches are now managed by AppStateContainer
5049        }
5050
5051        // Matching columns are now stored in AppStateContainer
5052    }
5053
5054    fn next_column_match(&mut self) {
5055        // Use DataView's column search navigation
5056        // Extract all needed data first to avoid borrow conflicts
5057        let column_match_data =
5058            if let Some(dataview) = self.state_container.get_buffer_dataview_mut() {
5059                if let Some(visual_idx) = dataview.next_column_match() {
5060                    // Get the column name and match info
5061                    let matching_columns = dataview.get_matching_columns();
5062                    let current_match_index = dataview.current_column_match_index();
5063                    let current_match = current_match_index + 1;
5064                    let total_matches = matching_columns.len();
5065                    let col_name = matching_columns
5066                        .get(current_match_index)
5067                        .map(|(_, name)| name.clone())
5068                        .unwrap_or_default();
5069
5070                    // Convert visual index to DataTable index for Buffer/AppStateContainer
5071                    // (they still use DataTable indices for now)
5072                    let display_columns = dataview.get_display_columns();
5073                    let datatable_idx = if visual_idx < display_columns.len() {
5074                        display_columns[visual_idx]
5075                    } else {
5076                        visual_idx // Fallback
5077                    };
5078
5079                    Some((
5080                        visual_idx,
5081                        datatable_idx,
5082                        col_name,
5083                        current_match,
5084                        total_matches,
5085                        current_match_index,
5086                    ))
5087                } else {
5088                    None
5089                }
5090            } else {
5091                None
5092            };
5093
5094        // Now process the match data without holding dataview reference
5095        if let Some((
5096            visual_idx,
5097            datatable_idx,
5098            col_name,
5099            current_match,
5100            total_matches,
5101            current_match_index,
5102        )) = column_match_data
5103        {
5104            // Update both AppStateContainer and Buffer with DataTable index (for legacy compatibility)
5105            self.state_container.set_current_column(datatable_idx);
5106            self.state_container
5107                .set_current_column_buffer(datatable_idx);
5108
5109            // Update viewport to show the column using ViewportManager
5110            // ViewportManager's set_current_column now expects VISUAL index
5111            {
5112                let mut viewport_manager_borrow = self.viewport_manager.borrow_mut();
5113                if let Some(viewport_manager) = viewport_manager_borrow.as_mut() {
5114                    viewport_manager.set_current_column(visual_idx);
5115                }
5116            }
5117
5118            // Always sync navigation state with updated viewport (like vim search)
5119            debug!(target: "column_search_sync", "next_column_match: About to call sync_navigation_with_viewport() - visual_idx: {}, datatable_idx: {}", visual_idx, datatable_idx);
5120            debug!(target: "column_search_sync", "next_column_match: Pre-sync - viewport current_column: {}", 
5121                if let Ok(vm) = self.viewport_manager.try_borrow() {
5122                    vm.as_ref().map_or(0, super::viewport_manager::ViewportManager::get_crosshair_col)
5123                } else { 0 });
5124            self.sync_navigation_with_viewport();
5125            debug!(target: "column_search_sync", "next_column_match: Post-sync - navigation current_column: {}", 
5126                self.state_container.navigation().selected_column);
5127            debug!(target: "column_search_sync", "next_column_match: sync_navigation_with_viewport() completed");
5128
5129            debug!(target: "navigation",
5130                "Column search: Jumped to visual column {} (datatable: {}) '{}', synced with viewport",
5131                visual_idx, datatable_idx, col_name);
5132
5133            // CRITICAL: Update AppStateContainer's column_search.current_match
5134            // This ensures Enter key will jump to the correct column
5135            {
5136                let mut column_search = self.state_container.column_search_mut();
5137                column_search.current_match = current_match_index;
5138                debug!(target: "column_search_sync", "next_column_match: Updated AppStateContainer column_search.current_match to {}", column_search.current_match);
5139            }
5140
5141            self.state_container.set_status_message(format!(
5142                "Column {current_match}/{total_matches}: {col_name} - Tab/Shift-Tab to navigate"
5143            ));
5144        }
5145    }
5146
5147    fn previous_column_match(&mut self) {
5148        // Use DataView's column search navigation
5149        // Extract all needed data first to avoid borrow conflicts
5150        let column_match_data =
5151            if let Some(dataview) = self.state_container.get_buffer_dataview_mut() {
5152                if let Some(visual_idx) = dataview.prev_column_match() {
5153                    // Get the column name and match info
5154                    let matching_columns = dataview.get_matching_columns();
5155                    let current_match_index = dataview.current_column_match_index();
5156                    let current_match = current_match_index + 1;
5157                    let total_matches = matching_columns.len();
5158                    let col_name = matching_columns
5159                        .get(current_match_index)
5160                        .map(|(_, name)| name.clone())
5161                        .unwrap_or_default();
5162
5163                    // Convert visual index to DataTable index for Buffer/AppStateContainer
5164                    // (they still use DataTable indices for now)
5165                    let display_columns = dataview.get_display_columns();
5166                    let datatable_idx = if visual_idx < display_columns.len() {
5167                        display_columns[visual_idx]
5168                    } else {
5169                        visual_idx // Fallback
5170                    };
5171
5172                    Some((
5173                        visual_idx,
5174                        datatable_idx,
5175                        col_name,
5176                        current_match,
5177                        total_matches,
5178                        current_match_index,
5179                    ))
5180                } else {
5181                    None
5182                }
5183            } else {
5184                None
5185            };
5186
5187        // Now process the match data without holding dataview reference
5188        if let Some((
5189            visual_idx,
5190            datatable_idx,
5191            col_name,
5192            current_match,
5193            total_matches,
5194            current_match_index,
5195        )) = column_match_data
5196        {
5197            // Update both AppStateContainer and Buffer with DataTable index (for legacy compatibility)
5198            self.state_container.set_current_column(datatable_idx);
5199            self.state_container
5200                .set_current_column_buffer(datatable_idx);
5201
5202            // Update viewport to show the column using ViewportManager
5203            // ViewportManager's set_current_column now expects VISUAL index
5204            {
5205                let mut viewport_manager_borrow = self.viewport_manager.borrow_mut();
5206                if let Some(viewport_manager) = viewport_manager_borrow.as_mut() {
5207                    viewport_manager.set_current_column(visual_idx);
5208                }
5209            }
5210
5211            // Always sync navigation state with updated viewport (like vim search)
5212            debug!(target: "column_search_sync", "previous_column_match: About to call sync_navigation_with_viewport() - visual_idx: {}, datatable_idx: {}", visual_idx, datatable_idx);
5213            debug!(target: "column_search_sync", "previous_column_match: Pre-sync - viewport current_column: {}", 
5214                if let Ok(vm) = self.viewport_manager.try_borrow() {
5215                    vm.as_ref().map_or(0, super::viewport_manager::ViewportManager::get_crosshair_col)
5216                } else { 0 });
5217            self.sync_navigation_with_viewport();
5218            debug!(target: "column_search_sync", "previous_column_match: Post-sync - navigation current_column: {}", 
5219                self.state_container.navigation().selected_column);
5220            debug!(target: "column_search_sync", "previous_column_match: sync_navigation_with_viewport() completed");
5221
5222            debug!(target: "navigation",
5223                "Column search (prev): Jumped to visual column {} (datatable: {}) '{}', synced with viewport",
5224                visual_idx, datatable_idx, col_name);
5225
5226            // CRITICAL: Update AppStateContainer's column_search.current_match
5227            // This ensures Enter key will jump to the correct column
5228            {
5229                let mut column_search = self.state_container.column_search_mut();
5230                column_search.current_match = current_match_index;
5231                debug!(target: "column_search_sync", "previous_column_match: Updated AppStateContainer column_search.current_match to {}", column_search.current_match);
5232            }
5233
5234            self.state_container.set_status_message(format!(
5235                "Column {current_match}/{total_matches}: {col_name} - Tab/Shift-Tab to navigate"
5236            ));
5237        }
5238    }
5239
5240    fn apply_fuzzy_filter(&mut self) {
5241        info!(
5242            "apply_fuzzy_filter called on thread {:?}",
5243            std::thread::current().id()
5244        );
5245
5246        // Delegate all state coordination to StateCoordinator
5247        use crate::ui::state::state_coordinator::StateCoordinator;
5248        let (_match_count, indices) = StateCoordinator::apply_fuzzy_filter_with_refs(
5249            &mut self.state_container,
5250            &self.viewport_manager,
5251        );
5252
5253        // Update fuzzy filter indices for compatibility
5254        self.state_container.set_fuzzy_filter_indices(indices);
5255
5256        // Update ViewportManager with the filtered DataView
5257        // Sync the dataview to both managers
5258        self.sync_dataview_to_managers();
5259    }
5260
5261    fn toggle_sort_current_column(&mut self) {
5262        // Get visual column index from ViewportManager's crosshair
5263        let visual_col_idx = if let Some(ref viewport_manager) = *self.viewport_manager.borrow() {
5264            viewport_manager.get_crosshair_col()
5265        } else {
5266            0
5267        };
5268
5269        if let Some(dataview) = self.state_container.get_buffer_dataview_mut() {
5270            // DataView.toggle_sort expects VISIBLE column index
5271            // Get column name for display
5272            let column_names = dataview.column_names();
5273            let col_name = column_names
5274                .get(visual_col_idx)
5275                .cloned()
5276                .unwrap_or_else(|| format!("Column {visual_col_idx}"));
5277
5278            debug!(
5279                "toggle_sort_current_column: visual_idx={}, column_name={}",
5280                visual_col_idx, col_name
5281            );
5282
5283            if let Err(e) = dataview.toggle_sort(visual_col_idx) {
5284                self.state_container
5285                    .set_status_message(format!("Sort error: {e}"));
5286            } else {
5287                // Get the new sort state for status message
5288                let sort_state = dataview.get_sort_state();
5289                let message = match sort_state.order {
5290                    crate::data::data_view::SortOrder::Ascending => {
5291                        format!("Sorted '{col_name}' ascending ↑")
5292                    }
5293                    crate::data::data_view::SortOrder::Descending => {
5294                        format!("Sorted '{col_name}' descending ↓")
5295                    }
5296                    crate::data::data_view::SortOrder::None => {
5297                        format!("Cleared sort on '{col_name}'")
5298                    }
5299                };
5300                self.state_container.set_status_message(message);
5301
5302                // Update ViewportManager with the sorted DataView to keep them in sync
5303                if let Some(updated_dataview) = self.state_container.get_buffer_dataview() {
5304                    // Update TableWidgetManager with the sorted dataview as well
5305                    self.table_widget_manager
5306                        .borrow_mut()
5307                        .set_dataview(Arc::new(updated_dataview.clone()));
5308
5309                    let mut viewport_manager_borrow = self.viewport_manager.borrow_mut();
5310                    if let Some(ref mut viewport_manager) = *viewport_manager_borrow {
5311                        viewport_manager.set_dataview(Arc::new(updated_dataview.clone()));
5312                        debug!("Updated ViewportManager with sorted DataView");
5313                    }
5314                }
5315            }
5316        } else {
5317            // Could not find display position in DataTable
5318            self.state_container
5319                .set_status_message("Error: Invalid column position".to_string());
5320        }
5321    }
5322
5323    fn get_current_data(&self) -> Option<&DataView> {
5324        self.state_container.get_buffer_dataview()
5325    }
5326
5327    fn get_row_count(&self) -> usize {
5328        // Check if fuzzy filter is active first (most specific filter)
5329        if self.state_container.is_fuzzy_filter_active() {
5330            // Return the count of fuzzy filtered indices
5331            self.state_container.get_fuzzy_filter_indices().len()
5332        } else if let Some(dataview) = self.state_container.get_buffer_dataview() {
5333            // Return count from WHERE clause or other filters
5334            dataview.row_count()
5335        } else if let Some(provider) = self.get_data_provider() {
5336            // Use DataProvider trait for data access (migration step)
5337            provider.get_row_count()
5338        } else {
5339            0
5340        }
5341    }
5342
5343    /// Helper to sync dataview to both `ViewportManager` and `TableWidgetManager`
5344    fn sync_dataview_to_managers(&self) {
5345        if let Some(dataview) = self.state_container.get_buffer_dataview() {
5346            let arc_dataview = Arc::new(dataview.clone());
5347
5348            // Update ViewportManager
5349            if let Some(ref mut viewport_manager) = *self.viewport_manager.borrow_mut() {
5350                viewport_manager.set_dataview(arc_dataview.clone());
5351                debug!(
5352                    "Updated ViewportManager with DataView (row_count={})",
5353                    arc_dataview.row_count()
5354                );
5355            }
5356
5357            // Update TableWidgetManager
5358            self.table_widget_manager
5359                .borrow_mut()
5360                .set_dataview(arc_dataview);
5361            debug!("Updated TableWidgetManager with DataView");
5362        }
5363    }
5364
5365    pub fn reset_table_state(&mut self) {
5366        // Delegate all state reset coordination to StateCoordinator
5367        use crate::ui::state::state_coordinator::StateCoordinator;
5368        StateCoordinator::reset_table_state_with_refs(
5369            &mut self.state_container,
5370            &self.viewport_manager,
5371        );
5372    }
5373
5374    fn update_parser_for_current_buffer(&mut self) {
5375        // Sync input states
5376        self.sync_all_input_states();
5377
5378        // Delegate parser update to StateCoordinator
5379        use crate::ui::state::state_coordinator::StateCoordinator;
5380        StateCoordinator::update_parser_with_refs(&self.state_container, &mut self.hybrid_parser);
5381    }
5382
5383    /// Synchronize all state after buffer switch
5384    /// This should be called after any buffer switch operation to ensure:
5385    /// 1. Viewport is restored from the new buffer
5386    /// 2. Parser schema is updated with the new buffer's columns
5387    fn sync_after_buffer_switch(&mut self) {
5388        // Restore viewport state from new buffer
5389        self.restore_viewport_from_current_buffer();
5390
5391        // Update parser schema for the new buffer
5392        self.update_parser_for_current_buffer();
5393    }
5394
5395    /// Update `ViewportManager` when `DataView` changes
5396    fn update_viewport_manager(&mut self, dataview: Option<DataView>) {
5397        if let Some(dv) = dataview {
5398            // Get current column position to preserve it
5399            let current_column = self.state_container.get_current_column();
5400
5401            // Create new ViewportManager with the new DataView
5402            let mut new_viewport_manager = ViewportManager::new(Arc::new(dv));
5403
5404            // Update terminal size from current terminal
5405            if let Ok((width, height)) = crossterm::terminal::size() {
5406                // Calculate the actual data area height
5407                let data_rows_available = Self::calculate_available_data_rows(height);
5408                new_viewport_manager.update_terminal_size(width, data_rows_available);
5409                debug!(
5410                    "Updated new ViewportManager terminal size: {}x{} (data rows)",
5411                    width, data_rows_available
5412                );
5413            }
5414
5415            // Set the current column position to ensure proper viewport initialization
5416            // This is crucial for SELECT queries that subset columns
5417            if current_column < new_viewport_manager.dataview().column_count() {
5418                new_viewport_manager.set_current_column(current_column);
5419            } else {
5420                // If current column is out of bounds, reset to first column
5421                new_viewport_manager.set_current_column(0);
5422                self.state_container.set_current_column_buffer(0);
5423            }
5424
5425            *self.viewport_manager.borrow_mut() = Some(new_viewport_manager);
5426            debug!(
5427                "ViewportManager updated with new DataView, current_column={}",
5428                current_column
5429            );
5430        } else {
5431            // Clear ViewportManager if no DataView
5432            *self.viewport_manager.borrow_mut() = None;
5433            debug!("ViewportManager cleared (no DataView)");
5434        }
5435    }
5436
5437    pub fn calculate_optimal_column_widths(&mut self) {
5438        // Delegate to ViewportManager for optimal column width calculations
5439        let widths_from_viewport = {
5440            let mut viewport_opt = self.viewport_manager.borrow_mut();
5441            (*viewport_opt)
5442                .as_mut()
5443                .map(super::viewport_manager::ViewportManager::calculate_optimal_column_widths)
5444        };
5445
5446        if let Some(widths) = widths_from_viewport {
5447            self.state_container.set_column_widths(widths);
5448        }
5449    }
5450
5451    /// Centralized method for setting status messages
5452    /// Ensures consistent logging and state synchronization
5453    pub fn set_status_message(&mut self, message: impl Into<String>) {
5454        let msg = message.into();
5455        debug!("Status: {}", msg);
5456        self.state_container.set_status_message(msg.clone());
5457        // Future: Could also sync to state_container if needed
5458        // self.state_container.set_status(msg);
5459    }
5460
5461    /// Set error status message with consistent formatting
5462    fn set_error_status(&mut self, context: &str, error: impl std::fmt::Display) {
5463        let msg = format!("{context}: {error}");
5464        debug!("Error status: {}", msg);
5465        self.set_status_message(msg);
5466    }
5467
5468    fn export_to_csv(&mut self) {
5469        let result = {
5470            let ctx = crate::ui::operations::data_export_operations::DataExportContext {
5471                data_provider: self.get_data_provider(),
5472            };
5473            crate::ui::operations::data_export_operations::export_to_csv(&ctx)
5474        };
5475
5476        match result {
5477            crate::ui::operations::data_export_operations::ExportResult::Success(message) => {
5478                self.set_status_message(message);
5479            }
5480            crate::ui::operations::data_export_operations::ExportResult::Error(error) => {
5481                self.set_error_status("Export failed", error);
5482            }
5483        }
5484    }
5485
5486    // ========== YANK OPERATIONS ==========
5487
5488    // Yank operations are provided by the YankBehavior trait in traits/yank_ops.rs
5489    // The trait provides: yank_cell, yank_row, yank_column, yank_all, yank_query,
5490    // yank_as_test_case, and yank_debug_with_context
5491
5492    fn paste_from_clipboard(&mut self) {
5493        // Paste from system clipboard into the current input field
5494        match self.state_container.read_from_clipboard() {
5495            Ok(text) => {
5496                let mode = self.shadow_state.borrow().get_mode();
5497                match mode {
5498                    AppMode::Command => {
5499                        // Always use single-line mode paste
5500                        // Get current cursor position
5501                        let cursor_pos = self.get_input_cursor();
5502                        let current_value = self.get_input_text();
5503
5504                        // Insert at cursor position
5505                        let mut new_value = String::new();
5506                        new_value.push_str(&current_value[..cursor_pos]);
5507                        new_value.push_str(&text);
5508                        new_value.push_str(&current_value[cursor_pos..]);
5509
5510                        self.set_input_text_with_cursor(new_value, cursor_pos + text.len());
5511
5512                        self.state_container
5513                            .set_status_message(format!("Pasted {} characters", text.len()));
5514                    }
5515                    AppMode::Filter
5516                    | AppMode::FuzzyFilter
5517                    | AppMode::Search
5518                    | AppMode::ColumnSearch => {
5519                        // For search/filter modes, append to current pattern
5520                        let cursor_pos = self.get_input_cursor();
5521                        let current_value = self.get_input_text();
5522
5523                        let mut new_value = String::new();
5524                        new_value.push_str(&current_value[..cursor_pos]);
5525                        new_value.push_str(&text);
5526                        new_value.push_str(&current_value[cursor_pos..]);
5527
5528                        self.set_input_text_with_cursor(new_value, cursor_pos + text.len());
5529
5530                        // Update the appropriate filter/search state (reuse the mode we already have)
5531                        match mode {
5532                            AppMode::Filter => {
5533                                let pattern = self.get_input_text();
5534                                self.state_container.filter_mut().pattern = pattern.clone();
5535                                self.apply_filter(&pattern);
5536                            }
5537                            AppMode::FuzzyFilter => {
5538                                let input_text = self.get_input_text();
5539                                self.state_container.set_fuzzy_filter_pattern(input_text);
5540                                self.apply_fuzzy_filter();
5541                            }
5542                            AppMode::Search => {
5543                                let search_text = self.get_input_text();
5544                                self.state_container.set_search_pattern(search_text);
5545                                // TODO: self.search_in_results();
5546                            }
5547                            AppMode::ColumnSearch => {
5548                                let input_text = self.get_input_text();
5549                                self.state_container.start_column_search(input_text);
5550                                // Column search pattern is now in AppStateContainer
5551                            }
5552                            _ => {}
5553                        }
5554                    }
5555                    _ => {
5556                        self.state_container
5557                            .set_status_message("Paste not available in this mode".to_string());
5558                    }
5559                }
5560            }
5561            Err(e) => {
5562                self.state_container
5563                    .set_status_message(format!("Failed to paste: {e}"));
5564            }
5565        }
5566    }
5567
5568    fn export_to_json(&mut self) {
5569        // TODO: Handle filtered data in future DataView implementation
5570        let result = {
5571            let ctx = crate::ui::operations::data_export_operations::DataExportContext {
5572                data_provider: self.get_data_provider(),
5573            };
5574            crate::ui::operations::data_export_operations::export_to_json(&ctx)
5575        };
5576
5577        match result {
5578            crate::ui::operations::data_export_operations::ExportResult::Success(message) => {
5579                self.set_status_message(message);
5580            }
5581            crate::ui::operations::data_export_operations::ExportResult::Error(error) => {
5582                self.set_error_status("Export failed", error);
5583            }
5584        }
5585    }
5586
5587    fn get_horizontal_scroll_offset(&self) -> u16 {
5588        // Delegate to cursor_manager (incremental refactoring)
5589        let (horizontal, _vertical) = self.cursor_manager.scroll_offsets();
5590        horizontal
5591    }
5592
5593    fn update_horizontal_scroll(&mut self, terminal_width: u16) {
5594        let inner_width = terminal_width.saturating_sub(3) as usize; // Account for borders + 1 char padding
5595        let cursor_pos = self.get_input_cursor();
5596
5597        // Update cursor_manager scroll (incremental refactoring)
5598        self.cursor_manager
5599            .update_horizontal_scroll(cursor_pos, terminal_width.saturating_sub(3));
5600
5601        // Update scroll state in container
5602        let mut scroll = self.state_container.scroll_mut();
5603        if cursor_pos < scroll.input_scroll_offset as usize {
5604            scroll.input_scroll_offset = cursor_pos as u16;
5605        }
5606        // If cursor is after the scroll window, scroll right
5607        else if cursor_pos >= scroll.input_scroll_offset as usize + inner_width {
5608            scroll.input_scroll_offset = (cursor_pos + 1).saturating_sub(inner_width) as u16;
5609        }
5610    }
5611
5612    fn get_cursor_token_position(&self) -> (usize, usize) {
5613        let ctx = crate::ui::operations::simple_operations::TextNavigationContext {
5614            query: &self.get_input_text(),
5615            cursor_pos: self.get_input_cursor(),
5616        };
5617        crate::ui::operations::simple_operations::get_cursor_token_position(&ctx)
5618    }
5619
5620    fn get_token_at_cursor(&self) -> Option<String> {
5621        let ctx = crate::ui::operations::simple_operations::TextNavigationContext {
5622            query: &self.get_input_text(),
5623            cursor_pos: self.get_input_cursor(),
5624        };
5625        crate::ui::operations::simple_operations::get_token_at_cursor(&ctx)
5626    }
5627
5628    /// Debug method to dump current buffer state (disabled to prevent TUI corruption)
5629    #[allow(dead_code)]
5630    fn ui(&mut self, f: &mut Frame) {
5631        // Always use single-line mode input height
5632        let input_height = INPUT_AREA_HEIGHT;
5633
5634        // Always show tab bar for consistent layout
5635        let buffer_count = self.state_container.buffers().all_buffers().len();
5636        let tab_bar_height = 2; // Always reserve space for tab bar
5637
5638        let chunks = Layout::default()
5639            .direction(Direction::Vertical)
5640            .constraints(
5641                [
5642                    Constraint::Length(tab_bar_height),    // Tab bar (always shown)
5643                    Constraint::Length(input_height),      // Command input area
5644                    Constraint::Min(0),                    // Results
5645                    Constraint::Length(STATUS_BAR_HEIGHT), // Status bar
5646                ]
5647                .as_ref(),
5648            )
5649            .split(f.area());
5650
5651        // Always render tab bar (even with single buffer)
5652        if buffer_count > 0 {
5653            let buffer_names: Vec<String> = self
5654                .state_container
5655                .buffers()
5656                .all_buffers()
5657                .iter()
5658                .map(super::super::buffer::BufferAPI::get_name)
5659                .collect();
5660            let current_index = self.state_container.buffers().current_index();
5661
5662            let tab_widget = TabBarWidget::new(current_index, buffer_names);
5663            tab_widget.render(f, chunks[0]);
5664        }
5665
5666        // Fixed chunk indices since tab bar is always present
5667        let input_chunk_idx = 1;
5668        let results_chunk_idx = 2;
5669        let status_chunk_idx = 3;
5670
5671        // Update horizontal scroll based on actual terminal width
5672        self.update_horizontal_scroll(chunks[input_chunk_idx].width);
5673
5674        // Command input area
5675        // Get the current input text length and cursor position for display
5676        let input_text_for_count = self.get_input_text();
5677        let char_count = input_text_for_count.len();
5678        let cursor_pos = self.get_input_cursor();
5679        let char_count_display = if char_count > 0 {
5680            format!(" [{cursor_pos}/{char_count} chars]")
5681        } else {
5682            String::new()
5683        };
5684
5685        let scroll_offset = self.get_horizontal_scroll_offset();
5686        let scroll_indicator = if scroll_offset > 0 {
5687            " ◀ " // Indicate text is scrolled (text hidden to the left)
5688        } else {
5689            ""
5690        };
5691
5692        let input_title = match self.shadow_state.borrow().get_mode() {
5693            AppMode::Command => format!("SQL Query{char_count_display}{scroll_indicator}"),
5694            AppMode::Results => format!(
5695                "SQL Query (Results Mode - Press ↑ to edit){char_count_display}{scroll_indicator}"
5696            ),
5697            AppMode::Search => format!("Search Pattern{char_count_display}{scroll_indicator}"),
5698            AppMode::Filter => format!("Filter Pattern{char_count_display}{scroll_indicator}"),
5699            AppMode::FuzzyFilter => {
5700                format!("Fuzzy Filter{char_count_display}{scroll_indicator}")
5701            }
5702            AppMode::ColumnSearch => {
5703                format!("Column Search{char_count_display}{scroll_indicator}")
5704            }
5705            AppMode::Help => "Help".to_string(),
5706            AppMode::History => {
5707                let query = self.state_container.history_search().query.clone();
5708                format!("History Search: '{query}' (Esc to cancel)")
5709            }
5710            AppMode::Debug => "Parser Debug (F5)".to_string(),
5711            AppMode::PrettyQuery => "Pretty Query View (F6)".to_string(),
5712            AppMode::JumpToRow => format!("Jump to row: {}", self.get_jump_to_row_input()),
5713            AppMode::ColumnStats => "Column Statistics (S to close)".to_string(),
5714        };
5715
5716        let input_block = Block::default().borders(Borders::ALL).title(input_title);
5717
5718        // Check if we should use the search modes widget for rendering
5719        let use_search_widget = matches!(
5720            self.shadow_state.borrow().get_mode(),
5721            AppMode::Search | AppMode::Filter | AppMode::FuzzyFilter | AppMode::ColumnSearch
5722        ) && self.search_modes_widget.is_active();
5723
5724        if use_search_widget {
5725            // Let the search modes widget render the input field with debounce indicator
5726            self.search_modes_widget.render(f, chunks[input_chunk_idx]);
5727        } else {
5728            // Always get input text through the buffer API for consistency
5729            let input_text_string = self.get_input_text();
5730
5731            // Debug log to track rendering issues
5732            trace!(target: "render", "Rendering input: text='{}', mode={:?}, cursor={}",
5733                   if input_text_string.len() > 50 {
5734                       format!("{}...", &input_text_string[..50])
5735                   } else {
5736                       input_text_string.clone()
5737                   },
5738                   self.shadow_state.borrow().get_mode(),
5739                   self.get_input_cursor());
5740
5741            // Get history search query if in history mode
5742            let history_query_string = if self.shadow_state.borrow().is_in_history_mode() {
5743                self.state_container.history_search().query.clone()
5744            } else {
5745                String::new()
5746            };
5747
5748            let input_text = match self.shadow_state.borrow().get_mode() {
5749                AppMode::History => &history_query_string,
5750                _ => &input_text_string,
5751            };
5752
5753            let input_paragraph = match self.shadow_state.borrow().get_mode() {
5754                AppMode::Command => {
5755                    match self.state_container.get_edit_mode() {
5756                        Some(EditMode::SingleLine) => {
5757                            // Use syntax highlighting for SQL command input with horizontal scrolling
5758                            let highlighted_line =
5759                                self.sql_highlighter.simple_sql_highlight(input_text);
5760                            Paragraph::new(Text::from(vec![highlighted_line]))
5761                                .block(input_block)
5762                                .scroll((0, self.get_horizontal_scroll_offset()))
5763                        }
5764                        Some(EditMode::MultiLine) => {
5765                            // MultiLine mode is no longer supported, always use single-line
5766                            let highlighted_line =
5767                                self.sql_highlighter.simple_sql_highlight(input_text);
5768                            Paragraph::new(Text::from(vec![highlighted_line]))
5769                                .block(input_block)
5770                                .scroll((0, self.get_horizontal_scroll_offset()))
5771                        }
5772                        None => {
5773                            // Default to single-line mode
5774                            let highlighted_line =
5775                                self.sql_highlighter.simple_sql_highlight(input_text);
5776                            Paragraph::new(Text::from(vec![highlighted_line]))
5777                                .block(input_block)
5778                                .scroll((0, self.get_horizontal_scroll_offset()))
5779                        }
5780                    }
5781                }
5782                _ => {
5783                    // Plain text for other modes
5784                    Paragraph::new(input_text.as_str())
5785                        .block(input_block)
5786                        .style(match self.shadow_state.borrow().get_mode() {
5787                            AppMode::Results => Style::default().fg(Color::DarkGray),
5788                            AppMode::Search => Style::default().fg(Color::Yellow),
5789                            AppMode::Filter => Style::default().fg(Color::Cyan),
5790                            AppMode::FuzzyFilter => Style::default().fg(Color::Magenta),
5791                            AppMode::ColumnSearch => Style::default().fg(Color::Green),
5792                            AppMode::Help => Style::default().fg(Color::DarkGray),
5793                            AppMode::History => Style::default().fg(Color::Magenta),
5794                            AppMode::Debug => Style::default().fg(Color::Yellow),
5795                            AppMode::PrettyQuery => Style::default().fg(Color::Green),
5796                            AppMode::JumpToRow => Style::default().fg(Color::Magenta),
5797                            AppMode::ColumnStats => Style::default().fg(Color::Cyan),
5798                            _ => Style::default(),
5799                        })
5800                        .scroll((0, self.get_horizontal_scroll_offset()))
5801                }
5802            };
5803
5804            // Render the input paragraph (single-line mode)
5805            f.render_widget(input_paragraph, chunks[input_chunk_idx]);
5806        }
5807        let results_area = chunks[results_chunk_idx];
5808
5809        // Set cursor position for input modes (skip if search widget is handling it)
5810        if !use_search_widget {
5811            match self.shadow_state.borrow().get_mode() {
5812                AppMode::Command => {
5813                    // Always use single-line cursor handling
5814                    // Calculate cursor position with horizontal scrolling
5815                    let inner_width = chunks[input_chunk_idx].width.saturating_sub(2) as usize;
5816                    let cursor_pos = self.get_visual_cursor().1; // Get column position for single-line
5817                    let scroll_offset = self.get_horizontal_scroll_offset() as usize;
5818
5819                    // Calculate visible cursor position
5820                    if cursor_pos >= scroll_offset && cursor_pos < scroll_offset + inner_width {
5821                        let visible_pos = cursor_pos - scroll_offset;
5822                        f.set_cursor_position((
5823                            chunks[input_chunk_idx].x + visible_pos as u16 + 1,
5824                            chunks[input_chunk_idx].y + 1,
5825                        ));
5826                    }
5827                }
5828                AppMode::Search => {
5829                    f.set_cursor_position((
5830                        chunks[input_chunk_idx].x + self.get_input_cursor() as u16 + 1,
5831                        chunks[input_chunk_idx].y + 1,
5832                    ));
5833                }
5834                AppMode::Filter => {
5835                    f.set_cursor_position((
5836                        chunks[input_chunk_idx].x + self.get_input_cursor() as u16 + 1,
5837                        chunks[input_chunk_idx].y + 1,
5838                    ));
5839                }
5840                AppMode::FuzzyFilter => {
5841                    f.set_cursor_position((
5842                        chunks[input_chunk_idx].x + self.get_input_cursor() as u16 + 1,
5843                        chunks[input_chunk_idx].y + 1,
5844                    ));
5845                }
5846                AppMode::ColumnSearch => {
5847                    f.set_cursor_position((
5848                        chunks[input_chunk_idx].x + self.get_input_cursor() as u16 + 1,
5849                        chunks[input_chunk_idx].y + 1,
5850                    ));
5851                }
5852                AppMode::JumpToRow => {
5853                    f.set_cursor_position((
5854                        chunks[input_chunk_idx].x + self.get_jump_to_row_input().len() as u16 + 1,
5855                        chunks[input_chunk_idx].y + 1,
5856                    ));
5857                }
5858                AppMode::History => {
5859                    let query_len = self.state_container.history_search().query.len();
5860                    f.set_cursor_position((
5861                        chunks[input_chunk_idx].x + query_len as u16 + 1,
5862                        chunks[input_chunk_idx].y + 1,
5863                    ));
5864                }
5865                _ => {}
5866            }
5867        }
5868
5869        // Results area - render based on mode to reduce complexity
5870        let mode = self.shadow_state.borrow().get_mode();
5871        match mode {
5872            AppMode::Help => self.render_help(f, results_area),
5873            AppMode::History => self.render_history(f, results_area),
5874            AppMode::Debug => self.render_debug(f, results_area),
5875            AppMode::PrettyQuery => self.render_pretty_query(f, results_area),
5876            AppMode::ColumnStats => self.render_column_stats(f, results_area),
5877            _ if self.state_container.has_dataview() => {
5878                // Calculate viewport using DataView
5879                // V50: Render using DataProvider which works with DataTable
5880                if let Some(provider) = self.get_data_provider() {
5881                    self.render_table_with_provider(f, results_area, provider.as_ref());
5882                }
5883            }
5884            _ => {
5885                // Simple placeholder - reduced text to improve rendering speed
5886                let placeholder = Paragraph::new("Enter SQL query and press Enter\n\nTip: Use Tab for completion, Ctrl+R for history")
5887                    .block(Block::default().borders(Borders::ALL).title("Results"))
5888                    .style(Style::default().fg(Color::DarkGray));
5889                f.render_widget(placeholder, results_area);
5890            }
5891        }
5892
5893        // Render mode-specific status line
5894        self.render_status_line(f, chunks[status_chunk_idx]);
5895        // ========== RENDERING ==========
5896    }
5897
5898    /// Add mode styling and indicator to status spans
5899    fn add_mode_styling(&self, spans: &mut Vec<Span>) -> (Style, Color) {
5900        // Determine the mode color
5901        let (status_style, mode_color) = match self.shadow_state.borrow().get_mode() {
5902            AppMode::Command => (Style::default().fg(Color::Green), Color::Green),
5903            AppMode::Results => (Style::default().fg(Color::Blue), Color::Blue),
5904            AppMode::Search => (Style::default().fg(Color::Yellow), Color::Yellow),
5905            AppMode::Filter => (Style::default().fg(Color::Cyan), Color::Cyan),
5906            AppMode::FuzzyFilter => (Style::default().fg(Color::Magenta), Color::Magenta),
5907            AppMode::ColumnSearch => (Style::default().fg(Color::Green), Color::Green),
5908            AppMode::Help => (Style::default().fg(Color::Magenta), Color::Magenta),
5909            AppMode::History => (Style::default().fg(Color::Magenta), Color::Magenta),
5910            AppMode::Debug => (Style::default().fg(Color::Yellow), Color::Yellow),
5911            AppMode::PrettyQuery => (Style::default().fg(Color::Green), Color::Green),
5912            AppMode::JumpToRow => (Style::default().fg(Color::Magenta), Color::Magenta),
5913            AppMode::ColumnStats => (Style::default().fg(Color::Cyan), Color::Cyan),
5914        };
5915
5916        let mode_indicator = match self.shadow_state.borrow().get_mode() {
5917            AppMode::Command => "CMD",
5918            AppMode::Results => "NAV",
5919            AppMode::Search => "SEARCH",
5920            AppMode::Filter => "FILTER",
5921            AppMode::FuzzyFilter => "FUZZY",
5922            AppMode::ColumnSearch => "COL",
5923            AppMode::Help => "HELP",
5924            AppMode::History => "HISTORY",
5925            AppMode::Debug => "DEBUG",
5926            AppMode::PrettyQuery => "PRETTY",
5927            AppMode::JumpToRow => "JUMP",
5928            AppMode::ColumnStats => "STATS",
5929        };
5930
5931        // Mode indicator with color
5932        spans.push(Span::styled(
5933            format!("[{mode_indicator}]"),
5934            Style::default().fg(mode_color).add_modifier(Modifier::BOLD),
5935        ));
5936
5937        (status_style, mode_color)
5938    }
5939
5940    /// Add data source display to status spans
5941    fn add_data_source_display(&self, _spans: &mut Vec<Span>) {
5942        // Skip showing data source in status line since tab bar shows file names
5943        // This avoids redundancy like "[trades.csv] [1/2] trades.csv"
5944    }
5945
5946    /// Add buffer information to status spans
5947    fn add_buffer_information(&self, spans: &mut Vec<Span>) {
5948        let index = self.state_container.buffers().current_index();
5949        let total = self.state_container.buffers().all_buffers().len();
5950
5951        // Show buffer indicator if multiple buffers
5952        if total > 1 {
5953            spans.push(Span::raw(" "));
5954            spans.push(Span::styled(
5955                format!("[{}/{}]", index + 1, total),
5956                Style::default().fg(Color::Yellow),
5957            ));
5958        }
5959
5960        // Show table name from current query (simplified)
5961        // Since tab bar shows file names, we just show the table being queried
5962        if let Some(buffer) = self.state_container.buffers().current() {
5963            let query = buffer.get_input_text();
5964            // Simple extraction of table name from "SELECT ... FROM table" pattern
5965            if let Some(from_pos) = query.to_uppercase().find(" FROM ") {
5966                let after_from = &query[from_pos + 6..];
5967                // Take the first word after FROM as the table name
5968                if let Some(table_name) = after_from.split_whitespace().next() {
5969                    // Clean up the table name (remove quotes, etc.)
5970                    let clean_name = table_name
5971                        .trim_matches('"')
5972                        .trim_matches('\'')
5973                        .trim_matches('`');
5974
5975                    spans.push(Span::raw(" "));
5976                    spans.push(Span::styled(
5977                        clean_name.to_string(),
5978                        Style::default().fg(Color::Cyan),
5979                    ));
5980                }
5981            }
5982        }
5983    }
5984
5985    /// Add mode-specific information to status spans
5986    fn add_mode_specific_info(&self, spans: &mut Vec<Span>, mode_color: Color, area: Rect) {
5987        match self.shadow_state.borrow().get_mode() {
5988            AppMode::Command => {
5989                // In command mode, show editing-related info
5990                if !self.get_input_text().trim().is_empty() {
5991                    let (token_pos, total_tokens) = self.get_cursor_token_position();
5992                    spans.push(Span::raw(" | "));
5993                    spans.push(Span::styled(
5994                        format!("Token {token_pos}/{total_tokens}"),
5995                        Style::default().fg(Color::DarkGray),
5996                    ));
5997
5998                    // Show current token if available
5999                    if let Some(token) = self.get_token_at_cursor() {
6000                        spans.push(Span::raw(" "));
6001                        spans.push(Span::styled(
6002                            format!("[{token}]"),
6003                            Style::default().fg(Color::Cyan),
6004                        ));
6005                    }
6006
6007                    // Check for parser errors
6008                    if let Some(error_msg) = self.check_parser_error(&self.get_input_text()) {
6009                        spans.push(Span::raw(" | "));
6010                        spans.push(Span::styled(
6011                            format!("{} {}", self.config.display.icons.warning, error_msg),
6012                            Style::default().fg(Color::Red).add_modifier(Modifier::BOLD),
6013                        ));
6014                    }
6015                }
6016            }
6017            AppMode::Results => {
6018                // Extract this separately due to its size
6019                self.add_results_mode_info(spans, area);
6020            }
6021            AppMode::Search | AppMode::Filter | AppMode::FuzzyFilter | AppMode::ColumnSearch => {
6022                // Show the pattern being typed - always use input for consistency
6023                let pattern = self.get_input_text();
6024                if !pattern.is_empty() {
6025                    spans.push(Span::raw(" | Pattern: "));
6026                    spans.push(Span::styled(pattern, Style::default().fg(mode_color)));
6027                }
6028            }
6029            _ => {}
6030        }
6031    }
6032
6033    /// Add Results mode specific information (restored critical navigation info)
6034    fn add_results_mode_info(&self, spans: &mut Vec<Span>, area: Rect) {
6035        let total_rows = self.get_row_count();
6036        if total_rows > 0 {
6037            // Get selected row directly from navigation state (0-indexed) and add 1 for display
6038            let selected = self.state_container.navigation().selected_row + 1;
6039            spans.push(Span::raw(" | "));
6040
6041            // Show selection mode
6042            let selection_mode = self.get_selection_mode();
6043            let mode_text = match selection_mode {
6044                SelectionMode::Cell => "CELL",
6045                SelectionMode::Row => "ROW",
6046                SelectionMode::Column => "COL",
6047            };
6048            spans.push(Span::styled(
6049                format!("[{mode_text}]"),
6050                Style::default()
6051                    .fg(Color::Cyan)
6052                    .add_modifier(Modifier::BOLD),
6053            ));
6054
6055            spans.push(Span::raw(" "));
6056            spans.push(Span::styled(
6057                format!("Row {selected}/{total_rows}"),
6058                Style::default().fg(Color::White),
6059            ));
6060
6061            // Add cursor coordinates (x,y) - column and row position
6062            // Use ViewportManager's visual column position (1-based for display)
6063            let visual_col_display =
6064                if let Some(ref viewport_manager) = *self.viewport_manager.borrow() {
6065                    viewport_manager.get_crosshair_col() + 1
6066                } else {
6067                    1
6068                };
6069            spans.push(Span::raw(" "));
6070            spans.push(Span::styled(
6071                format!("({visual_col_display},{selected})"),
6072                Style::default().fg(Color::DarkGray),
6073            ));
6074
6075            // Add actual terminal cursor position if we can calculate it
6076            if let Some(ref mut viewport_manager) = *self.viewport_manager.borrow_mut() {
6077                let available_width = area.width.saturating_sub(TABLE_BORDER_WIDTH);
6078                // Use ViewportManager's crosshair column position
6079                let visual_col = viewport_manager.get_crosshair_col();
6080                if let Some(x_pos) =
6081                    viewport_manager.get_column_x_position(visual_col, available_width)
6082                {
6083                    // Add 2 for left border and padding, add 3 for header rows
6084                    let terminal_x = x_pos + 2;
6085                    let terminal_y = (selected as u16)
6086                        .saturating_sub(self.state_container.get_scroll_offset().0 as u16)
6087                        + 3;
6088                    spans.push(Span::raw(" "));
6089                    spans.push(Span::styled(
6090                        format!("[{terminal_x}x{terminal_y}]"),
6091                        Style::default().fg(Color::DarkGray),
6092                    ));
6093                }
6094            }
6095
6096            // Column information
6097            if let Some(dataview) = self.state_container.get_buffer_dataview() {
6098                let headers = dataview.column_names();
6099
6100                // Get ViewportManager's crosshair position (visual coordinates)
6101                // and use it to get the correct column name
6102                let (visual_row, visual_col) =
6103                    if let Some(ref viewport_manager) = *self.viewport_manager.borrow() {
6104                        (
6105                            viewport_manager.get_crosshair_row(),
6106                            viewport_manager.get_crosshair_col(),
6107                        )
6108                    } else {
6109                        (0, 0)
6110                    };
6111
6112                // Use ViewportManager's visual column index to get the correct column name
6113                if visual_col < headers.len() {
6114                    spans.push(Span::raw(" | Col: "));
6115                    spans.push(Span::styled(
6116                        headers[visual_col].clone(),
6117                        Style::default().fg(Color::Cyan),
6118                    ));
6119
6120                    // Show ViewportManager's crosshair position and viewport size
6121                    let viewport_info =
6122                        if let Some(ref viewport_manager) = *self.viewport_manager.borrow() {
6123                            let viewport_rows = viewport_manager.get_viewport_rows();
6124                            let viewport_height = viewport_rows.end - viewport_rows.start;
6125                            format!("[V:{visual_row},{visual_col} @ {viewport_height}r]")
6126                        } else {
6127                            format!("[V:{visual_row},{visual_col}]")
6128                        };
6129                    spans.push(Span::raw(" "));
6130                    spans.push(Span::styled(
6131                        viewport_info,
6132                        Style::default().fg(Color::Magenta),
6133                    ));
6134                }
6135            }
6136        }
6137    }
6138
6139    fn render_status_line(&self, f: &mut Frame, area: Rect) {
6140        let mut spans = Vec::new();
6141
6142        // Add mode styling and indicator
6143        let (status_style, mode_color) = self.add_mode_styling(&mut spans);
6144
6145        // Add data source display
6146        self.add_data_source_display(&mut spans);
6147
6148        // Add buffer information
6149        self.add_buffer_information(&mut spans);
6150
6151        // Add mode-specific information
6152        self.add_mode_specific_info(&mut spans, mode_color, area);
6153
6154        // Add query source indicator
6155        self.add_query_source_indicator(&mut spans);
6156
6157        // Add case sensitivity indicator
6158        self.add_case_sensitivity_indicator(&mut spans);
6159
6160        // Add column packing mode indicator
6161        self.add_column_packing_indicator(&mut spans);
6162
6163        // Add status message
6164        self.add_status_message(&mut spans);
6165
6166        // Determine help text based on current mode
6167        let help_text = self.get_help_text_for_mode();
6168
6169        self.add_global_indicators(&mut spans);
6170
6171        // Add shadow state display
6172        self.add_shadow_state_display(&mut spans);
6173
6174        self.add_help_text_display(&mut spans, help_text, area);
6175
6176        let status_line = Line::from(spans);
6177        let status = Paragraph::new(status_line)
6178            .block(Block::default().borders(Borders::ALL))
6179            .style(status_style);
6180        f.render_widget(status, area);
6181    }
6182
6183    /// Build a `TableRenderContext` with all data needed for rendering
6184    /// This collects all the scattered data into a single struct
6185    fn build_table_context(
6186        &self,
6187        area: Rect,
6188        provider: &dyn DataProvider,
6189    ) -> crate::ui::rendering::table_render_context::TableRenderContext {
6190        use crate::ui::rendering::table_render_context::TableRenderContextBuilder;
6191
6192        let row_count = provider.get_row_count();
6193        let available_width = area.width.saturating_sub(TABLE_BORDER_WIDTH);
6194        // The area passed here is already the table area
6195        // The Table widget itself handles borders and header
6196        let available_height = area.height;
6197
6198        // Get headers from ViewportManager (single source of truth)
6199        let headers = {
6200            let viewport_manager = self.viewport_manager.borrow();
6201            let viewport_manager = viewport_manager
6202                .as_ref()
6203                .expect("ViewportManager must exist");
6204            viewport_manager.get_column_names_ordered()
6205        };
6206
6207        // Update ViewportManager with current terminal dimensions
6208        {
6209            let mut viewport_opt = self.viewport_manager.borrow_mut();
6210            if let Some(ref mut viewport_manager) = *viewport_opt {
6211                // Calculate data rows for the table area
6212                let data_rows = Self::calculate_table_data_rows(available_height);
6213                viewport_manager.update_terminal_size(available_width, data_rows);
6214                let _ = viewport_manager.get_column_widths(); // Trigger recalculation
6215            }
6216        }
6217
6218        // Get structured column information from ViewportManager
6219        let (pinned_visual_positions, crosshair_column_position, _) = {
6220            let mut viewport_manager_borrow = self.viewport_manager.borrow_mut();
6221            let viewport_manager = viewport_manager_borrow
6222                .as_mut()
6223                .expect("ViewportManager must exist for rendering");
6224            let info = viewport_manager.get_visible_columns_info(available_width);
6225
6226            // info.0 = visible_indices (all visible column source indices)
6227            // info.1 = pinned_visible (pinned column source indices)
6228            // info.2 = scrollable_visible (scrollable column source indices)
6229            let visible_indices = &info.0;
6230            let pinned_source_indices = &info.1;
6231
6232            // Convert pinned source indices to visual positions
6233            // The TableRenderContext expects visual positions (0-based positions in the visible array)
6234            let mut pinned_visual_positions = Vec::new();
6235            for &source_idx in pinned_source_indices {
6236                if let Some(visual_pos) = visible_indices.iter().position(|&x| x == source_idx) {
6237                    pinned_visual_positions.push(visual_pos);
6238                }
6239            }
6240
6241            // Get the crosshair's viewport-relative position for rendering
6242            // The viewport manager stores crosshair in absolute coordinates
6243            // but we need viewport-relative for rendering
6244            let crosshair_column_position =
6245                if let Some((_, col_pos)) = viewport_manager.get_crosshair_viewport_position() {
6246                    col_pos
6247                } else {
6248                    // Crosshair is outside viewport, default to 0
6249                    0
6250                };
6251
6252            let crosshair_visual = viewport_manager.get_crosshair_col();
6253
6254            (
6255                pinned_visual_positions,
6256                crosshair_column_position,
6257                crosshair_visual,
6258            )
6259        };
6260
6261        // Calculate row viewport
6262        let row_viewport_start = self
6263            .state_container
6264            .navigation()
6265            .scroll_offset
6266            .0
6267            .min(row_count.saturating_sub(1));
6268        let row_viewport_end = (row_viewport_start + available_height as usize).min(row_count);
6269        let visible_row_indices: Vec<usize> = (row_viewport_start..row_viewport_end).collect();
6270
6271        // Get the visual display data from ViewportManager
6272        let (column_headers, data_to_display, column_widths_visual) = {
6273            let mut viewport_manager_borrow = self.viewport_manager.borrow_mut();
6274            if let Some(ref mut viewport_manager) = *viewport_manager_borrow {
6275                viewport_manager.get_visual_display(available_width, &visible_row_indices)
6276            } else {
6277                // Fallback
6278                let visible_rows = provider
6279                    .get_visible_rows(row_viewport_start, row_viewport_end - row_viewport_start);
6280                let widths = vec![15u16; headers.len()];
6281                (headers.clone(), visible_rows, widths)
6282            }
6283        };
6284
6285        // Get sort state
6286        let sort_state = self
6287            .buffer()
6288            .get_dataview()
6289            .map(|dv| dv.get_sort_state().clone());
6290
6291        // Get filter info
6292        let fuzzy_filter_pattern = if self.state_container.is_fuzzy_filter_active() {
6293            let pattern = self.state_container.get_fuzzy_filter_pattern();
6294            if pattern.is_empty() {
6295                None
6296            } else {
6297                Some(pattern)
6298            }
6299        } else {
6300            None
6301        };
6302
6303        // Build the context
6304        let selected_row = self.state_container.navigation().selected_row;
6305        let selected_col = crosshair_column_position;
6306
6307        // Log what we're passing to the renderer
6308        trace!(target: "search", "Building TableRenderContext: selected_row={}, selected_col={}, mode={:?}",
6309               selected_row, selected_col, self.state_container.get_selection_mode());
6310
6311        TableRenderContextBuilder::new()
6312            .row_count(row_count)
6313            .visible_rows(visible_row_indices.clone(), data_to_display)
6314            .columns(column_headers, column_widths_visual)
6315            .pinned_columns(pinned_visual_positions)
6316            .selection(
6317                selected_row,
6318                selected_col,
6319                self.state_container.get_selection_mode(),
6320            )
6321            .row_viewport(row_viewport_start..row_viewport_end)
6322            .sort_state(sort_state)
6323            .display_options(
6324                self.state_container.is_show_row_numbers(),
6325                self.shadow_state.borrow().get_mode(),
6326            )
6327            .filter(
6328                fuzzy_filter_pattern,
6329                self.state_container.is_case_insensitive(),
6330            )
6331            .dimensions(available_width, available_height)
6332            .build()
6333    }
6334
6335    /// New trait-based table rendering method
6336    /// This uses `DataProvider` trait instead of directly accessing `QueryResponse`
6337    fn render_table_with_provider(&self, f: &mut Frame, area: Rect, provider: &dyn DataProvider) {
6338        // Build the context with all data needed for rendering
6339        let context = self.build_table_context(area, provider);
6340
6341        // Use the pure table renderer
6342        crate::ui::rendering::table_renderer::render_table(f, area, &context);
6343    }
6344
6345    fn render_help(&mut self, f: &mut Frame, area: Rect) {
6346        // Delegate to the help widget which handles tabs, scrolling, and sections
6347        self.help_widget.render(f, area);
6348    }
6349
6350    fn render_help_two_column(&self, f: &mut Frame, area: Rect) {
6351        // Create two-column layout
6352        let chunks = Layout::default()
6353            .direction(Direction::Horizontal)
6354            .constraints([Constraint::Percentage(50), Constraint::Percentage(50)])
6355            .split(area);
6356
6357        // Get help content from HelpText module
6358        let left_content = HelpText::left_column();
6359        let right_content = HelpText::right_column();
6360
6361        // Calculate visible area for scrolling
6362        let visible_height = area.height.saturating_sub(2) as usize; // Account for borders
6363        let left_total_lines = left_content.len();
6364        let right_total_lines = right_content.len();
6365        let max_lines = left_total_lines.max(right_total_lines);
6366
6367        // Apply scroll offset (from state container or local)
6368        let scroll_offset = { self.state_container.help_scroll_offset() as usize };
6369
6370        // Get visible portions with scrolling
6371        let left_visible: Vec<Line> = left_content
6372            .into_iter()
6373            .skip(scroll_offset)
6374            .take(visible_height)
6375            .collect();
6376
6377        let right_visible: Vec<Line> = right_content
6378            .into_iter()
6379            .skip(scroll_offset)
6380            .take(visible_height)
6381            .collect();
6382
6383        // Create scroll indicator in title
6384        let scroll_indicator = if max_lines > visible_height {
6385            format!(
6386                " (↓/↑ to scroll, {}/{})",
6387                scroll_offset + 1,
6388                max_lines.saturating_sub(visible_height) + 1
6389            )
6390        } else {
6391            String::new()
6392        };
6393
6394        // Render left column
6395        let left_paragraph = Paragraph::new(Text::from(left_visible))
6396            .block(
6397                Block::default()
6398                    .borders(Borders::ALL)
6399                    .title(format!("Help - Commands{scroll_indicator}")),
6400            )
6401            .style(Style::default());
6402
6403        // Render right column
6404        let right_paragraph = Paragraph::new(Text::from(right_visible))
6405            .block(
6406                Block::default()
6407                    .borders(Borders::ALL)
6408                    .title("Help - Navigation & Features"),
6409            )
6410            .style(Style::default());
6411
6412        f.render_widget(left_paragraph, chunks[0]);
6413        f.render_widget(right_paragraph, chunks[1]);
6414    }
6415
6416    fn render_debug(&self, f: &mut Frame, area: Rect) {
6417        <Self as DebugContext>::render_debug(self, f, area);
6418    }
6419
6420    fn render_pretty_query(&self, f: &mut Frame, area: Rect) {
6421        <Self as DebugContext>::render_pretty_query(self, f, area);
6422    }
6423
6424    fn render_history(&self, f: &mut Frame, area: Rect) {
6425        // Get history state from AppStateContainer
6426        let history_search = self.state_container.history_search();
6427        let matches_empty = history_search.matches.is_empty();
6428        let search_query_empty = history_search.query.is_empty();
6429
6430        if matches_empty {
6431            let no_history = if search_query_empty {
6432                "No command history found.\nExecute some queries to build history."
6433            } else {
6434                "No matches found for your search.\nTry a different search term."
6435            };
6436
6437            let placeholder = Paragraph::new(no_history)
6438                .block(
6439                    Block::default()
6440                        .borders(Borders::ALL)
6441                        .title("Command History"),
6442                )
6443                .style(Style::default().fg(Color::DarkGray));
6444            f.render_widget(placeholder, area);
6445            return;
6446        }
6447
6448        // Split the area to show selected command details
6449        let chunks = Layout::default()
6450            .direction(Direction::Vertical)
6451            .constraints([
6452                Constraint::Percentage(50), // History list - 50% of space
6453                Constraint::Percentage(50), // Selected command preview - 50% of space
6454            ])
6455            .split(area);
6456
6457        self.render_history_list(f, chunks[0]);
6458        self.render_selected_command_preview(f, chunks[1]);
6459    }
6460
6461    fn render_history_list(&self, f: &mut Frame, area: Rect) {
6462        // Get history data from AppStateContainer
6463        let history_search = self.state_container.history_search();
6464        let matches = history_search.matches.clone();
6465        let selected_index = history_search.selected_index;
6466        let match_count = matches.len();
6467
6468        // Create more compact history list - just show essential info
6469        let history_items: Vec<Line> = matches
6470            .iter()
6471            .enumerate()
6472            .map(|(i, history_match)| {
6473                let entry = &history_match.entry;
6474                let is_selected = i == selected_index;
6475
6476                let success_indicator = if entry.success { "✓" } else { "✗" };
6477                let time_ago = {
6478                    let elapsed = chrono::Utc::now() - entry.timestamp;
6479                    if elapsed.num_days() > 0 {
6480                        format!("{}d", elapsed.num_days())
6481                    } else if elapsed.num_hours() > 0 {
6482                        format!("{}h", elapsed.num_hours())
6483                    } else if elapsed.num_minutes() > 0 {
6484                        format!("{}m", elapsed.num_minutes())
6485                    } else {
6486                        "now".to_string()
6487                    }
6488                };
6489
6490                // Use more space for the command, less for metadata
6491                let terminal_width = area.width as usize;
6492                let metadata_space = 15; // Reduced metadata: " ✓ 2x 1h"
6493                let available_for_command = terminal_width.saturating_sub(metadata_space).max(50);
6494
6495                let command_text = if entry.command.len() > available_for_command {
6496                    format!(
6497                        "{}…",
6498                        &entry.command[..available_for_command.saturating_sub(1)]
6499                    )
6500                } else {
6501                    entry.command.clone()
6502                };
6503
6504                let line_text = format!(
6505                    "{} {} {} {}x {}",
6506                    if is_selected { "►" } else { " " },
6507                    command_text,
6508                    success_indicator,
6509                    entry.execution_count,
6510                    time_ago
6511                );
6512
6513                let mut style = Style::default();
6514                if is_selected {
6515                    style = style.bg(Color::DarkGray).add_modifier(Modifier::BOLD);
6516                }
6517                if !entry.success {
6518                    style = style.fg(Color::Red);
6519                }
6520
6521                // Highlight matching characters for fuzzy search
6522                if !history_match.indices.is_empty() && is_selected {
6523                    style = style.fg(Color::Yellow);
6524                }
6525
6526                Line::from(line_text).style(style)
6527            })
6528            .collect();
6529
6530        let history_paragraph = Paragraph::new(history_items)
6531            .block(Block::default().borders(Borders::ALL).title(format!(
6532                "History ({match_count} matches) - j/k to navigate, Enter to select"
6533            )))
6534            .wrap(ratatui::widgets::Wrap { trim: false });
6535
6536        f.render_widget(history_paragraph, area);
6537    }
6538
6539    fn render_selected_command_preview(&self, f: &mut Frame, area: Rect) {
6540        // Get the selected match from AppStateContainer
6541        let history_search = self.state_container.history_search();
6542        let selected_match = history_search
6543            .matches
6544            .get(history_search.selected_index)
6545            .cloned();
6546
6547        if let Some(selected_match) = selected_match {
6548            let entry = &selected_match.entry;
6549
6550            // Pretty format the SQL command - adjust compactness based on available space
6551            use crate::recursive_parser::format_sql_pretty_compact;
6552
6553            // Calculate how many columns we can fit per line
6554            let available_width = area.width.saturating_sub(6) as usize; // Account for indentation and borders
6555            let avg_col_width = 15; // Assume average column name is ~15 chars
6556            let cols_per_line = (available_width / avg_col_width).max(3).min(12); // Between 3-12 columns per line
6557
6558            let mut pretty_lines = format_sql_pretty_compact(&entry.command, cols_per_line);
6559
6560            // If too many lines for the area, use a more compact format
6561            let max_lines = area.height.saturating_sub(2) as usize; // Account for borders
6562            if pretty_lines.len() > max_lines && cols_per_line < 12 {
6563                // Try with more columns per line
6564                pretty_lines = format_sql_pretty_compact(&entry.command, 15);
6565            }
6566
6567            // Convert to Text with syntax highlighting
6568            let mut highlighted_lines = Vec::new();
6569            for line in pretty_lines {
6570                highlighted_lines.push(self.sql_highlighter.simple_sql_highlight(&line));
6571            }
6572
6573            let preview_text = Text::from(highlighted_lines);
6574
6575            let duration_text = entry
6576                .duration_ms
6577                .map_or_else(|| "?ms".to_string(), |d| format!("{d}ms"));
6578
6579            let success_text = if entry.success {
6580                "✓ Success"
6581            } else {
6582                "✗ Failed"
6583            };
6584
6585            let preview = Paragraph::new(preview_text)
6586                .block(Block::default().borders(Borders::ALL).title(format!(
6587                    "Pretty SQL Preview: {} | {} | Used {}x",
6588                    success_text, duration_text, entry.execution_count
6589                )))
6590                .scroll((0, 0)); // Allow scrolling if needed
6591
6592            f.render_widget(preview, area);
6593        } else {
6594            let empty_preview = Paragraph::new("No command selected")
6595                .block(Block::default().borders(Borders::ALL).title("Preview"))
6596                .style(Style::default().fg(Color::DarkGray));
6597            f.render_widget(empty_preview, area);
6598        }
6599    }
6600
6601    fn handle_column_stats_input(&mut self, key: crossterm::event::KeyEvent) -> Result<bool> {
6602        // Create context and delegate to extracted handler
6603        let mut ctx = crate::ui::input::input_handlers::StatsInputContext {
6604            buffer_manager: self.state_container.buffers_mut(),
6605            stats_widget: &mut self.stats_widget,
6606            shadow_state: &self.shadow_state,
6607        };
6608
6609        let result = crate::ui::input::input_handlers::handle_column_stats_input(&mut ctx, key)?;
6610
6611        // Check if mode changed to Results (happens when stats view is closed)
6612        if self.shadow_state.borrow().get_mode() == AppMode::Results {
6613            self.shadow_state
6614                .borrow_mut()
6615                .observe_mode_change(AppMode::Results, "column_stats_closed");
6616        }
6617
6618        Ok(result)
6619    }
6620
6621    fn handle_jump_to_row_input(&mut self, key: crossterm::event::KeyEvent) -> Result<bool> {
6622        match key.code {
6623            KeyCode::Enter => {
6624                // Use NavigationBehavior trait's complete_jump_to_row method
6625                let input = self.get_jump_to_row_input();
6626                self.complete_jump_to_row(&input);
6627            }
6628            _ => {
6629                // Use InputBehavior trait's process_jump_to_row_key for other keys
6630                self.process_jump_to_row_key(key);
6631            }
6632        }
6633        Ok(false)
6634    }
6635
6636    fn render_column_stats(&self, f: &mut Frame, area: Rect) {
6637        // Delegate to the stats widget
6638        self.stats_widget.render(
6639            f,
6640            area,
6641            self.state_container
6642                .current_buffer()
6643                .expect("Buffer should exist"),
6644        );
6645    }
6646
6647    // === Editor Widget Helper Methods ===
6648    // ========== QUERY EXECUTION ==========
6649
6650    // These methods handle the actions returned by the editor widget
6651
6652    fn handle_execute_query(&mut self) -> Result<bool> {
6653        use crate::ui::state::state_coordinator::StateCoordinator;
6654
6655        let query = self.get_input_text().trim().to_string();
6656        debug!(target: "action", "Executing query: {}", query);
6657
6658        // Use StateCoordinator to handle special commands and state changes
6659        let should_exit = StateCoordinator::handle_execute_query_with_refs(
6660            &mut self.state_container,
6661            &self.shadow_state,
6662            &query,
6663        )?;
6664
6665        if should_exit {
6666            return Ok(true);
6667        }
6668
6669        // If not a special command and not empty, execute the SQL query
6670        if !query.is_empty() && !query.starts_with(':') {
6671            if let Err(e) = self.execute_query_v2(&query) {
6672                self.state_container
6673                    .set_status_message(format!("Error executing query: {e}"));
6674            }
6675            // Don't clear input - preserve query for editing
6676        }
6677
6678        Ok(false) // Continue running, don't exit
6679    }
6680
6681    fn handle_buffer_action(&mut self, action: BufferAction) -> Result<bool> {
6682        match action {
6683            BufferAction::NextBuffer => {
6684                let message = self
6685                    .buffer_handler
6686                    .next_buffer(self.state_container.buffers_mut());
6687                debug!("{}", message);
6688                // Sync all state after buffer switch
6689                self.sync_after_buffer_switch();
6690                Ok(false)
6691            }
6692            BufferAction::PreviousBuffer => {
6693                let message = self
6694                    .buffer_handler
6695                    .previous_buffer(self.state_container.buffers_mut());
6696                debug!("{}", message);
6697                // Sync all state after buffer switch
6698                self.sync_after_buffer_switch();
6699                Ok(false)
6700            }
6701            BufferAction::QuickSwitch => {
6702                let message = self
6703                    .buffer_handler
6704                    .quick_switch(self.state_container.buffers_mut());
6705                debug!("{}", message);
6706                // Sync all state after buffer switch
6707                self.sync_after_buffer_switch();
6708                Ok(false)
6709            }
6710            BufferAction::NewBuffer => {
6711                let message = self
6712                    .buffer_handler
6713                    .new_buffer(self.state_container.buffers_mut(), &self.config);
6714                debug!("{}", message);
6715                Ok(false)
6716            }
6717            BufferAction::CloseBuffer => {
6718                let (success, message) = self
6719                    .buffer_handler
6720                    .close_buffer(self.state_container.buffers_mut());
6721                debug!("{}", message);
6722                Ok(!success) // Exit if we couldn't close (only one left)
6723            }
6724            BufferAction::ListBuffers => {
6725                let buffer_list = self
6726                    .buffer_handler
6727                    .list_buffers(self.state_container.buffers());
6728                // For now, just log the list - later we can show a popup
6729                for line in &buffer_list {
6730                    debug!("{}", line);
6731                }
6732                Ok(false)
6733            }
6734            BufferAction::SwitchToBuffer(buffer_index) => {
6735                let message = self
6736                    .buffer_handler
6737                    .switch_to_buffer(self.state_container.buffers_mut(), buffer_index);
6738                debug!("{}", message);
6739
6740                // Sync all state after buffer switch
6741                self.sync_after_buffer_switch();
6742
6743                Ok(false)
6744            }
6745        }
6746    }
6747
6748    fn handle_expand_asterisk(&mut self) -> Result<bool> {
6749        if let Some(buffer) = self.state_container.buffers_mut().current_mut() {
6750            if buffer.expand_asterisk(&self.hybrid_parser) {
6751                // Sync for rendering if needed
6752                if buffer.get_edit_mode() == EditMode::SingleLine {
6753                    let text = buffer.get_input_text();
6754                    let cursor = buffer.get_input_cursor_position();
6755                    self.set_input_text_with_cursor(text, cursor);
6756                }
6757            }
6758        }
6759        Ok(false)
6760    }
6761
6762    pub(crate) fn toggle_debug_mode(&mut self) {
6763        // Use the DebugContext trait which has all the logic
6764        DebugContext::toggle_debug_mode(self);
6765    }
6766
6767    // ==================== Debug Helper Methods ====================
6768    // These are kept in the TUI to avoid regressions from moving data access
6769
6770    /// Generate the parser debug section
6771    pub(crate) fn debug_generate_parser_info(&self, query: &str) -> String {
6772        self.hybrid_parser
6773            .get_detailed_debug_info(query, query.len())
6774    }
6775
6776    fn debug_generate_navigation_state(&self) -> String {
6777        let mut debug_info = String::new();
6778        debug_info.push_str("\n========== NAVIGATION DEBUG ==========\n");
6779        let current_column = self.state_container.get_current_column();
6780        let scroll_offset = self.state_container.get_scroll_offset();
6781        let nav_state = self.state_container.navigation();
6782
6783        debug_info.push_str(&format!("Buffer Column Position: {current_column}\n"));
6784        debug_info.push_str(&format!(
6785            "Buffer Scroll Offset: row={}, col={}\n",
6786            scroll_offset.0, scroll_offset.1
6787        ));
6788        debug_info.push_str(&format!(
6789            "NavigationState Column: {}\n",
6790            nav_state.selected_column
6791        ));
6792        debug_info.push_str(&format!(
6793            "NavigationState Row: {:?}\n",
6794            nav_state.selected_row
6795        ));
6796        debug_info.push_str(&format!(
6797            "NavigationState Scroll Offset: row={}, col={}\n",
6798            nav_state.scroll_offset.0, nav_state.scroll_offset.1
6799        ));
6800
6801        // Show if synchronization is correct
6802        if current_column != nav_state.selected_column {
6803            debug_info.push_str(&format!(
6804                "⚠️  WARNING: Column mismatch! Buffer={}, Nav={}\n",
6805                current_column, nav_state.selected_column
6806            ));
6807        }
6808        if scroll_offset.1 != nav_state.scroll_offset.1 {
6809            debug_info.push_str(&format!(
6810                "⚠️  WARNING: Scroll column mismatch! Buffer={}, Nav={}\n",
6811                scroll_offset.1, nav_state.scroll_offset.1
6812            ));
6813        }
6814
6815        debug_info.push_str("\n--- Navigation Flow ---\n");
6816        debug_info.push_str(
6817            "(Enable RUST_LOG=sql_cli::ui::viewport_manager=debug,navigation=debug to see flow)\n",
6818        );
6819
6820        // Show pinned column info for navigation context
6821        if let Some(dataview) = self.state_container.get_buffer_dataview() {
6822            let pinned_count = dataview.get_pinned_columns().len();
6823            let pinned_names = dataview.get_pinned_column_names();
6824            debug_info.push_str(&format!("Pinned Column Count: {pinned_count}\n"));
6825            if !pinned_names.is_empty() {
6826                debug_info.push_str(&format!("Pinned Column Names: {pinned_names:?}\n"));
6827            }
6828            debug_info.push_str(&format!("First Scrollable Column: {pinned_count}\n"));
6829
6830            // Show if current column is in pinned or scrollable area
6831            if current_column < pinned_count {
6832                debug_info.push_str(&format!(
6833                    "Current Position: PINNED area (column {current_column})\n"
6834                ));
6835            } else {
6836                debug_info.push_str(&format!(
6837                    "Current Position: SCROLLABLE area (column {}, scrollable index {})\n",
6838                    current_column,
6839                    current_column - pinned_count
6840                ));
6841            }
6842
6843            // Show display column order
6844            let display_columns = dataview.get_display_columns();
6845            debug_info.push_str("\n--- COLUMN ORDERING ---\n");
6846            debug_info.push_str(&format!(
6847                "Display column order (first 10): {:?}\n",
6848                &display_columns[..display_columns.len().min(10)]
6849            ));
6850            if display_columns.len() > 10 {
6851                debug_info.push_str(&format!(
6852                    "... and {} more columns\n",
6853                    display_columns.len() - 10
6854                ));
6855            }
6856
6857            // Find current column in display order
6858            if let Some(display_idx) = display_columns
6859                .iter()
6860                .position(|&idx| idx == current_column)
6861            {
6862                debug_info.push_str(&format!(
6863                    "Current column {} is at display index {}/{}\n",
6864                    current_column,
6865                    display_idx,
6866                    display_columns.len()
6867                ));
6868
6869                // Show what happens on next move
6870                if display_idx + 1 < display_columns.len() {
6871                    let next_col = display_columns[display_idx + 1];
6872                    debug_info.push_str(&format!(
6873                        "Next 'l' press should move to column {} (display index {})\n",
6874                        next_col,
6875                        display_idx + 1
6876                    ));
6877                } else {
6878                    debug_info.push_str("Next 'l' press should wrap to first column\n");
6879                }
6880            } else {
6881                debug_info.push_str(&format!(
6882                    "WARNING: Current column {current_column} not found in display order!\n"
6883                ));
6884            }
6885        }
6886        debug_info.push_str("==========================================\n");
6887        debug_info
6888    }
6889
6890    fn debug_generate_column_search_state(&self) -> String {
6891        let mut debug_info = String::new();
6892        let show_column_search = self.shadow_state.borrow().get_mode() == AppMode::ColumnSearch
6893            || !self.state_container.column_search().pattern.is_empty();
6894        if show_column_search {
6895            let column_search = self.state_container.column_search();
6896            debug_info.push_str("\n========== COLUMN SEARCH STATE ==========\n");
6897            debug_info.push_str(&format!("Pattern: '{}'\n", column_search.pattern));
6898            debug_info.push_str(&format!(
6899                "Matching Columns: {} found\n",
6900                column_search.matching_columns.len()
6901            ));
6902            if !column_search.matching_columns.is_empty() {
6903                debug_info.push_str("Matches:\n");
6904                for (idx, (col_idx, col_name)) in column_search.matching_columns.iter().enumerate()
6905                {
6906                    let marker = if idx == column_search.current_match {
6907                        " <--"
6908                    } else {
6909                        ""
6910                    };
6911                    debug_info
6912                        .push_str(&format!("  [{idx}] {col_name} (index {col_idx}){marker}\n"));
6913                }
6914            }
6915            debug_info.push_str(&format!(
6916                "Current Match Index: {}\n",
6917                column_search.current_match
6918            ));
6919            debug_info.push_str(&format!(
6920                "Current Column: {}\n",
6921                self.state_container.get_current_column()
6922            ));
6923            debug_info.push_str("==========================================\n");
6924        }
6925        debug_info
6926    }
6927
6928    pub(crate) fn debug_generate_trace_logs(&self) -> String {
6929        let mut debug_info = String::from("\n========== TRACE LOGS ==========\n");
6930        debug_info.push_str("(Most recent at bottom, last 100 entries)\n");
6931
6932        if let Some(ref log_buffer) = self.log_buffer {
6933            let recent_logs = log_buffer.get_recent(100);
6934            for entry in recent_logs {
6935                debug_info.push_str(&entry.format_for_display());
6936                debug_info.push('\n');
6937            }
6938            debug_info.push_str(&format!("Total log entries: {}\n", log_buffer.len()));
6939        } else {
6940            debug_info.push_str("Log buffer not initialized\n");
6941        }
6942        debug_info.push_str("================================\n");
6943
6944        debug_info
6945    }
6946
6947    /// Generate the state change logs debug section
6948    pub(crate) fn debug_generate_state_logs(&self) -> String {
6949        let mut debug_info = String::new();
6950
6951        if let Some(ref debug_service) = self.debug_service {
6952            debug_info.push_str("\n========== STATE CHANGE LOGS ==========\n");
6953            debug_info.push_str("(Most recent at bottom, from DebugService)\n");
6954            let debug_entries = debug_service.get_entries();
6955            let recent = debug_entries.iter().rev().take(50).rev();
6956            for entry in recent {
6957                debug_info.push_str(&format!(
6958                    "[{}] {:?} [{}]: {}\n",
6959                    entry.timestamp, entry.level, entry.component, entry.message
6960                ));
6961            }
6962            debug_info.push_str(&format!(
6963                "Total state change entries: {}\n",
6964                debug_entries.len()
6965            ));
6966            debug_info.push_str("================================\n");
6967        } else {
6968            debug_info.push_str("\n========== STATE CHANGE LOGS ==========\n");
6969            debug_info.push_str("DebugService not available (service_container is None)\n");
6970            debug_info.push_str("================================\n");
6971        }
6972
6973        debug_info
6974    }
6975
6976    /// Extract time in milliseconds from a timing string
6977    pub(crate) fn debug_extract_timing(&self, s: &str) -> Option<f64> {
6978        crate::ui::rendering::ui_layout_utils::extract_timing_from_debug_string(s)
6979    }
6980
6981    fn show_pretty_query(&mut self) {
6982        if let Some(buffer) = self.state_container.buffers_mut().current_mut() {
6983            self.shadow_state.borrow_mut().set_mode(
6984                AppMode::PrettyQuery,
6985                buffer,
6986                "pretty_query_show",
6987            );
6988            let query = buffer.get_input_text();
6989            self.debug_widget.generate_pretty_sql(&query);
6990        }
6991    }
6992
6993    /// Add global indicators like key sequence display
6994    fn add_global_indicators(&self, spans: &mut Vec<Span>) {
6995        if self.key_sequence_renderer.has_content() {
6996            let key_display = self.key_sequence_renderer.get_display();
6997            if !key_display.is_empty() {
6998                spans.push(Span::raw(" | Keys: "));
6999                spans.push(Span::styled(
7000                    key_display,
7001                    Style::default()
7002                        .fg(Color::Cyan)
7003                        .add_modifier(Modifier::ITALIC),
7004                ));
7005            }
7006        }
7007    }
7008
7009    fn add_query_source_indicator(&self, spans: &mut Vec<Span>) {
7010        if let Some(source) = self.state_container.get_last_query_source() {
7011            spans.push(Span::raw(" | "));
7012            let (icon, label, color) = match source.as_str() {
7013                "cache" => (
7014                    &self.config.display.icons.cache,
7015                    "CACHE".to_string(),
7016                    Color::Cyan,
7017                ),
7018                "file" | "FileDataSource" => (
7019                    &self.config.display.icons.file,
7020                    "FILE".to_string(),
7021                    Color::Green,
7022                ),
7023                "SqlServerDataSource" => (
7024                    &self.config.display.icons.database,
7025                    "SQL".to_string(),
7026                    Color::Blue,
7027                ),
7028                "PublicApiDataSource" => (
7029                    &self.config.display.icons.api,
7030                    "API".to_string(),
7031                    Color::Yellow,
7032                ),
7033                _ => (
7034                    &self.config.display.icons.api,
7035                    source.clone(),
7036                    Color::Magenta,
7037                ),
7038            };
7039            spans.push(Span::raw(format!("{icon} ")));
7040            spans.push(Span::styled(label, Style::default().fg(color)));
7041        }
7042    }
7043
7044    fn add_case_sensitivity_indicator(&self, spans: &mut Vec<Span>) {
7045        let case_insensitive = self.state_container.is_case_insensitive();
7046        if case_insensitive {
7047            spans.push(Span::raw(" | "));
7048            let icon = self.config.display.icons.case_insensitive.clone();
7049            spans.push(Span::styled(
7050                format!("{icon} CASE"),
7051                Style::default().fg(Color::Cyan),
7052            ));
7053        }
7054    }
7055
7056    fn add_column_packing_indicator(&self, spans: &mut Vec<Span>) {
7057        if let Some(ref viewport_manager) = *self.viewport_manager.borrow() {
7058            let packing_mode = viewport_manager.get_packing_mode();
7059            spans.push(Span::raw(" | "));
7060            let (text, color) = match packing_mode {
7061                ColumnPackingMode::DataFocus => ("DATA", Color::Cyan),
7062                ColumnPackingMode::HeaderFocus => ("HEADER", Color::Yellow),
7063                ColumnPackingMode::Balanced => ("BALANCED", Color::Green),
7064            };
7065            spans.push(Span::styled(text, Style::default().fg(color)));
7066        }
7067    }
7068
7069    fn add_status_message(&self, spans: &mut Vec<Span>) {
7070        let status_msg = self.state_container.get_buffer_status_message();
7071        if !status_msg.is_empty() {
7072            spans.push(Span::raw(" | "));
7073            spans.push(Span::styled(
7074                status_msg,
7075                Style::default()
7076                    .fg(Color::Yellow)
7077                    .add_modifier(Modifier::BOLD),
7078            ));
7079        }
7080    }
7081
7082    fn get_help_text_for_mode(&self) -> &str {
7083        match self.shadow_state.borrow().get_mode() {
7084            AppMode::Command => "Enter:Run | Tab:Complete | ↓:Results | F1:Help",
7085            AppMode::Results => match self.get_selection_mode() {
7086                SelectionMode::Cell => "v:Row mode | y:Yank cell | ↑:Edit | F1:Help",
7087                SelectionMode::Row => "v:Cell mode | y:Yank | f:Filter | ↑:Edit | F1:Help",
7088                SelectionMode::Column => "v:Cell mode | y:Yank col | ↑:Edit | F1:Help",
7089            },
7090            AppMode::Search | AppMode::Filter | AppMode::FuzzyFilter | AppMode::ColumnSearch => {
7091                "Enter:Apply | Esc:Cancel"
7092            }
7093            AppMode::Help | AppMode::Debug | AppMode::PrettyQuery | AppMode::ColumnStats => {
7094                "Esc:Close"
7095            }
7096            AppMode::History => "Enter:Select | Esc:Cancel",
7097            AppMode::JumpToRow => "Enter:Jump | Esc:Cancel",
7098        }
7099    }
7100
7101    fn add_shadow_state_display(&self, spans: &mut Vec<Span>) {
7102        let shadow_display = self.shadow_state.borrow().status_display();
7103        spans.push(Span::raw(" "));
7104        spans.push(Span::styled(
7105            shadow_display,
7106            Style::default().fg(Color::Cyan),
7107        ));
7108    }
7109
7110    /// Add right-aligned help text if space allows
7111    fn add_help_text_display<'a>(&self, spans: &mut Vec<Span<'a>>, help_text: &'a str, area: Rect) {
7112        let current_length: usize = spans.iter().map(|s| s.content.len()).sum();
7113        let available_width = area.width.saturating_sub(TABLE_BORDER_WIDTH) as usize;
7114        let help_length = help_text.len();
7115
7116        if current_length + help_length + 3 < available_width {
7117            let padding = available_width - current_length - help_length - 3;
7118            spans.push(Span::raw(" ".repeat(padding)));
7119            spans.push(Span::raw(" | "));
7120            spans.push(Span::styled(
7121                help_text,
7122                Style::default().fg(Color::DarkGray),
7123            ));
7124        }
7125    }
7126}
7127
7128// Implement ActionHandlerContext trait for EnhancedTuiApp
7129impl ActionHandlerContext for EnhancedTuiApp {
7130    // Navigation methods - delegate to trait implementations
7131    fn previous_row(&mut self) {
7132        <Self as NavigationBehavior>::previous_row(self);
7133
7134        // Update TableWidgetManager for rendering
7135        let current_row = self.state_container.navigation().selected_row;
7136        let current_col = self.state_container.navigation().selected_column;
7137        self.table_widget_manager
7138            .borrow_mut()
7139            .navigate_to(current_row, current_col);
7140        info!(target: "navigation", "previous_row: Updated TableWidgetManager to ({}, {})", current_row, current_col);
7141    }
7142
7143    fn next_row(&mut self) {
7144        info!(target: "navigation", "next_row called - calling NavigationBehavior::next_row");
7145        <Self as NavigationBehavior>::next_row(self);
7146
7147        // Update TableWidgetManager for rendering
7148        let current_row = self.state_container.navigation().selected_row;
7149        let current_col = self.state_container.navigation().selected_column;
7150        self.table_widget_manager
7151            .borrow_mut()
7152            .navigate_to(current_row, current_col);
7153        info!(target: "navigation", "next_row: Updated TableWidgetManager to ({}, {})", current_row, current_col);
7154    }
7155
7156    fn move_column_left(&mut self) {
7157        <Self as ColumnBehavior>::move_column_left(self);
7158
7159        // Update TableWidgetManager for rendering
7160        let current_row = self.state_container.navigation().selected_row;
7161        let current_col = self.state_container.navigation().selected_column;
7162        self.table_widget_manager
7163            .borrow_mut()
7164            .navigate_to(current_row, current_col);
7165        info!(target: "navigation", "move_column_left: Updated TableWidgetManager to ({}, {})", current_row, current_col);
7166    }
7167
7168    fn move_column_right(&mut self) {
7169        <Self as ColumnBehavior>::move_column_right(self);
7170
7171        // Update TableWidgetManager for rendering
7172        let current_row = self.state_container.navigation().selected_row;
7173        let current_col = self.state_container.navigation().selected_column;
7174        self.table_widget_manager
7175            .borrow_mut()
7176            .navigate_to(current_row, current_col);
7177        info!(target: "navigation", "move_column_right: Updated TableWidgetManager to ({}, {})", current_row, current_col);
7178    }
7179
7180    fn page_up(&mut self) {
7181        <Self as NavigationBehavior>::page_up(self);
7182    }
7183
7184    fn page_down(&mut self) {
7185        <Self as NavigationBehavior>::page_down(self);
7186    }
7187
7188    fn goto_first_row(&mut self) {
7189        use crate::ui::state::state_coordinator::StateCoordinator;
7190
7191        // Always perform the normal goto first row behavior
7192        <Self as NavigationBehavior>::goto_first_row(self);
7193
7194        // Use StateCoordinator to handle vim search coordination
7195        StateCoordinator::goto_first_row_with_refs(
7196            &mut self.state_container,
7197            Some(&self.vim_search_adapter),
7198            Some(&self.viewport_manager),
7199        );
7200    }
7201
7202    fn goto_last_row(&mut self) {
7203        use crate::ui::state::state_coordinator::StateCoordinator;
7204
7205        // Perform the normal goto last row behavior
7206        <Self as NavigationBehavior>::goto_last_row(self);
7207
7208        // Use StateCoordinator for additional coordination
7209        StateCoordinator::goto_last_row_with_refs(&mut self.state_container);
7210    }
7211
7212    fn goto_first_column(&mut self) {
7213        <Self as ColumnBehavior>::goto_first_column(self);
7214    }
7215
7216    fn goto_last_column(&mut self) {
7217        <Self as ColumnBehavior>::goto_last_column(self);
7218    }
7219
7220    fn goto_row(&mut self, row: usize) {
7221        use crate::ui::state::state_coordinator::StateCoordinator;
7222
7223        // Perform the normal goto line behavior
7224        <Self as NavigationBehavior>::goto_line(self, row + 1); // Convert to 1-indexed
7225
7226        // Use StateCoordinator for additional coordination
7227        StateCoordinator::goto_row_with_refs(&mut self.state_container, row);
7228    }
7229
7230    fn goto_column(&mut self, col: usize) {
7231        // For now, implement basic column navigation
7232        // TODO: Implement proper goto_column functionality
7233        let current_col = self.state_container.get_current_column();
7234        if col < current_col {
7235            for _ in 0..(current_col - col) {
7236                <Self as ColumnBehavior>::move_column_left(self);
7237            }
7238        } else if col > current_col {
7239            for _ in 0..(col - current_col) {
7240                <Self as ColumnBehavior>::move_column_right(self);
7241            }
7242        }
7243    }
7244
7245    // Mode and UI state
7246    fn set_mode(&mut self, mode: AppMode) {
7247        // Use the proper synchronization method that updates both buffer and shadow_state
7248        self.set_mode_via_shadow_state(mode, "action_handler");
7249    }
7250
7251    fn get_mode(&self) -> AppMode {
7252        self.shadow_state.borrow().get_mode()
7253    }
7254
7255    fn set_status_message(&mut self, message: String) {
7256        self.state_container.set_status_message(message);
7257    }
7258
7259    // Column operations - delegate to trait implementations
7260    fn toggle_column_pin(&mut self) {
7261        // Call the existing toggle_column_pin implementation directly
7262        self.toggle_column_pin_impl();
7263    }
7264
7265    fn hide_current_column(&mut self) {
7266        <Self as ColumnBehavior>::hide_current_column(self);
7267    }
7268
7269    fn unhide_all_columns(&mut self) {
7270        <Self as ColumnBehavior>::unhide_all_columns(self);
7271    }
7272
7273    fn clear_all_pinned_columns(&mut self) {
7274        // Call the existing clear_all_pinned_columns implementation directly
7275        self.clear_all_pinned_columns_impl();
7276    }
7277
7278    // Export operations
7279    fn export_to_csv(&mut self) {
7280        // For now, just set a status message - actual implementation will be added later
7281        self.state_container
7282            .set_status_message("CSV export not yet implemented".to_string());
7283    }
7284
7285    fn export_to_json(&mut self) {
7286        // For now, just set a status message - actual implementation will be added later
7287        self.state_container
7288            .set_status_message("JSON export not yet implemented".to_string());
7289    }
7290
7291    // Yank operations - delegate to trait implementations
7292    fn yank_cell(&mut self) {
7293        YankBehavior::yank_cell(self);
7294    }
7295
7296    fn yank_row(&mut self) {
7297        YankBehavior::yank_row(self);
7298    }
7299
7300    fn yank_column(&mut self) {
7301        YankBehavior::yank_column(self);
7302    }
7303
7304    fn yank_all(&mut self) {
7305        YankBehavior::yank_all(self);
7306    }
7307
7308    fn yank_query(&mut self) {
7309        YankBehavior::yank_query(self);
7310    }
7311
7312    // Toggle operations
7313    fn toggle_selection_mode(&mut self) {
7314        self.state_container.toggle_selection_mode();
7315        let new_mode = self.state_container.get_selection_mode();
7316        let msg = match new_mode {
7317            SelectionMode::Cell => "Cell mode - Navigate to select individual cells",
7318            SelectionMode::Row => "Row mode - Navigate to select rows",
7319            SelectionMode::Column => "Column mode - Navigate to select columns",
7320        };
7321        self.state_container.set_status_message(msg.to_string());
7322    }
7323
7324    fn toggle_row_numbers(&mut self) {
7325        let current = self.state_container.is_show_row_numbers();
7326        self.state_container.set_show_row_numbers(!current);
7327        let message = if current {
7328            "Row numbers: OFF".to_string()
7329        } else {
7330            "Row numbers: ON (showing line numbers)".to_string()
7331        };
7332        self.state_container.set_status_message(message);
7333        // Recalculate column widths with new mode
7334        self.calculate_optimal_column_widths();
7335    }
7336
7337    fn toggle_compact_mode(&mut self) {
7338        let current_mode = self.state_container.is_compact_mode();
7339        self.state_container.set_compact_mode(!current_mode);
7340        let message = if current_mode {
7341            "Compact mode disabled"
7342        } else {
7343            "Compact mode enabled"
7344        };
7345        self.state_container.set_status_message(message.to_string());
7346    }
7347
7348    fn toggle_case_insensitive(&mut self) {
7349        let current = self.state_container.is_case_insensitive();
7350        self.state_container.set_case_insensitive(!current);
7351        self.state_container.set_status_message(format!(
7352            "Case-insensitive string comparisons: {}",
7353            if current { "OFF" } else { "ON" }
7354        ));
7355    }
7356
7357    fn toggle_key_indicator(&mut self) {
7358        let enabled = !self.key_indicator.enabled;
7359        self.key_indicator.set_enabled(enabled);
7360        self.key_sequence_renderer.set_enabled(enabled);
7361        self.state_container.set_status_message(format!(
7362            "Key press indicator {}",
7363            if enabled { "enabled" } else { "disabled" }
7364        ));
7365    }
7366
7367    // Clear operations
7368    fn clear_filter(&mut self) {
7369        // Check if we have an active filter to clear
7370        if let Some(dataview) = self.state_container.get_buffer_dataview() {
7371            if dataview.has_filter() {
7372                // Clear the filter
7373                if let Some(dataview_mut) = self.state_container.get_buffer_dataview_mut() {
7374                    dataview_mut.clear_filter();
7375                    self.state_container
7376                        .set_status_message("Filter cleared".to_string());
7377                }
7378
7379                // Update ViewportManager after clearing filter
7380                // Sync the dataview to both managers
7381                self.sync_dataview_to_managers();
7382            } else {
7383                self.state_container
7384                    .set_status_message("No active filter to clear".to_string());
7385            }
7386        } else {
7387            self.state_container
7388                .set_status_message("No data loaded".to_string());
7389        }
7390    }
7391
7392    fn clear_line(&mut self) {
7393        self.state_container.clear_line();
7394    }
7395
7396    // Mode operations
7397    fn start_search(&mut self) {
7398        self.start_vim_search();
7399    }
7400
7401    fn start_column_search(&mut self) {
7402        self.enter_search_mode(SearchMode::ColumnSearch);
7403    }
7404
7405    fn start_filter(&mut self) {
7406        self.enter_search_mode(SearchMode::Filter);
7407    }
7408
7409    fn start_fuzzy_filter(&mut self) {
7410        self.enter_search_mode(SearchMode::FuzzyFilter);
7411    }
7412
7413    fn exit_current_mode(&mut self) {
7414        // Handle escape based on current mode.
7415        // All transitions must go through set_mode_via_shadow_state so that
7416        // shadow_state and the buffer mode stay in sync. Updating only
7417        // state_container.set_mode(...) leaves shadow_state stale, which
7418        // causes rendering (which reads shadow_state) to diverge from key
7419        // dispatch (which reads state_container) — e.g. grey input box while
7420        // j/k get appended to the query.
7421        let mode = self.shadow_state.borrow().get_mode();
7422        match mode {
7423            AppMode::Results => {
7424                // VimSearchAdapter now handles Escape in Results mode when search is active
7425                // If we get here, it means search wasn't active, so switch to Command mode
7426                self.set_mode_via_shadow_state(AppMode::Command, "escape_from_results");
7427            }
7428            AppMode::Command => {
7429                self.set_mode_via_shadow_state(AppMode::Results, "escape_from_command");
7430            }
7431            AppMode::Help => {
7432                self.set_mode_via_shadow_state(AppMode::Results, "escape_from_help");
7433            }
7434            AppMode::JumpToRow => {
7435                self.set_mode_via_shadow_state(AppMode::Results, "escape_from_jump_to_row");
7436                <Self as InputBehavior>::clear_jump_to_row_input(self);
7437                // Clear jump-to-row state (can mutate directly now)
7438                self.state_container.jump_to_row_mut().is_active = false;
7439                self.state_container
7440                    .set_status_message("Jump to row cancelled".to_string());
7441            }
7442            _ => {
7443                // For any other mode, return to Results
7444                self.set_mode_via_shadow_state(AppMode::Results, "escape_to_results");
7445            }
7446        }
7447    }
7448
7449    fn toggle_debug_mode(&mut self) {
7450        // Use the DebugContext trait
7451        <Self as DebugContext>::toggle_debug_mode(self);
7452    }
7453
7454    // Column arrangement operations
7455    fn move_current_column_left(&mut self) {
7456        <Self as ColumnBehavior>::move_current_column_left(self);
7457    }
7458
7459    fn move_current_column_right(&mut self) {
7460        <Self as ColumnBehavior>::move_current_column_right(self);
7461    }
7462
7463    // Search navigation
7464    fn next_search_match(&mut self) {
7465        self.vim_search_next();
7466    }
7467
7468    fn previous_search_match(&mut self) {
7469        if self.vim_search_adapter.borrow().is_active()
7470            || self.vim_search_adapter.borrow().get_pattern().is_some()
7471        {
7472            self.vim_search_previous();
7473        }
7474    }
7475
7476    // Statistics and display
7477    fn show_column_statistics(&mut self) {
7478        self.calculate_column_statistics();
7479    }
7480
7481    fn cycle_column_packing(&mut self) {
7482        let message = {
7483            let mut viewport_manager_borrow = self.viewport_manager.borrow_mut();
7484            let viewport_manager = viewport_manager_borrow
7485                .as_mut()
7486                .expect("ViewportManager must exist");
7487            let new_mode = viewport_manager.cycle_packing_mode();
7488            format!("Column packing: {}", new_mode.display_name())
7489        };
7490        self.state_container.set_status_message(message);
7491    }
7492
7493    // Viewport navigation
7494    fn navigate_to_viewport_top(&mut self) {
7495        let result = {
7496            let mut viewport_manager_borrow = self.viewport_manager.borrow_mut();
7497            (*viewport_manager_borrow)
7498                .as_mut()
7499                .map(super::viewport_manager::ViewportManager::navigate_to_viewport_top)
7500        };
7501
7502        if let Some(result) = result {
7503            // ViewportManager has updated, sync NavigationState from it
7504            self.sync_navigation_with_viewport();
7505
7506            // Update buffer's selected row
7507            self.state_container
7508                .set_selected_row(Some(result.row_position));
7509
7510            // Update buffer's scroll offset
7511            if result.viewport_changed {
7512                let scroll_offset = self.state_container.navigation().scroll_offset;
7513                self.state_container.set_scroll_offset(scroll_offset);
7514            }
7515        }
7516    }
7517
7518    fn navigate_to_viewport_middle(&mut self) {
7519        let result = {
7520            let mut viewport_manager_borrow = self.viewport_manager.borrow_mut();
7521            (*viewport_manager_borrow)
7522                .as_mut()
7523                .map(super::viewport_manager::ViewportManager::navigate_to_viewport_middle)
7524        };
7525
7526        if let Some(result) = result {
7527            // ViewportManager has updated, sync NavigationState from it
7528            self.sync_navigation_with_viewport();
7529
7530            // Update buffer's selected row
7531            self.state_container
7532                .set_selected_row(Some(result.row_position));
7533
7534            // Update buffer's scroll offset
7535            if result.viewport_changed {
7536                let scroll_offset = self.state_container.navigation().scroll_offset;
7537                self.state_container.set_scroll_offset(scroll_offset);
7538            }
7539        }
7540    }
7541
7542    fn navigate_to_viewport_bottom(&mut self) {
7543        let result = {
7544            let mut viewport_manager_borrow = self.viewport_manager.borrow_mut();
7545            (*viewport_manager_borrow)
7546                .as_mut()
7547                .map(super::viewport_manager::ViewportManager::navigate_to_viewport_bottom)
7548        };
7549
7550        if let Some(result) = result {
7551            // ViewportManager has updated, sync NavigationState from it
7552            self.sync_navigation_with_viewport();
7553
7554            // Update buffer's selected row
7555            self.state_container
7556                .set_selected_row(Some(result.row_position));
7557
7558            // Update buffer's scroll offset
7559            if result.viewport_changed {
7560                let scroll_offset = self.state_container.navigation().scroll_offset;
7561                self.state_container.set_scroll_offset(scroll_offset);
7562            }
7563        }
7564    }
7565
7566    // Input and text editing methods
7567    fn move_input_cursor_left(&mut self) {
7568        self.state_container.move_input_cursor_left();
7569    }
7570
7571    fn move_input_cursor_right(&mut self) {
7572        self.state_container.move_input_cursor_right();
7573    }
7574
7575    fn move_input_cursor_home(&mut self) {
7576        self.state_container.set_input_cursor_position(0);
7577    }
7578
7579    fn move_input_cursor_end(&mut self) {
7580        let text_len = self.state_container.get_input_text().chars().count();
7581        self.state_container.set_input_cursor_position(text_len);
7582    }
7583
7584    fn backspace(&mut self) {
7585        self.state_container.backspace();
7586    }
7587
7588    fn delete(&mut self) {
7589        self.state_container.delete();
7590    }
7591
7592    fn undo(&mut self) {
7593        self.state_container.perform_undo();
7594    }
7595
7596    fn redo(&mut self) {
7597        self.state_container.perform_redo();
7598    }
7599
7600    fn start_jump_to_row(&mut self) {
7601        self.state_container.set_mode(AppMode::JumpToRow);
7602        self.shadow_state
7603            .borrow_mut()
7604            .observe_mode_change(AppMode::JumpToRow, "jump_to_row_requested");
7605        <Self as InputBehavior>::clear_jump_to_row_input(self);
7606
7607        // Set jump-to-row state as active (can mutate directly now)
7608        self.state_container.jump_to_row_mut().is_active = true;
7609
7610        self.state_container
7611            .set_status_message("Enter row number (1-based):".to_string());
7612    }
7613
7614    fn clear_jump_to_row_input(&mut self) {
7615        <Self as InputBehavior>::clear_jump_to_row_input(self);
7616    }
7617
7618    fn toggle_cursor_lock(&mut self) {
7619        // Toggle cursor lock in ViewportManager
7620        let is_locked = {
7621            let mut viewport_manager_borrow = self.viewport_manager.borrow_mut();
7622            if let Some(ref mut viewport_manager) = *viewport_manager_borrow {
7623                viewport_manager.toggle_cursor_lock();
7624                Some(viewport_manager.is_cursor_locked())
7625            } else {
7626                None
7627            }
7628        };
7629
7630        if let Some(is_locked) = is_locked {
7631            let msg = if is_locked {
7632                "Cursor lock ON - cursor stays in viewport position while scrolling"
7633            } else {
7634                "Cursor lock OFF"
7635            };
7636            self.state_container.set_status_message(msg.to_string());
7637
7638            // Log for shadow state learning (not tracking as state change yet)
7639            info!(target: "shadow_state",
7640                "Cursor lock toggled: {} (in {:?} mode)",
7641                if is_locked { "ON" } else { "OFF" },
7642                self.shadow_state.borrow().get_mode()
7643            );
7644        }
7645    }
7646
7647    fn toggle_viewport_lock(&mut self) {
7648        // Toggle viewport lock in ViewportManager
7649        let is_locked = {
7650            let mut viewport_manager_borrow = self.viewport_manager.borrow_mut();
7651            if let Some(ref mut viewport_manager) = *viewport_manager_borrow {
7652                viewport_manager.toggle_viewport_lock();
7653                Some(viewport_manager.is_viewport_locked())
7654            } else {
7655                None
7656            }
7657        };
7658
7659        if let Some(is_locked) = is_locked {
7660            let msg = if is_locked {
7661                "Viewport lock ON - navigation constrained to current viewport"
7662            } else {
7663                "Viewport lock OFF"
7664            };
7665            self.state_container.set_status_message(msg.to_string());
7666
7667            // Log for shadow state learning (not tracking as state change yet)
7668            info!(target: "shadow_state",
7669                "Viewport lock toggled: {} (in {:?} mode)",
7670                if is_locked { "ON" } else { "OFF" },
7671                self.shadow_state.borrow().get_mode()
7672            );
7673        }
7674    }
7675
7676    // Debug and development operations
7677    fn show_debug_info(&mut self) {
7678        <Self as DebugContext>::toggle_debug_mode(self);
7679    }
7680
7681    fn show_pretty_query(&mut self) {
7682        self.show_pretty_query();
7683    }
7684
7685    fn show_help(&mut self) {
7686        self.state_container.set_help_visible(true);
7687        self.set_mode_via_shadow_state(AppMode::Help, "help_requested");
7688        self.help_widget.on_enter();
7689    }
7690
7691    // Text editing operations
7692    fn kill_line(&mut self) {
7693        use crate::ui::traits::input_ops::InputBehavior;
7694        InputBehavior::kill_line(self);
7695        let message = if self.state_container.is_kill_ring_empty() {
7696            "Kill line - nothing to kill".to_string()
7697        } else {
7698            let kill_ring = self.state_container.get_kill_ring();
7699            format!(
7700                "Killed to end of line - {} chars in kill ring",
7701                kill_ring.len()
7702            )
7703        };
7704        self.state_container.set_status_message(message);
7705    }
7706
7707    fn kill_line_backward(&mut self) {
7708        use crate::ui::traits::input_ops::InputBehavior;
7709        InputBehavior::kill_line_backward(self);
7710        let message = if self.state_container.is_kill_ring_empty() {
7711            "Kill line backward - nothing to kill".to_string()
7712        } else {
7713            let kill_ring = self.state_container.get_kill_ring();
7714            format!(
7715                "Killed to beginning of line - {} chars in kill ring",
7716                kill_ring.len()
7717            )
7718        };
7719        self.state_container.set_status_message(message);
7720    }
7721
7722    fn delete_word_backward(&mut self) {
7723        use crate::ui::traits::input_ops::InputBehavior;
7724        InputBehavior::delete_word_backward(self);
7725    }
7726
7727    fn delete_word_forward(&mut self) {
7728        use crate::ui::traits::input_ops::InputBehavior;
7729        InputBehavior::delete_word_forward(self);
7730    }
7731
7732    fn jump_to_prev_token(&mut self) {
7733        use crate::ui::traits::input_ops::InputBehavior;
7734        InputBehavior::jump_to_prev_token(self);
7735    }
7736
7737    fn jump_to_next_token(&mut self) {
7738        use crate::ui::traits::input_ops::InputBehavior;
7739        InputBehavior::jump_to_next_token(self);
7740    }
7741
7742    fn expand_asterisk(&mut self) {
7743        if let Some(buffer) = self.state_container.buffers_mut().current_mut() {
7744            if buffer.expand_asterisk(&self.hybrid_parser) {
7745                // Sync for rendering if needed
7746                if buffer.get_edit_mode() == EditMode::SingleLine {
7747                    let text = buffer.get_input_text();
7748                    let cursor = buffer.get_input_cursor_position();
7749                    self.set_input_text_with_cursor(text, cursor);
7750                }
7751            }
7752        }
7753    }
7754
7755    fn expand_asterisk_visible(&mut self) {
7756        if let Some(buffer) = self.state_container.buffers_mut().current_mut() {
7757            if buffer.expand_asterisk_visible() {
7758                // Sync for rendering if needed
7759                if buffer.get_edit_mode() == EditMode::SingleLine {
7760                    let text = buffer.get_input_text();
7761                    let cursor = buffer.get_input_cursor_position();
7762                    self.set_input_text_with_cursor(text, cursor);
7763                }
7764            }
7765        }
7766    }
7767
7768    fn previous_history_command(&mut self) {
7769        let history_entries = self
7770            .state_container
7771            .command_history()
7772            .get_navigation_entries();
7773        let history_commands: Vec<String> =
7774            history_entries.iter().map(|e| e.command.clone()).collect();
7775
7776        if let Some(buffer) = self.state_container.buffers_mut().current_mut() {
7777            if buffer.navigate_history_up(&history_commands) {
7778                self.sync_all_input_states();
7779                self.state_container
7780                    .set_status_message("Previous command from history".to_string());
7781            }
7782        }
7783    }
7784
7785    fn next_history_command(&mut self) {
7786        let history_entries = self
7787            .state_container
7788            .command_history()
7789            .get_navigation_entries();
7790        let history_commands: Vec<String> =
7791            history_entries.iter().map(|e| e.command.clone()).collect();
7792
7793        if let Some(buffer) = self.state_container.buffers_mut().current_mut() {
7794            if buffer.navigate_history_down(&history_commands) {
7795                self.sync_all_input_states();
7796                self.state_container
7797                    .set_status_message("Next command from history".to_string());
7798            }
7799        }
7800    }
7801}
7802
7803// Implement NavigationBehavior trait for EnhancedTuiApp
7804impl NavigationBehavior for EnhancedTuiApp {
7805    fn viewport_manager(&self) -> &RefCell<Option<ViewportManager>> {
7806        &self.viewport_manager
7807    }
7808
7809    fn buffer_mut(&mut self) -> &mut dyn BufferAPI {
7810        self.state_container
7811            .current_buffer_mut()
7812            .expect("Buffer should exist")
7813    }
7814
7815    fn buffer(&self) -> &dyn BufferAPI {
7816        self.state_container
7817            .current_buffer()
7818            .expect("Buffer should exist")
7819    }
7820
7821    fn state_container(&self) -> &AppStateContainer {
7822        &self.state_container
7823    }
7824
7825    fn state_container_mut(&mut self) -> &mut AppStateContainer {
7826        &mut self.state_container
7827    }
7828
7829    fn get_row_count(&self) -> usize {
7830        self.get_row_count()
7831    }
7832
7833    fn set_mode_with_sync(&mut self, mode: AppMode, trigger: &str) {
7834        // Use the existing method that properly synchronizes both buffer and shadow_state
7835        self.set_mode_via_shadow_state(mode, trigger);
7836    }
7837}
7838
7839// Implement ColumnBehavior trait for EnhancedTuiApp
7840impl ColumnBehavior for EnhancedTuiApp {
7841    fn viewport_manager(&self) -> &RefCell<Option<ViewportManager>> {
7842        &self.viewport_manager
7843    }
7844
7845    fn buffer_mut(&mut self) -> &mut dyn BufferAPI {
7846        self.state_container
7847            .current_buffer_mut()
7848            .expect("Buffer should exist")
7849    }
7850
7851    fn buffer(&self) -> &dyn BufferAPI {
7852        self.state_container
7853            .current_buffer()
7854            .expect("Buffer should exist")
7855    }
7856
7857    fn state_container(&self) -> &AppStateContainer {
7858        &self.state_container
7859    }
7860
7861    fn is_in_results_mode(&self) -> bool {
7862        self.shadow_state.borrow().is_in_results_mode()
7863    }
7864}
7865
7866impl InputBehavior for EnhancedTuiApp {
7867    fn buffer_manager(&mut self) -> &mut BufferManager {
7868        self.state_container.buffers_mut()
7869    }
7870
7871    fn cursor_manager(&mut self) -> &mut CursorManager {
7872        &mut self.cursor_manager
7873    }
7874
7875    fn set_input_text_with_cursor(&mut self, text: String, cursor: usize) {
7876        self.set_input_text_with_cursor(text, cursor);
7877    }
7878
7879    fn state_container(&self) -> &AppStateContainer {
7880        &self.state_container
7881    }
7882
7883    fn state_container_mut(&mut self) -> &mut AppStateContainer {
7884        &mut self.state_container
7885    }
7886
7887    fn buffer_mut(&mut self) -> &mut dyn BufferAPI {
7888        self.state_container
7889            .current_buffer_mut()
7890            .expect("Buffer should exist")
7891    }
7892
7893    fn set_mode_with_sync(&mut self, mode: AppMode, trigger: &str) {
7894        // Use the existing method that properly synchronizes both buffer and shadow_state
7895        self.set_mode_via_shadow_state(mode, trigger);
7896    }
7897}
7898
7899impl YankBehavior for EnhancedTuiApp {
7900    fn buffer(&self) -> &dyn BufferAPI {
7901        self.state_container
7902            .current_buffer()
7903            .expect("Buffer should exist")
7904    }
7905
7906    fn buffer_mut(&mut self) -> &mut dyn BufferAPI {
7907        self.state_container
7908            .current_buffer_mut()
7909            .expect("Buffer should exist")
7910    }
7911
7912    fn state_container(&self) -> &AppStateContainer {
7913        &self.state_container
7914    }
7915
7916    fn set_status_message(&mut self, message: String) {
7917        self.state_container.set_status_message(message);
7918    }
7919
7920    fn set_error_status(&mut self, prefix: &str, error: anyhow::Error) {
7921        self.set_error_status(prefix, error);
7922    }
7923}
7924
7925impl BufferManagementBehavior for EnhancedTuiApp {
7926    fn buffer_manager(&mut self) -> &mut BufferManager {
7927        self.state_container.buffers_mut()
7928    }
7929
7930    fn buffer_handler(&mut self) -> &mut BufferHandler {
7931        &mut self.buffer_handler
7932    }
7933
7934    fn buffer(&self) -> &dyn BufferAPI {
7935        self.state_container
7936            .current_buffer()
7937            .expect("Buffer should exist")
7938    }
7939
7940    fn buffer_mut(&mut self) -> &mut dyn BufferAPI {
7941        self.state_container
7942            .current_buffer_mut()
7943            .expect("Buffer should exist")
7944    }
7945
7946    fn config(&self) -> &Config {
7947        &self.config
7948    }
7949
7950    fn cursor_manager(&mut self) -> &mut CursorManager {
7951        &mut self.cursor_manager
7952    }
7953
7954    fn set_input_text_with_cursor(&mut self, text: String, cursor: usize) {
7955        self.set_input_text_with_cursor(text, cursor);
7956    }
7957
7958    fn next_buffer(&mut self) -> String {
7959        // Save current viewport state to current buffer before switching
7960        self.save_viewport_to_current_buffer();
7961
7962        let result = self
7963            .buffer_handler
7964            .next_buffer(self.state_container.buffers_mut());
7965
7966        // Sync all state after buffer switch
7967        self.sync_after_buffer_switch();
7968
7969        result
7970    }
7971
7972    fn previous_buffer(&mut self) -> String {
7973        // Save current viewport state to current buffer before switching
7974        self.save_viewport_to_current_buffer();
7975
7976        let result = self
7977            .buffer_handler
7978            .previous_buffer(self.state_container.buffers_mut());
7979
7980        // Sync all state after buffer switch
7981        self.sync_after_buffer_switch();
7982
7983        result
7984    }
7985
7986    fn quick_switch_buffer(&mut self) -> String {
7987        // Save current viewport state to current buffer before switching
7988        self.save_viewport_to_current_buffer();
7989
7990        let result = self
7991            .buffer_handler
7992            .quick_switch(self.state_container.buffers_mut());
7993
7994        // Sync all state after buffer switch
7995        self.sync_after_buffer_switch();
7996
7997        result
7998    }
7999
8000    fn close_buffer(&mut self) -> (bool, String) {
8001        self.buffer_handler
8002            .close_buffer(self.state_container.buffers_mut())
8003    }
8004
8005    fn switch_to_buffer(&mut self, index: usize) -> String {
8006        // Save current viewport state to current buffer before switching
8007        self.save_viewport_to_current_buffer();
8008
8009        // Switch buffer
8010        let result = self
8011            .buffer_handler
8012            .switch_to_buffer(self.state_container.buffers_mut(), index);
8013
8014        // Sync all state after buffer switch
8015        self.sync_after_buffer_switch();
8016
8017        result
8018    }
8019
8020    fn buffer_count(&self) -> usize {
8021        self.state_container.buffers().all_buffers().len()
8022    }
8023
8024    fn current_buffer_index(&self) -> usize {
8025        self.state_container.buffers().current_index()
8026    }
8027}
8028
8029pub fn run_enhanced_tui_multi(api_url: &str, data_files: Vec<&str>) -> Result<()> {
8030    // Set up panic hook to restore terminal on panic
8031    let original_hook = std::panic::take_hook();
8032    std::panic::set_hook(Box::new(move |panic_info| {
8033        // Restore terminal
8034        let _ = disable_raw_mode();
8035        let _ = execute!(
8036            io::stdout(),
8037            LeaveAlternateScreen,
8038            DisableMouseCapture,
8039            crossterm::cursor::Show
8040        );
8041
8042        // Call the original panic hook
8043        original_hook(panic_info);
8044    }));
8045
8046    let app = if data_files.is_empty() {
8047        EnhancedTuiApp::new(api_url)
8048    } else {
8049        // Use ApplicationOrchestrator for clean data source separation
8050        use crate::services::ApplicationOrchestrator;
8051
8052        // Get config for orchestrator setup
8053        let config = Config::default();
8054        let orchestrator = ApplicationOrchestrator::new(
8055            config.behavior.case_insensitive_default,
8056            config.behavior.hide_empty_columns,
8057        );
8058
8059        // Load the first file through the orchestrator
8060        let mut app = orchestrator.create_tui_with_file(data_files[0])?;
8061
8062        // Load additional files into separate buffers
8063        for file_path in data_files.iter().skip(1) {
8064            if let Err(e) = orchestrator.load_additional_file(&mut app, file_path) {
8065                app.state_container
8066                    .set_status_message(format!("Error loading {file_path}: {e}"));
8067                continue;
8068            }
8069        }
8070
8071        // Switch back to the first buffer if we loaded multiple
8072        if data_files.len() > 1 {
8073            app.state_container.buffers_mut().switch_to(0);
8074            // Sync all state after buffer switch
8075            app.sync_after_buffer_switch();
8076            app.state_container.set_status_message(format!(
8077                "Loaded {} files into separate buffers. Use Alt+Tab to switch.",
8078                data_files.len()
8079            ));
8080        } else if data_files.len() == 1 {
8081            // Even for single file, ensure parser is initialized
8082            app.update_parser_for_current_buffer();
8083        }
8084
8085        app
8086    };
8087
8088    let result = app.run();
8089
8090    // Restore the original panic hook
8091    let _ = std::panic::take_hook(); // Drop our hook
8092                                     // Note: We can't restore the original hook here as it was moved into the closure
8093
8094    result
8095}
8096
8097pub fn run_enhanced_tui(api_url: &str, data_file: Option<&str>) -> Result<()> {
8098    // For backward compatibility, convert single file to vec and call multi version
8099    let files = if let Some(file) = data_file {
8100        vec![file]
8101    } else {
8102        vec![]
8103    };
8104    run_enhanced_tui_multi(api_url, files)
8105}