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, process normally
1883                self.handle_results_input(single_key)
1884            }
1885        }
1886    }
1887
1888    /// Dispatch key to appropriate mode handler, returns true if exit is requested
1889    fn try_handle_mode_dispatch(&mut self, key: crossterm::event::KeyEvent) -> Result<bool> {
1890        let mode = self.shadow_state.borrow().get_mode();
1891        debug!(
1892            "try_handle_mode_dispatch: mode={:?}, key={:?}",
1893            mode, key.code
1894        );
1895        match mode {
1896            AppMode::Command => self.handle_command_input(key),
1897            AppMode::Results => {
1898                // Results mode uses chord processing
1899                self.try_handle_chord_processing(key)
1900            }
1901            AppMode::Search | AppMode::Filter | AppMode::FuzzyFilter | AppMode::ColumnSearch => {
1902                self.handle_search_modes_input(key)
1903            }
1904            AppMode::Help => self.handle_help_input(key),
1905            AppMode::History => self.handle_history_input(key),
1906            AppMode::Debug => self.handle_debug_input(key),
1907            AppMode::PrettyQuery => self.handle_pretty_query_input(key),
1908            AppMode::JumpToRow => self.handle_jump_to_row_input(key),
1909            AppMode::ColumnStats => self.handle_column_stats_input(key),
1910        }
1911    }
1912
1913    /// Handle key event processing, returns true if exit is requested
1914    fn try_handle_key_event<B: Backend>(
1915        &mut self,
1916        terminal: &mut Terminal<B>,
1917        key: crossterm::event::KeyEvent,
1918    ) -> Result<bool> {
1919        // On Windows, filter out key release events - only handle key press
1920        // This prevents double-triggering of toggles
1921        if key.kind != crossterm::event::KeyEventKind::Press {
1922            return Ok(false);
1923        }
1924
1925        // SAFETY: Always allow Ctrl-C to exit, regardless of app state
1926        // This prevents getting stuck in unresponsive states
1927        if key.code == KeyCode::Char('c') && key.modifiers.contains(KeyModifiers::CONTROL) {
1928            info!(target: "app", "Ctrl-C detected, forcing exit");
1929            return Ok(true);
1930        }
1931
1932        // Record key press for visual indicator
1933        let key_display = format_key_for_display(&key);
1934        self.key_indicator.record_key(key_display.clone());
1935        self.key_sequence_renderer.record_key(key_display);
1936
1937        // Dispatch to appropriate mode handler
1938        let should_exit = self.try_handle_mode_dispatch(key)?;
1939
1940        if should_exit {
1941            return Ok(true);
1942        }
1943
1944        // Only redraw after handling a key event OR if TableWidgetManager needs render
1945        if self.table_widget_manager.borrow().needs_render() {
1946            info!("TableWidgetManager needs render after key event");
1947        }
1948        terminal.draw(|f| self.ui(f))?;
1949        self.table_widget_manager.borrow_mut().rendered();
1950
1951        Ok(false)
1952    }
1953
1954    /// Handle all events, returns true if exit is requested
1955    fn try_handle_events<B: Backend>(&mut self, terminal: &mut Terminal<B>) -> Result<bool> {
1956        // Use poll with timeout to allow checking for debounced actions
1957        if event::poll(std::time::Duration::from_millis(50))? {
1958            if let Event::Key(key) = event::read()? {
1959                if self.try_handle_key_event(terminal, key)? {
1960                    return Ok(true);
1961                }
1962            } else {
1963                // Ignore other events (mouse, resize, etc.) to reduce CPU
1964            }
1965        } else {
1966            // No event available, but still redraw if we have pending debounced actions or table needs render
1967            if self.search_modes_widget.is_active()
1968                || self.table_widget_manager.borrow().needs_render()
1969            {
1970                if self.table_widget_manager.borrow().needs_render() {
1971                    info!("TableWidgetManager needs periodic render");
1972                }
1973                terminal.draw(|f| self.ui(f))?;
1974                self.table_widget_manager.borrow_mut().rendered();
1975            }
1976        }
1977        Ok(false)
1978    }
1979
1980    fn run_app<B: Backend>(&mut self, terminal: &mut Terminal<B>) -> Result<()> {
1981        self.initialize_viewport(terminal)?;
1982
1983        loop {
1984            // Handle debounced search actions
1985            if self.try_handle_debounced_actions(terminal)? {
1986                break;
1987            }
1988
1989            // Handle all events (key presses, etc.)
1990            if self.try_handle_events(terminal)? {
1991                break;
1992            }
1993        }
1994        Ok(())
1995    }
1996
1997    fn handle_command_input(&mut self, key: crossterm::event::KeyEvent) -> Result<bool> {
1998        // Normalize and log the key
1999        let normalized_key = self.normalize_and_log_key(key);
2000
2001        // === PHASE 1: Try CommandEditor for text input and editing ===
2002        // IMPORTANT: This must come BEFORE try_action_system to properly intercept keys
2003        // We handle comprehensive text editing operations
2004        // Let CommandEditor handle most text-related keys (except Tab which needs full TUI state)
2005
2006        // Check for special Ctrl/Alt combinations that should NOT go to CommandEditor
2007        let is_special_combo = if let KeyCode::Char(c) = normalized_key.code {
2008            // Special Ctrl combinations
2009            (normalized_key.modifiers.contains(KeyModifiers::CONTROL) && matches!(c,
2010                'x' | 'X' | // Expand asterisk
2011                'p' | 'P' | // Previous history  
2012                'n' | 'N' | // Next history
2013                'r' | 'R' | // History search
2014                'j' | 'J' | // Export JSON
2015                'o' | 'O' | // Open buffer
2016                'b' | 'B' | // Buffer operations
2017                'l' | 'L'   // Clear screen
2018            )) ||
2019            // Special Alt combinations that aren't word navigation
2020            (normalized_key.modifiers.contains(KeyModifiers::ALT) && matches!(c,
2021                'x' | 'X' |   // Expand asterisk visible only
2022                '[' | ']' |   // Jump to prev/next SQL token
2023                ',' | '.'     // Jump prev/next token (terminal-safe aliases)
2024            ))
2025        } else {
2026            false
2027        };
2028
2029        let should_try_command_editor = !is_special_combo
2030            && matches!(
2031                normalized_key.code,
2032                KeyCode::Char(_)
2033                    | KeyCode::Backspace
2034                    | KeyCode::Delete
2035                    | KeyCode::Left
2036                    | KeyCode::Right
2037                    | KeyCode::Home
2038                    | KeyCode::End
2039            );
2040
2041        if should_try_command_editor {
2042            // IMPORTANT: Sync state TO CommandEditor before processing
2043            // This ensures CommandEditor has the current text/cursor position
2044            let before_text = self.input.value().to_string();
2045            let before_cursor = self.input.cursor();
2046
2047            if self.command_editor.get_text() != before_text {
2048                self.command_editor.set_text(before_text.clone());
2049            }
2050            if self.command_editor.get_cursor() != before_cursor {
2051                self.command_editor.set_cursor(before_cursor);
2052            }
2053
2054            // Now handle the input with proper state
2055            let result = self.command_editor.handle_input(
2056                normalized_key,
2057                &mut self.state_container,
2058                &self.shadow_state,
2059            )?;
2060
2061            // Sync the text back to the main input after processing
2062            // (This is temporary until we fully migrate)
2063            let new_text = self.command_editor.get_text();
2064            let new_cursor = self.command_editor.get_cursor();
2065
2066            // Debug logging to see what's happening
2067            if new_text != before_text || new_cursor != before_cursor {
2068                debug!(
2069                    "CommandEditor changed input: '{}' -> '{}', cursor: {} -> {}",
2070                    before_text, new_text, before_cursor, new_cursor
2071                );
2072            }
2073
2074            // Use from() instead of new() to preserve any internal state
2075            self.input = tui_input::Input::from(new_text.clone()).with_cursor(new_cursor);
2076
2077            // CRITICAL: Update the buffer, not just command_input!
2078            // The rendering uses get_buffer_input_text() which reads from the buffer
2079            if let Some(buffer) = self.state_container.buffers_mut().current_mut() {
2080                buffer.set_input_text(new_text.clone());
2081                buffer.set_input_cursor_position(new_cursor);
2082            }
2083
2084            // Also update command_input for consistency
2085            self.state_container.set_input_text(new_text);
2086            self.state_container.set_input_cursor_position(new_cursor);
2087
2088            if result {
2089                return Ok(true);
2090            }
2091
2092            // For char/backspace, we've handled it - don't let action system process it again
2093            return Ok(false);
2094        }
2095        // === END PHASE 1 ===
2096
2097        // Try the new action system first
2098        if let Some(result) = self.try_action_system(normalized_key)? {
2099            return Ok(result);
2100        }
2101
2102        // Try editor widget for high-level actions
2103        if let Some(result) = self.try_editor_widget(normalized_key)? {
2104            return Ok(result);
2105        }
2106
2107        // ORIGINAL LOGIC: Keep all existing logic as fallback
2108
2109        // Try history navigation first
2110        if let Some(result) = self.try_handle_history_navigation(&normalized_key)? {
2111            return Ok(result);
2112        }
2113
2114        // Store old cursor position
2115        let old_cursor = self.get_input_cursor();
2116
2117        // Also log to tracing
2118        trace!(target: "input", "Key: {:?} Modifiers: {:?}", key.code, key.modifiers);
2119
2120        // DON'T process chord handler in Command mode - yanking makes no sense when editing queries!
2121        // The 'y' key should just type 'y' in the query editor.
2122
2123        // Try buffer operations
2124        if let Some(result) = self.try_handle_buffer_operations(&key)? {
2125            return Ok(result);
2126        }
2127
2128        // Try function keys
2129        if let Some(result) = self.try_handle_function_keys(&key)? {
2130            return Ok(result);
2131        }
2132
2133        // Try text editing operations
2134        if let Some(result) = self.try_handle_text_editing(&key)? {
2135            return Ok(result);
2136        }
2137
2138        // Try mode transitions and core input handling
2139        if let Some(result) = self.try_handle_mode_transitions(&key, old_cursor)? {
2140            return Ok(result);
2141        }
2142
2143        // All input should be handled by the try_handle_* methods above
2144        // If we reach here, it means we missed handling a key combination
2145
2146        Ok(false)
2147    }
2148
2149    // ========== COMMAND INPUT HELPER METHODS ==========
2150    // These helpers break down the massive handle_command_input method into logical groups
2151
2152    /// Normalize key for platform differences and log it
2153    fn normalize_and_log_key(
2154        &mut self,
2155        key: crossterm::event::KeyEvent,
2156    ) -> crossterm::event::KeyEvent {
2157        let normalized = self.state_container.normalize_key(key);
2158
2159        // Get the action that will be performed (if any)
2160        let action = self
2161            .key_dispatcher
2162            .get_command_action(&normalized)
2163            .map(std::string::ToString::to_string);
2164
2165        // Log the key press
2166        if normalized != key {
2167            self.state_container
2168                .log_key_press(key, Some(format!("normalized to {normalized:?}")));
2169        }
2170        self.state_container.log_key_press(normalized, action);
2171
2172        normalized
2173    }
2174
2175    /// Try handling with the new action system
2176    fn try_action_system(
2177        &mut self,
2178        normalized_key: crossterm::event::KeyEvent,
2179    ) -> Result<Option<bool>> {
2180        let action_context = self.build_action_context();
2181        if let Some(action) = self.key_mapper.map_key(normalized_key, &action_context) {
2182            info!(
2183                "✓ Action system (Command): key {:?} -> action {:?}",
2184                normalized_key.code, action
2185            );
2186            if let Ok(result) = self.try_handle_action(action, &action_context) {
2187                match result {
2188                    ActionResult::Handled => {
2189                        debug!("Action handled by new system in Command mode");
2190                        return Ok(Some(false));
2191                    }
2192                    ActionResult::Exit => {
2193                        return Ok(Some(true));
2194                    }
2195                    ActionResult::NotHandled => {
2196                        // Fall through to existing handling
2197                    }
2198                    _ => {}
2199                }
2200            }
2201        }
2202        Ok(None)
2203    }
2204
2205    /// Try handling with editor widget
2206    fn try_editor_widget(
2207        &mut self,
2208        normalized_key: crossterm::event::KeyEvent,
2209    ) -> Result<Option<bool>> {
2210        let key_dispatcher = self.key_dispatcher.clone();
2211        let editor_result = if let Some(buffer) = self.state_container.buffers_mut().current_mut() {
2212            self.editor_widget
2213                .handle_key(normalized_key, &key_dispatcher, buffer)?
2214        } else {
2215            EditorAction::PassToMainApp(normalized_key)
2216        };
2217
2218        match editor_result {
2219            EditorAction::Quit => return Ok(Some(true)),
2220            EditorAction::ExecuteQuery => {
2221                return self.handle_execute_query().map(Some);
2222            }
2223            EditorAction::BufferAction(buffer_action) => {
2224                return self.handle_buffer_action(buffer_action).map(Some);
2225            }
2226            EditorAction::ExpandAsterisk => {
2227                return self.handle_expand_asterisk().map(Some);
2228            }
2229            EditorAction::ShowHelp => {
2230                self.state_container.set_help_visible(true);
2231                // Use proper mode synchronization
2232                self.set_mode_via_shadow_state(AppMode::Help, "help_requested");
2233                return Ok(Some(false));
2234            }
2235            EditorAction::ShowDebug => {
2236                // This is now handled by passing through to original F5 handler
2237                return Ok(Some(false));
2238            }
2239            EditorAction::ShowPrettyQuery => {
2240                self.show_pretty_query();
2241                return Ok(Some(false));
2242            }
2243            EditorAction::SwitchMode(mode) => {
2244                self.handle_editor_mode_switch(mode);
2245                return Ok(Some(false));
2246            }
2247            EditorAction::PassToMainApp(_) => {
2248                // Fall through to original logic
2249            }
2250            EditorAction::Continue => return Ok(Some(false)),
2251        }
2252
2253        Ok(None)
2254    }
2255
2256    /// Handle mode switch from editor widget
2257    fn handle_editor_mode_switch(&mut self, mode: AppMode) {
2258        debug!(target: "shadow_state", "EditorAction::SwitchMode to {:?}", mode);
2259        if let Some(buffer) = self.state_container.buffers_mut().current_mut() {
2260            // Use shadow state to set mode (with write-through to buffer)
2261            let trigger = match mode {
2262                AppMode::Results => "enter_results_mode",
2263                AppMode::Command => "enter_command_mode",
2264                AppMode::History => "enter_history_mode",
2265                _ => "switch_mode",
2266            };
2267            debug!(target: "shadow_state", "Setting mode via shadow state to {:?} with trigger {}", mode, trigger);
2268            self.shadow_state
2269                .borrow_mut()
2270                .set_mode(mode.clone(), buffer, trigger);
2271        } else {
2272            debug!(target: "shadow_state", "No buffer available for mode switch!");
2273        }
2274
2275        // Special handling for History mode - initialize history search
2276        if mode == AppMode::History {
2277            eprintln!("[DEBUG] Using AppStateContainer for history search");
2278            let current_input = self.get_input_text();
2279
2280            // Start history search
2281            self.state_container.start_history_search(current_input);
2282
2283            // Initialize with schema context
2284            self.update_history_matches_in_container();
2285
2286            // Get match count
2287            let match_count = self.state_container.history_search().matches.len();
2288
2289            self.state_container
2290                .set_status_message(format!("History search: {match_count} matches"));
2291        }
2292    }
2293
2294    /// Handle function key inputs (F1-F12)
2295    fn try_handle_function_keys(
2296        &mut self,
2297        key: &crossterm::event::KeyEvent,
2298    ) -> Result<Option<bool>> {
2299        match key.code {
2300            KeyCode::F(1) | KeyCode::Char('?') => {
2301                // Toggle between Help mode and previous mode
2302                if self.shadow_state.borrow().is_in_help_mode() {
2303                    // Exit help mode
2304                    let mode = if self.state_container.has_dataview() {
2305                        AppMode::Results
2306                    } else {
2307                        AppMode::Command
2308                    };
2309                    // Use proper mode synchronization
2310                    self.set_mode_via_shadow_state(mode, "exit_help");
2311                    self.state_container.set_help_visible(false);
2312                    self.help_widget.on_exit();
2313                } else {
2314                    // Enter help mode
2315                    self.state_container.set_help_visible(true);
2316                    // Use proper mode synchronization
2317                    self.set_mode_via_shadow_state(AppMode::Help, "help_requested");
2318                    self.help_widget.on_enter();
2319                }
2320                Ok(Some(false))
2321            }
2322            KeyCode::F(3) => {
2323                // Show pretty printed query
2324                self.show_pretty_query();
2325                Ok(Some(false))
2326            }
2327            KeyCode::F(5) => {
2328                // Toggle debug mode
2329                self.toggle_debug_mode();
2330                Ok(Some(false))
2331            }
2332            KeyCode::F(6) => {
2333                // Toggle row numbers
2334                let current = self.state_container.is_show_row_numbers();
2335                self.state_container.set_show_row_numbers(!current);
2336                self.state_container.set_status_message(format!(
2337                    "Row numbers: {}",
2338                    if current { "OFF" } else { "ON" }
2339                ));
2340                Ok(Some(false))
2341            }
2342            KeyCode::F(7) => {
2343                // Toggle compact mode
2344                let current_mode = self.state_container.is_compact_mode();
2345                self.state_container.set_compact_mode(!current_mode);
2346                let message = if current_mode {
2347                    "Compact mode disabled"
2348                } else {
2349                    "Compact mode enabled"
2350                };
2351                self.state_container.set_status_message(message.to_string());
2352                Ok(Some(false))
2353            }
2354            KeyCode::F(8) => {
2355                // Toggle case-insensitive string comparisons
2356                let current = self.state_container.is_case_insensitive();
2357                self.state_container.set_case_insensitive(!current);
2358                self.state_container.set_status_message(format!(
2359                    "Case-insensitive string comparisons: {}",
2360                    if current { "OFF" } else { "ON" }
2361                ));
2362                Ok(Some(false))
2363            }
2364            KeyCode::F(9) => {
2365                // F9 as alternative for kill line (for terminals that intercept Ctrl+K)
2366                use crate::ui::traits::input_ops::InputBehavior;
2367                InputBehavior::kill_line(self);
2368                let message = if self.state_container.is_kill_ring_empty() {
2369                    "Killed to end of line".to_string()
2370                } else {
2371                    format!(
2372                        "Killed to end of line ('{}' saved to kill ring)",
2373                        self.state_container.get_kill_ring()
2374                    )
2375                };
2376                self.state_container.set_status_message(message);
2377                Ok(Some(false))
2378            }
2379            KeyCode::F(10) => {
2380                // F10 as alternative for kill line backward
2381                use crate::ui::traits::input_ops::InputBehavior;
2382                InputBehavior::kill_line_backward(self);
2383                let message = if self.state_container.is_kill_ring_empty() {
2384                    "Killed to beginning of line".to_string()
2385                } else {
2386                    format!(
2387                        "Killed to beginning of line ('{}' saved to kill ring)",
2388                        self.state_container.get_kill_ring()
2389                    )
2390                };
2391                self.state_container.set_status_message(message);
2392                Ok(Some(false))
2393            }
2394            KeyCode::F(12) => {
2395                // Toggle key press indicator
2396                let enabled = !self.key_indicator.enabled;
2397                self.key_indicator.set_enabled(enabled);
2398                self.key_sequence_renderer.set_enabled(enabled);
2399                self.state_container.set_status_message(format!(
2400                    "Key press indicator {}",
2401                    if enabled { "enabled" } else { "disabled" }
2402                ));
2403                Ok(Some(false))
2404            }
2405            _ => Ok(None), // Not a function key we handle
2406        }
2407    }
2408
2409    /// Handle buffer management operations
2410    fn try_handle_buffer_operations(
2411        &mut self,
2412        key: &crossterm::event::KeyEvent,
2413    ) -> Result<Option<bool>> {
2414        if let Some(action) = self.key_dispatcher.get_command_action(key) {
2415            match action {
2416                "quit" => return Ok(Some(true)),
2417                "next_buffer" => {
2418                    // Save viewport state before switching
2419                    self.save_viewport_to_current_buffer();
2420
2421                    let message = self
2422                        .buffer_handler
2423                        .next_buffer(self.state_container.buffers_mut());
2424                    debug!("{}", message);
2425
2426                    // Sync all state after buffer switch
2427                    self.sync_after_buffer_switch();
2428                    return Ok(Some(false));
2429                }
2430                "previous_buffer" => {
2431                    // Save viewport state before switching
2432                    self.save_viewport_to_current_buffer();
2433
2434                    let message = self
2435                        .buffer_handler
2436                        .previous_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                "quick_switch_buffer" => {
2444                    // Save viewport state before switching
2445                    self.save_viewport_to_current_buffer();
2446
2447                    let message = self
2448                        .buffer_handler
2449                        .quick_switch(self.state_container.buffers_mut());
2450                    debug!("{}", message);
2451
2452                    // Sync all state after buffer switch
2453                    self.sync_after_buffer_switch();
2454
2455                    return Ok(Some(false));
2456                }
2457                "new_buffer" => {
2458                    let message = self
2459                        .buffer_handler
2460                        .new_buffer(self.state_container.buffers_mut(), &self.config);
2461                    debug!("{}", message);
2462                    return Ok(Some(false));
2463                }
2464                "close_buffer" => {
2465                    let (success, message) = self
2466                        .buffer_handler
2467                        .close_buffer(self.state_container.buffers_mut());
2468                    debug!("{}", message);
2469                    return Ok(Some(!success)); // Exit if we couldn't close (only one left)
2470                }
2471                "list_buffers" => {
2472                    let buffer_list = self
2473                        .buffer_handler
2474                        .list_buffers(self.state_container.buffers());
2475                    for line in &buffer_list {
2476                        debug!("{}", line);
2477                    }
2478                    return Ok(Some(false));
2479                }
2480                action if action.starts_with("switch_to_buffer_") => {
2481                    if let Some(buffer_num_str) = action.strip_prefix("switch_to_buffer_") {
2482                        if let Ok(buffer_num) = buffer_num_str.parse::<usize>() {
2483                            // Save viewport state before switching
2484                            self.save_viewport_to_current_buffer();
2485
2486                            let message = self.buffer_handler.switch_to_buffer(
2487                                self.state_container.buffers_mut(),
2488                                buffer_num - 1,
2489                            );
2490                            debug!("{}", message);
2491
2492                            // Sync all state after buffer switch
2493                            self.sync_after_buffer_switch();
2494                        }
2495                    }
2496                    return Ok(Some(false));
2497                }
2498                _ => {} // Not a buffer operation
2499            }
2500        }
2501        Ok(None)
2502    }
2503
2504    /// Handle history navigation operations
2505    fn try_handle_history_navigation(
2506        &mut self,
2507        key: &crossterm::event::KeyEvent,
2508    ) -> Result<Option<bool>> {
2509        // Handle Ctrl+R for history search
2510        if let KeyCode::Char('r') = key.code {
2511            if key.modifiers.contains(KeyModifiers::CONTROL) {
2512                // Start history search mode
2513                let current_input = self.get_input_text();
2514
2515                // Start history search
2516                self.state_container.start_history_search(current_input);
2517
2518                // Initialize with schema context
2519                self.update_history_matches_in_container();
2520
2521                // Get status
2522                let match_count = self.state_container.history_search().matches.len();
2523
2524                self.state_container.set_mode(AppMode::History);
2525                self.shadow_state
2526                    .borrow_mut()
2527                    .observe_mode_change(AppMode::History, "history_search_started");
2528                self.state_container.set_status_message(format!(
2529                    "History search started (Ctrl+R) - {match_count} matches"
2530                ));
2531                return Ok(Some(false));
2532            }
2533        }
2534
2535        // Handle Ctrl+P for previous history
2536        if let KeyCode::Char('p') = key.code {
2537            if key.modifiers.contains(KeyModifiers::CONTROL) {
2538                let history_entries = self
2539                    .state_container
2540                    .command_history()
2541                    .get_navigation_entries();
2542                let history_commands: Vec<String> =
2543                    history_entries.iter().map(|e| e.command.clone()).collect();
2544
2545                if let Some(buffer) = self.state_container.buffers_mut().current_mut() {
2546                    if buffer.navigate_history_up(&history_commands) {
2547                        self.sync_all_input_states();
2548                        self.state_container
2549                            .set_status_message("Previous command from history".to_string());
2550                    }
2551                }
2552                return Ok(Some(false));
2553            }
2554        }
2555
2556        // Handle Ctrl+N for next history
2557        if let KeyCode::Char('n') = key.code {
2558            if key.modifiers.contains(KeyModifiers::CONTROL) {
2559                let history_entries = self
2560                    .state_container
2561                    .command_history()
2562                    .get_navigation_entries();
2563                let history_commands: Vec<String> =
2564                    history_entries.iter().map(|e| e.command.clone()).collect();
2565
2566                if let Some(buffer) = self.state_container.buffers_mut().current_mut() {
2567                    if buffer.navigate_history_down(&history_commands) {
2568                        self.sync_all_input_states();
2569                        self.state_container
2570                            .set_status_message("Next command from history".to_string());
2571                    }
2572                }
2573                return Ok(Some(false));
2574            }
2575        }
2576
2577        // Handle Alt+Up/Down as alternatives
2578        match key.code {
2579            KeyCode::Up if key.modifiers.contains(KeyModifiers::ALT) => {
2580                let history_entries = self
2581                    .state_container
2582                    .command_history()
2583                    .get_navigation_entries();
2584                let history_commands: Vec<String> =
2585                    history_entries.iter().map(|e| e.command.clone()).collect();
2586
2587                if let Some(buffer) = self.state_container.buffers_mut().current_mut() {
2588                    if buffer.navigate_history_up(&history_commands) {
2589                        self.sync_all_input_states();
2590                        self.state_container
2591                            .set_status_message("Previous command (Alt+Up)".to_string());
2592                    }
2593                }
2594                Ok(Some(false))
2595            }
2596            KeyCode::Down if key.modifiers.contains(KeyModifiers::ALT) => {
2597                let history_entries = self
2598                    .state_container
2599                    .command_history()
2600                    .get_navigation_entries();
2601                let history_commands: Vec<String> =
2602                    history_entries.iter().map(|e| e.command.clone()).collect();
2603
2604                if let Some(buffer) = self.state_container.buffers_mut().current_mut() {
2605                    if buffer.navigate_history_down(&history_commands) {
2606                        self.sync_all_input_states();
2607                        self.state_container
2608                            .set_status_message("Next command (Alt+Down)".to_string());
2609                    }
2610                }
2611                Ok(Some(false))
2612            }
2613            _ => Ok(None),
2614        }
2615    }
2616
2617    /// Handle text editing operations (word movement, kill line, clipboard, etc.)
2618    fn try_handle_text_editing(
2619        &mut self,
2620        key: &crossterm::event::KeyEvent,
2621    ) -> Result<Option<bool>> {
2622        // Try dispatcher actions first
2623        if let Some(action) = self.key_dispatcher.get_command_action(key) {
2624            match action {
2625                "expand_asterisk" => {
2626                    if let Some(buffer) = self.state_container.buffers_mut().current_mut() {
2627                        if buffer.expand_asterisk(&self.hybrid_parser) {
2628                            // Sync for rendering if needed
2629                            if buffer.get_edit_mode() == EditMode::SingleLine {
2630                                let text = buffer.get_input_text();
2631                                let cursor = buffer.get_input_cursor_position();
2632                                self.set_input_text_with_cursor(text, cursor);
2633                            }
2634                        }
2635                    }
2636                    return Ok(Some(false));
2637                }
2638                "expand_asterisk_visible" => {
2639                    if let Some(buffer) = self.state_container.buffers_mut().current_mut() {
2640                        if buffer.expand_asterisk_visible() {
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                // TODO: DUPLICATED IN COMMAND_EDITOR - Can be removed after full migration
2652                "delete_word_backward" => {
2653                    use crate::ui::traits::input_ops::InputBehavior;
2654                    InputBehavior::delete_word_backward(self);
2655                    return Ok(Some(false));
2656                }
2657                // TODO: DUPLICATED IN COMMAND_EDITOR - Can be removed after full migration
2658                "delete_word_forward" => {
2659                    use crate::ui::traits::input_ops::InputBehavior;
2660                    InputBehavior::delete_word_forward(self);
2661                    return Ok(Some(false));
2662                }
2663                // TODO: DUPLICATED IN COMMAND_EDITOR - Can be removed after full migration
2664                "kill_line" => {
2665                    use crate::ui::traits::input_ops::InputBehavior;
2666                    InputBehavior::kill_line(self);
2667                    return Ok(Some(false));
2668                }
2669                // TODO: DUPLICATED IN COMMAND_EDITOR - Can be removed after full migration
2670                "kill_line_backward" => {
2671                    use crate::ui::traits::input_ops::InputBehavior;
2672                    InputBehavior::kill_line_backward(self);
2673                    return Ok(Some(false));
2674                }
2675                // TODO: DUPLICATED IN COMMAND_EDITOR - Can be removed after full migration
2676                "move_word_backward" => {
2677                    self.move_cursor_word_backward();
2678                    return Ok(Some(false));
2679                }
2680                // TODO: DUPLICATED IN COMMAND_EDITOR - Can be removed after full migration
2681                "move_word_forward" => {
2682                    self.move_cursor_word_forward();
2683                    return Ok(Some(false));
2684                }
2685                _ => {} // Not a text editing action, fall through
2686            }
2687        }
2688
2689        // Handle hardcoded text editing keys
2690        match key.code {
2691            // TODO: DUPLICATED IN COMMAND_EDITOR - Can be removed after full migration
2692            KeyCode::Char('k') if key.modifiers.contains(KeyModifiers::CONTROL) => {
2693                // Kill line - delete from cursor to end of line
2694                self.state_container
2695                    .set_status_message("Ctrl+K pressed - killing to end of line".to_string());
2696                use crate::ui::traits::input_ops::InputBehavior;
2697                InputBehavior::kill_line(self);
2698                Ok(Some(false))
2699            }
2700            // TODO: DUPLICATED IN COMMAND_EDITOR - Can be removed after full migration
2701            KeyCode::Char('k') if key.modifiers.contains(KeyModifiers::ALT) => {
2702                // Alternative: Alt+K for kill line (for terminals that intercept Ctrl+K)
2703                self.state_container
2704                    .set_status_message("Alt+K - killing to end of line".to_string());
2705                use crate::ui::traits::input_ops::InputBehavior;
2706                InputBehavior::kill_line(self);
2707                Ok(Some(false))
2708            }
2709            // TODO: DUPLICATED IN COMMAND_EDITOR - Can be removed after full migration
2710            KeyCode::Char('u') if key.modifiers.contains(KeyModifiers::CONTROL) => {
2711                // Kill line backward - delete from cursor to beginning of line
2712                use crate::ui::traits::input_ops::InputBehavior;
2713                InputBehavior::kill_line_backward(self);
2714                Ok(Some(false))
2715            }
2716            KeyCode::Char('v') if key.modifiers.contains(KeyModifiers::CONTROL) => {
2717                // Paste from system clipboard
2718                self.paste_from_clipboard();
2719                Ok(Some(false))
2720            }
2721            KeyCode::Char('y') if key.modifiers.contains(KeyModifiers::CONTROL) => {
2722                // Yank - paste from kill ring (if we have one)
2723                self.yank();
2724                Ok(Some(false))
2725            }
2726            // TODO: DUPLICATED IN COMMAND_EDITOR - Can be removed after full migration
2727            KeyCode::Left if key.modifiers.contains(KeyModifiers::CONTROL) => {
2728                // Move backward one word
2729                self.move_cursor_word_backward();
2730                Ok(Some(false))
2731            }
2732            // TODO: DUPLICATED IN COMMAND_EDITOR - Can be removed after full migration
2733            KeyCode::Right if key.modifiers.contains(KeyModifiers::CONTROL) => {
2734                // Move forward one word
2735                self.move_cursor_word_forward();
2736                Ok(Some(false))
2737            }
2738            // TODO: DUPLICATED IN COMMAND_EDITOR - Can be removed after full migration
2739            KeyCode::Char('b') if key.modifiers.contains(KeyModifiers::ALT) => {
2740                // Move backward one word (alt+b like in bash)
2741                self.move_cursor_word_backward();
2742                Ok(Some(false))
2743            }
2744            // TODO: DUPLICATED IN COMMAND_EDITOR - Can be removed after full migration
2745            KeyCode::Char('f') if key.modifiers.contains(KeyModifiers::ALT) => {
2746                // Move forward one word (alt+f like in bash)
2747                self.move_cursor_word_forward();
2748                Ok(Some(false))
2749            }
2750            _ => Ok(None), // Not a text editing key we handle
2751        }
2752    }
2753
2754    /// Handle mode transitions and core input processing (Enter, Tab, Down arrow, input)
2755    fn try_handle_mode_transitions(
2756        &mut self,
2757        key: &crossterm::event::KeyEvent,
2758        old_cursor: usize,
2759    ) -> Result<Option<bool>> {
2760        match key.code {
2761            KeyCode::Char('c') if key.modifiers.contains(KeyModifiers::CONTROL) => {
2762                // Ctrl+C - exit application
2763                Ok(Some(true))
2764            }
2765            KeyCode::Char('d') if key.modifiers.contains(KeyModifiers::CONTROL) => {
2766                // Ctrl+D - exit application
2767                Ok(Some(true))
2768            }
2769            KeyCode::Enter => {
2770                // Query execution and special command handling
2771                let query = self.get_input_text().trim().to_string();
2772                debug!(target: "action", "Executing query: {}", query);
2773
2774                if query.is_empty() {
2775                    self.state_container
2776                        .set_status_message("Empty query - please enter a SQL command".to_string());
2777                } else {
2778                    // Check for special commands
2779                    if query == ":help" {
2780                        self.state_container.set_help_visible(true);
2781                        // Use proper mode synchronization
2782                        self.set_mode_via_shadow_state(AppMode::Help, "help_requested");
2783                        self.state_container
2784                            .set_status_message("Help Mode - Press ESC to return".to_string());
2785                    } else if query == ":exit" || query == ":quit" || query == ":q" {
2786                        return Ok(Some(true));
2787                    } else if query == ":tui" {
2788                        // Already in TUI mode
2789                        self.state_container
2790                            .set_status_message("Already in TUI mode".to_string());
2791                    } else {
2792                        self.state_container
2793                            .set_status_message(format!("Processing query: '{query}'"));
2794                        self.execute_query_v2(&query)?;
2795                    }
2796                }
2797                Ok(Some(false))
2798            }
2799            KeyCode::Tab => {
2800                // Tab completion works in both modes
2801                self.apply_completion();
2802                Ok(Some(false))
2803            }
2804            KeyCode::Down => {
2805                debug!(target: "shadow_state", "Down arrow pressed in Command mode. has_dataview={}, edit_mode={:?}",
2806                    self.state_container.has_dataview(),
2807                    self.state_container.get_edit_mode());
2808
2809                if self.state_container.has_dataview()
2810                    && self.state_container.get_edit_mode() == Some(EditMode::SingleLine)
2811                {
2812                    debug!(target: "shadow_state", "Down arrow conditions met, switching to Results via set_mode");
2813                    // Switch to Results mode and restore state
2814                    self.state_container.set_mode(AppMode::Results);
2815                    self.shadow_state
2816                        .borrow_mut()
2817                        .observe_mode_change(AppMode::Results, "down_arrow_to_results");
2818                    // Restore previous position or default to 0
2819                    let row = self.state_container.get_last_results_row().unwrap_or(0);
2820                    self.state_container.set_table_selected_row(Some(row));
2821
2822                    // Restore the exact scroll offset from when we left
2823                    let last_offset = self.state_container.get_last_scroll_offset();
2824                    self.state_container.set_scroll_offset(last_offset);
2825                    Ok(Some(false))
2826                } else {
2827                    debug!(target: "shadow_state", "Down arrow conditions not met, falling through");
2828                    // Fall through to default handling
2829                    Ok(None)
2830                }
2831            }
2832            _ => {
2833                // Fallback input handling and completion
2834                self.handle_input_key(*key);
2835
2836                // Clear completion state when typing other characters
2837                self.state_container.clear_completion();
2838
2839                // Always use single-line completion
2840                self.handle_completion();
2841
2842                // Update horizontal scroll if cursor moved
2843                if self.get_input_cursor() != old_cursor {
2844                    self.update_horizontal_scroll(120); // Assume reasonable terminal width, will be adjusted in render
2845                }
2846
2847                Ok(Some(false))
2848            }
2849        }
2850    }
2851
2852    /// Handle navigation keys specific to Results mode
2853    fn try_handle_results_navigation(
2854        &mut self,
2855        key: &crossterm::event::KeyEvent,
2856    ) -> Result<Option<bool>> {
2857        match key.code {
2858            KeyCode::PageDown | KeyCode::Char('f')
2859                if key.modifiers.contains(KeyModifiers::CONTROL) =>
2860            {
2861                NavigationBehavior::page_down(self);
2862                Ok(Some(false))
2863            }
2864            KeyCode::PageUp | KeyCode::Char('b')
2865                if key.modifiers.contains(KeyModifiers::CONTROL) =>
2866            {
2867                NavigationBehavior::page_up(self);
2868                Ok(Some(false))
2869            }
2870            _ => Ok(None), // Not a navigation key we handle
2871        }
2872    }
2873
2874    /// Handle clipboard/yank operations in Results mode
2875    fn try_handle_results_clipboard(
2876        &mut self,
2877        key: &crossterm::event::KeyEvent,
2878    ) -> Result<Option<bool>> {
2879        match key.code {
2880            KeyCode::Char('y') => {
2881                let selection_mode = self.get_selection_mode();
2882                debug!("'y' key pressed - selection_mode={:?}", selection_mode);
2883                match selection_mode {
2884                    SelectionMode::Cell => {
2885                        // In cell mode, single 'y' yanks the cell directly
2886                        debug!("Yanking cell in cell selection mode");
2887                        self.state_container
2888                            .set_status_message("Yanking cell...".to_string());
2889                        YankBehavior::yank_cell(self);
2890                        // Status message will be set by yank_cell
2891                    }
2892                    SelectionMode::Row => {
2893                        // In row mode, 'y' is handled by chord handler (yy, yc, ya)
2894                        // The chord handler will process the key sequence
2895                        debug!("'y' pressed in row mode - waiting for chord completion");
2896                        self.state_container.set_status_message(
2897                            "Press second key for chord: yy=row, yc=column, ya=all, yv=cell"
2898                                .to_string(),
2899                        );
2900                    }
2901                    SelectionMode::Column => {
2902                        // In column mode, 'y' yanks the current column
2903                        debug!("Yanking column in column selection mode");
2904                        self.state_container
2905                            .set_status_message("Yanking column...".to_string());
2906                        YankBehavior::yank_column(self);
2907                    }
2908                }
2909                Ok(Some(false))
2910            }
2911            _ => Ok(None),
2912        }
2913    }
2914
2915    /// Handle export operations in Results mode
2916    fn try_handle_results_export(
2917        &mut self,
2918        key: &crossterm::event::KeyEvent,
2919    ) -> Result<Option<bool>> {
2920        match key.code {
2921            KeyCode::Char('e') if key.modifiers.contains(KeyModifiers::CONTROL) => {
2922                self.export_to_csv();
2923                Ok(Some(false))
2924            }
2925            KeyCode::Char('j') if key.modifiers.contains(KeyModifiers::CONTROL) => {
2926                self.export_to_json();
2927                Ok(Some(false))
2928            }
2929            _ => Ok(None),
2930        }
2931    }
2932
2933    /// Handle help and mode transitions in Results mode
2934    fn try_handle_results_help(
2935        &mut self,
2936        key: &crossterm::event::KeyEvent,
2937    ) -> Result<Option<bool>> {
2938        match key.code {
2939            KeyCode::F(1) | KeyCode::Char('?') => {
2940                self.state_container.set_help_visible(true);
2941                // Use proper mode synchronization
2942                self.set_mode_via_shadow_state(AppMode::Help, "help_requested");
2943                self.help_widget.on_enter();
2944                Ok(Some(false))
2945            }
2946            _ => Ok(None),
2947        }
2948    }
2949
2950    fn handle_results_input(&mut self, key: crossterm::event::KeyEvent) -> Result<bool> {
2951        // Log all keys in Results mode to debug Escape handling
2952        debug!(
2953            "handle_results_input: Processing key {:?} in Results mode",
2954            key
2955        );
2956
2957        // Check if vim search is active/navigating
2958        let is_vim_navigating = self.vim_search_adapter.borrow().is_navigating();
2959        let vim_is_active = self.vim_search_adapter.borrow().is_active();
2960        let has_search_pattern = !self.state_container.get_search_pattern().is_empty();
2961
2962        debug!(
2963            "Search state check: vim_navigating={}, vim_active={}, has_pattern={}, pattern='{}'",
2964            is_vim_navigating,
2965            vim_is_active,
2966            has_search_pattern,
2967            self.state_container.get_search_pattern()
2968        );
2969
2970        // If Escape is pressed and there's an active search or vim navigation, handle it properly
2971        if key.code == KeyCode::Esc {
2972            info!("ESCAPE KEY DETECTED in Results mode!");
2973
2974            if is_vim_navigating || vim_is_active || has_search_pattern {
2975                info!("Escape pressed with active search - clearing via StateCoordinator");
2976                debug!(
2977                    "Pre-clear state: vim_navigating={}, vim_active={}, pattern='{}'",
2978                    is_vim_navigating,
2979                    vim_is_active,
2980                    self.state_container.get_search_pattern()
2981                );
2982
2983                // Use StateCoordinator to handle ALL search cancellation logic
2984                use crate::ui::state::state_coordinator::StateCoordinator;
2985                StateCoordinator::cancel_search_with_refs(
2986                    &mut self.state_container,
2987                    &self.shadow_state,
2988                    Some(&self.vim_search_adapter),
2989                );
2990
2991                // Verify clearing worked
2992                let post_pattern = self.state_container.get_search_pattern();
2993                let post_vim_active = self.vim_search_adapter.borrow().is_active();
2994                info!(
2995                    "Post-clear state: pattern='{}', vim_active={}",
2996                    post_pattern, post_vim_active
2997                );
2998
2999                self.state_container
3000                    .set_status_message("Search cleared".to_string());
3001                return Ok(false);
3002            }
3003            info!("Escape pressed but no active search to clear");
3004        }
3005
3006        let selection_mode = self.state_container.get_selection_mode();
3007
3008        debug!(
3009            "handle_results_input: key={:?}, selection_mode={:?}",
3010            key, selection_mode
3011        );
3012
3013        // Normalize the key for platform differences
3014        let normalized = self.state_container.normalize_key(key);
3015
3016        // Get the action that will be performed (if any) - for logging purposes
3017        let action_context = self.build_action_context();
3018        let mapped_action = self.key_mapper.map_key(normalized, &action_context);
3019        let action = mapped_action.as_ref().map(|a| format!("{a:?}"));
3020
3021        // Log the key press
3022        if normalized != key {
3023            self.state_container
3024                .log_key_press(key, Some(format!("normalized to {normalized:?}")));
3025        }
3026        self.state_container
3027            .log_key_press(normalized, action.clone());
3028
3029        let normalized_key = normalized;
3030
3031        // Try the new action system first
3032        // Note: Even if chord mode is active, single keys that aren't part of chords
3033        // should still be processed by the action system
3034        let action_context = self.build_action_context();
3035        debug!(
3036            "Action context for key {:?}: mode={:?}",
3037            normalized_key.code, action_context.mode
3038        );
3039        if let Some(action) = self.key_mapper.map_key(normalized_key, &action_context) {
3040            info!(
3041                "✓ Action system: key {:?} -> action {:?}",
3042                normalized_key.code, action
3043            );
3044            if let Ok(result) = self.try_handle_action(action, &action_context) {
3045                match result {
3046                    ActionResult::Handled => {
3047                        debug!("Action handled by new system");
3048                        return Ok(false);
3049                    }
3050                    ActionResult::Exit => {
3051                        debug!("Action requested exit");
3052                        return Ok(true);
3053                    }
3054                    ActionResult::SwitchMode(mode) => {
3055                        debug!("Action requested mode switch to {:?}", mode);
3056                        self.state_container.set_mode(mode);
3057                        return Ok(false);
3058                    }
3059                    ActionResult::Error(err) => {
3060                        warn!("Action error: {}", err);
3061                        self.state_container
3062                            .set_status_message(format!("Error: {err}"));
3063                        return Ok(false);
3064                    }
3065                    ActionResult::NotHandled => {
3066                        // Fall through to existing handling
3067                        debug!("Action not handled, falling back to legacy system");
3068                    }
3069                }
3070            }
3071        }
3072
3073        // Debug uppercase G specifically
3074        if matches!(key.code, KeyCode::Char('G')) {
3075            debug!("Detected uppercase G key press!");
3076        }
3077
3078        // F6 is now available for future use
3079
3080        // F12 is now handled by the action system
3081
3082        // NOTE: Chord handling has been moved to handle_input level
3083        // This ensures chords work correctly before any other key processing
3084
3085        // All keys should now be handled through the action system above
3086        // Any keys that reach here are either:
3087        // 1. Not mapped in the action system yet
3088        // 2. Special cases that need direct handling
3089
3090        // For now, log unmapped keys for debugging
3091        if mapped_action.is_none() {
3092            debug!(
3093                "No action mapping for key {:?} in Results mode",
3094                normalized_key
3095            );
3096        }
3097
3098        // Try Results-specific navigation keys
3099        if let Some(result) = self.try_handle_results_navigation(&normalized_key)? {
3100            return Ok(result);
3101        }
3102
3103        // Try clipboard/yank operations
3104        if let Some(result) = self.try_handle_results_clipboard(&normalized_key)? {
3105            return Ok(result);
3106        }
3107
3108        // Try export operations
3109        if let Some(result) = self.try_handle_results_export(&normalized_key)? {
3110            return Ok(result);
3111        }
3112
3113        // Try help and mode transitions
3114        if let Some(result) = self.try_handle_results_help(&normalized_key)? {
3115            return Ok(result);
3116        }
3117
3118        // All key handling has been migrated to:
3119        // - Action system (handles most keys)
3120        // - try_handle_results_* methods (handles specific Results mode keys)
3121        // This completes the orchestration pattern for Results mode input
3122        Ok(false)
3123    }
3124    // ========== SEARCH OPERATIONS ==========
3125
3126    fn execute_search_action(&mut self, mode: SearchMode, pattern: String) {
3127        debug!(target: "search", "execute_search_action called: mode={:?}, pattern='{}', current_app_mode={:?}, thread={:?}",
3128               mode, pattern, self.shadow_state.borrow().get_mode(), std::thread::current().id());
3129        match mode {
3130            SearchMode::Search => {
3131                debug!(target: "search", "Executing search with pattern: '{}', app_mode={:?}", pattern, self.shadow_state.borrow().get_mode());
3132                debug!(target: "search", "Search: current results count={}",
3133                       self.state_container.get_buffer_dataview().map_or(0, |v| v.source().row_count()));
3134
3135                // Set search pattern in AppStateContainer
3136                self.state_container.start_search(pattern.clone());
3137
3138                self.state_container.set_search_pattern(pattern.clone());
3139                self.perform_search();
3140                let matches_count = self.state_container.search().matches.len();
3141                debug!(target: "search", "After perform_search, app_mode={:?}, matches_found={}",
3142                       self.shadow_state.borrow().get_mode(),
3143                       matches_count);
3144
3145                // CRITICAL: Sync search results to VimSearchManager so 'n' and 'N' work
3146                if matches_count > 0 {
3147                    // Get matches from SearchManager to sync to VimSearchManager
3148                    let matches_for_vim: Vec<(usize, usize)> = {
3149                        let search_manager = self.search_manager.borrow();
3150                        search_manager
3151                            .all_matches()
3152                            .iter()
3153                            .map(|m| (m.row, m.column))
3154                            .collect()
3155                    };
3156
3157                    // Sync to VimSearchManager
3158                    if let Some(dataview) = self.state_container.get_buffer_dataview() {
3159                        info!(target: "search", "Syncing {} matches to VimSearchManager for pattern '{}'",
3160                              matches_for_vim.len(), pattern);
3161                        self.vim_search_adapter
3162                            .borrow_mut()
3163                            .set_search_state_from_external(
3164                                pattern.clone(),
3165                                matches_for_vim,
3166                                dataview,
3167                            );
3168                    }
3169                }
3170
3171                // Navigate to the first match if found (like vim)
3172                if matches_count > 0 {
3173                    // Get the first match position from SearchManager
3174                    let (row, col) = {
3175                        let search_manager = self.search_manager.borrow();
3176                        if let Some(first_match) = search_manager.first_match() {
3177                            (first_match.row, first_match.column)
3178                        } else {
3179                            // Fallback to state_container if SearchManager is empty (shouldn't happen)
3180                            let search_state = self.state_container.search();
3181                            if let Some((row, col, _, _)) = search_state.matches.first() {
3182                                (*row, *col)
3183                            } else {
3184                                (0, 0)
3185                            }
3186                        }
3187                    };
3188
3189                    info!(target: "search", "NAVIGATION START: Moving to first match at data row={}, col={}", row, col);
3190
3191                    // Navigate to the match position
3192                    // Set the row position
3193                    self.state_container.set_table_selected_row(Some(row));
3194                    self.state_container.set_selected_row(Some(row));
3195                    info!(target: "search", "  Set row position to {}", row);
3196
3197                    // Set the column position
3198                    {
3199                        let mut nav = self.state_container.navigation_mut();
3200                        nav.selected_column = col;
3201                    }
3202                    self.state_container.set_current_column_buffer(col);
3203                    info!(target: "search", "  Set column position to {}", col);
3204
3205                    // CRITICAL: Update TableWidgetManager for debounced search navigation
3206                    info!(target: "search", "Updating TableWidgetManager for debounced search to ({}, {})", row, col);
3207                    self.table_widget_manager
3208                        .borrow_mut()
3209                        .on_debounced_search(row, col);
3210
3211                    // Update ViewportManager and ensure match is visible
3212                    {
3213                        let mut viewport_manager_borrow = self.viewport_manager.borrow_mut();
3214                        if let Some(ref mut viewport_manager) = *viewport_manager_borrow {
3215                            // Update the actual viewport to show the first match
3216                            let viewport_height = self.state_container.navigation().viewport_rows;
3217                            let viewport_width = self.state_container.navigation().viewport_columns;
3218                            let current_scroll = self.state_container.navigation().scroll_offset.0;
3219
3220                            info!(target: "search", "  Viewport dimensions: {}x{}, current_scroll: {}",
3221                                  viewport_height, viewport_width, current_scroll);
3222
3223                            // Calculate new scroll offset if needed to show the match
3224                            let new_row_offset = if row < current_scroll {
3225                                info!(target: "search", "  Match is above viewport, scrolling up to row {}", row);
3226                                row // Match is above, scroll up
3227                            } else if row >= current_scroll + viewport_height.saturating_sub(1) {
3228                                let centered = row.saturating_sub(viewport_height / 2);
3229                                info!(target: "search", "  Match is below viewport, centering at row {}", centered);
3230                                centered // Match is below, center it
3231                            } else {
3232                                info!(target: "search", "  Match is already visible, keeping scroll at {}", current_scroll);
3233                                current_scroll // Already visible
3234                            };
3235
3236                            // Calculate column scroll if needed
3237                            let current_col_scroll =
3238                                self.state_container.navigation().scroll_offset.1;
3239                            let new_col_offset = if col < current_col_scroll {
3240                                info!(target: "search", "  Match column {} is left of viewport (scroll={}), scrolling left", col, current_col_scroll);
3241                                col // Match is to the left, scroll left
3242                            } else if col >= current_col_scroll + viewport_width.saturating_sub(1) {
3243                                let centered = col.saturating_sub(viewport_width / 4);
3244                                info!(target: "search", "  Match column {} is right of viewport (scroll={}, width={}), scrolling to {}",
3245                                      col, current_col_scroll, viewport_width, centered);
3246                                centered // Match is to the right, scroll right but keep some context
3247                            } else {
3248                                info!(target: "search", "  Match column {} is visible, keeping scroll at {}", col, current_col_scroll);
3249                                current_col_scroll // Already visible
3250                            };
3251
3252                            // Update viewport to show the match (both row and column)
3253                            viewport_manager.set_viewport(
3254                                new_row_offset,
3255                                new_col_offset,
3256                                viewport_width as u16,
3257                                viewport_height as u16,
3258                            );
3259                            info!(target: "search", "  Set viewport to row_offset={}, col_offset={}", new_row_offset, new_col_offset);
3260
3261                            // Set crosshair to match position (needs viewport-relative coordinates)
3262                            let crosshair_row = row - new_row_offset;
3263                            let crosshair_col = col - new_col_offset;
3264                            viewport_manager.set_crosshair(crosshair_row, crosshair_col);
3265                            info!(target: "search", "  Set crosshair to viewport-relative ({}, {}), absolute was ({}, {})", 
3266                                  crosshair_row, crosshair_col, row, col);
3267
3268                            // Update navigation scroll offset (both row and column)
3269                            let mut nav = self.state_container.navigation_mut();
3270                            nav.scroll_offset.0 = new_row_offset;
3271                            nav.scroll_offset.1 = new_col_offset;
3272                        }
3273                    }
3274
3275                    // Also update the buffer's current match to trigger UI updates
3276                    self.state_container.set_current_match(Some((row, col)));
3277
3278                    // CRITICAL: Force the visual cursor position to update
3279                    // The crosshair is set but we need to ensure the visual cursor moves
3280                    {
3281                        let mut nav = self.state_container.navigation_mut();
3282                        nav.selected_row = row;
3283                        nav.selected_column = col;
3284                    }
3285                    info!(target: "search", "  Forced navigation state to row={}, col={}", row, col);
3286
3287                    // Update status to show we're at match 1 of N
3288                    self.state_container.set_status_message(format!(
3289                        "Match 1/{} at row {}, col {}",
3290                        matches_count,
3291                        row + 1,
3292                        col + 1
3293                    ));
3294                }
3295            }
3296            SearchMode::Filter => {
3297                use crate::ui::state::state_coordinator::StateCoordinator;
3298
3299                // Use StateCoordinator for all state transitions
3300                StateCoordinator::apply_filter_search_with_refs(
3301                    &mut self.state_container,
3302                    &self.shadow_state,
3303                    &pattern,
3304                );
3305
3306                // Apply the actual filter (implementation stays in TUI)
3307                self.apply_filter(&pattern);
3308
3309                debug!(target: "search", "After apply_filter, filtered_count={}",
3310                    self.state_container.get_buffer_dataview().map_or(0, super::super::data::data_view::DataView::row_count));
3311            }
3312            SearchMode::FuzzyFilter => {
3313                use crate::ui::state::state_coordinator::StateCoordinator;
3314
3315                // Use StateCoordinator for all state transitions
3316                StateCoordinator::apply_fuzzy_filter_search_with_refs(
3317                    &mut self.state_container,
3318                    &self.shadow_state,
3319                    &pattern,
3320                );
3321
3322                // Apply the actual fuzzy filter (implementation stays in TUI)
3323                self.apply_fuzzy_filter();
3324
3325                let indices_count = self.state_container.get_fuzzy_filter_indices().len();
3326                debug!(target: "search", "After apply_fuzzy_filter, matched_indices={}", indices_count);
3327            }
3328            SearchMode::ColumnSearch => {
3329                use crate::ui::state::state_coordinator::StateCoordinator;
3330
3331                debug!(target: "search", "Executing column search with pattern: '{}'", pattern);
3332
3333                // Use StateCoordinator for all state transitions
3334                StateCoordinator::apply_column_search_with_refs(
3335                    &mut self.state_container,
3336                    &self.shadow_state,
3337                    &pattern,
3338                );
3339
3340                // Apply the actual column search (implementation stays in TUI)
3341                self.search_columns();
3342
3343                debug!(target: "search", "After search_columns, app_mode={:?}", self.shadow_state.borrow().get_mode());
3344            }
3345        }
3346    }
3347
3348    fn enter_search_mode(&mut self, mode: SearchMode) {
3349        debug!(target: "search", "enter_search_mode called for {:?}, current_mode={:?}, input_text='{}'",
3350               mode, self.shadow_state.borrow().get_mode(), self.state_container.get_input_text());
3351
3352        // Get the SQL text based on the current mode
3353        let current_sql = if self.shadow_state.borrow().is_in_results_mode() {
3354            // In Results mode, use the last executed query
3355            let last_query = self.state_container.get_last_query();
3356            let input_text = self.state_container.get_input_text();
3357            debug!(target: "search", "COLUMN_SEARCH_SAVE_DEBUG: last_query='{}', input_text='{}'", last_query, input_text);
3358
3359            if !last_query.is_empty() {
3360                debug!(target: "search", "Using last_query for search mode: '{}'", last_query);
3361                last_query
3362            } else if !input_text.is_empty() {
3363                // If last_query is empty but we have input_text, use that as fallback
3364                // This handles the case where data is loaded but no query has been executed yet
3365                debug!(target: "search", "No last_query, using input_text as fallback: '{}'", input_text);
3366                input_text
3367            } else {
3368                // This shouldn't happen if we're properly saving queries
3369                warn!(target: "search", "No last_query or input_text found when entering search mode from Results!");
3370                String::new()
3371            }
3372        } else {
3373            // In Command mode, use the current input text
3374            self.get_input_text()
3375        };
3376
3377        let cursor_pos = current_sql.len();
3378
3379        debug!(
3380            "Entering {} mode, saving SQL: '{}', cursor: {}",
3381            mode.title(),
3382            current_sql,
3383            cursor_pos
3384        );
3385
3386        // Initialize the widget with saved state
3387        self.search_modes_widget
3388            .enter_mode(mode.clone(), current_sql, cursor_pos);
3389
3390        // Set the app mode - use sync_mode to ensure all state is synchronized
3391        debug!(target: "mode", "Setting app mode from {:?} to {:?}", self.shadow_state.borrow().get_mode(), mode.to_app_mode());
3392        let trigger = match mode {
3393            SearchMode::ColumnSearch => "backslash_column_search",
3394            SearchMode::Search => "data_search_started",
3395            SearchMode::FuzzyFilter => "fuzzy_filter_started",
3396            SearchMode::Filter => "filter_started",
3397        };
3398        self.sync_mode(mode.to_app_mode(), trigger);
3399
3400        // Also observe the search mode start in shadow state for search-specific tracking
3401        let search_type = match mode {
3402            SearchMode::ColumnSearch => crate::ui::state::shadow_state::SearchType::Column,
3403            SearchMode::Search => crate::ui::state::shadow_state::SearchType::Data,
3404            SearchMode::FuzzyFilter | SearchMode::Filter => {
3405                crate::ui::state::shadow_state::SearchType::Fuzzy
3406            }
3407        };
3408        self.shadow_state
3409            .borrow_mut()
3410            .observe_search_start(search_type, trigger);
3411
3412        // Clear patterns
3413        match mode {
3414            SearchMode::Search => {
3415                // Clear search in AppStateContainer
3416                self.state_container.clear_search();
3417                self.state_container.set_search_pattern(String::new());
3418            }
3419            SearchMode::Filter => {
3420                self.state_container.set_filter_pattern(String::new());
3421                self.state_container.filter_mut().clear();
3422            }
3423            SearchMode::FuzzyFilter => {
3424                self.state_container.set_fuzzy_filter_pattern(String::new());
3425                self.state_container.set_fuzzy_filter_indices(Vec::new());
3426                self.state_container.set_fuzzy_filter_active(false);
3427            }
3428            SearchMode::ColumnSearch => {
3429                // Clear column search in both AppStateContainer and DataView
3430                self.state_container.clear_column_search();
3431                if let Some(dataview) = self.state_container.get_buffer_dataview_mut() {
3432                    dataview.clear_column_search();
3433                }
3434
3435                // All column search state is now managed by AppStateContainer
3436            }
3437        }
3438
3439        // Clear input field for search mode use
3440        self.input = tui_input::Input::default();
3441        // Note: Not syncing here as search modes use input differently
3442    }
3443
3444    fn handle_search_modes_input(&mut self, key: crossterm::event::KeyEvent) -> Result<bool> {
3445        // Safety check: Always allow Ctrl-C to exit regardless of state
3446        if key.code == KeyCode::Char('c') && key.modifiers.contains(KeyModifiers::CONTROL) {
3447            return Ok(true); // Signal to quit
3448        }
3449
3450        // All search modes now use the same SearchModesWidget for consistent debouncing
3451
3452        let action = self.search_modes_widget.handle_key(key);
3453
3454        match action {
3455            SearchModesAction::Continue => {
3456                // No pattern change, nothing to do
3457            }
3458            SearchModesAction::InputChanged(mode, pattern) => {
3459                // Pattern changed, update UI but don't apply filter yet (will be debounced)
3460                self.set_input_text_with_cursor(pattern.clone(), pattern.len());
3461
3462                // Update the stored pattern
3463                match mode {
3464                    SearchMode::Search => {
3465                        self.state_container.set_search_pattern(pattern);
3466                    }
3467                    SearchMode::Filter => {
3468                        self.state_container.set_filter_pattern(pattern.clone());
3469                        let mut filter = self.state_container.filter_mut();
3470                        filter.pattern = pattern.clone();
3471                        filter.is_active = true;
3472                    }
3473                    SearchMode::FuzzyFilter => {
3474                        self.state_container.set_fuzzy_filter_pattern(pattern);
3475                    }
3476                    SearchMode::ColumnSearch => {
3477                        // Pattern is stored in AppStateContainer via start_column_search
3478                    }
3479                }
3480            }
3481            SearchModesAction::ExecuteDebounced(mode, pattern) => {
3482                // Execute the search but DON'T exit the mode - stay in search mode
3483                // This is for debounced typing updates
3484                self.execute_search_action(mode, pattern);
3485                // Don't exit! User is still typing/searching
3486            }
3487            SearchModesAction::Apply(mode, pattern) => {
3488                debug!(target: "search", "Apply action triggered for {:?} with pattern '{}'", mode, pattern);
3489                // Apply the filter/search with the pattern
3490                match mode {
3491                    SearchMode::Search => {
3492                        debug!(target: "search", "Search Apply: Applying search with pattern '{}'", pattern);
3493                        // Use execute_search_action to get the navigation to first match
3494                        self.execute_search_action(SearchMode::Search, pattern);
3495                        debug!(target: "search", "Search Apply: last_query='{}', will restore saved SQL from widget", self.state_container.get_last_query());
3496                        // For search, we always want to exit to Results mode after applying
3497                    }
3498                    SearchMode::Filter => {
3499                        debug!(target: "search", "Filter Apply: Applying filter with pattern '{}'", pattern);
3500                        self.state_container.set_filter_pattern(pattern.clone());
3501                        {
3502                            let mut filter = self.state_container.filter_mut();
3503                            filter.pattern = pattern.clone();
3504                            filter.is_active = true;
3505                        } // filter borrow ends here
3506                        self.apply_filter(&pattern); // Use the actual pattern, not empty string
3507                        debug!(target: "search", "Filter Apply: last_query='{}', will restore saved SQL from widget", self.state_container.get_last_query());
3508                    }
3509                    SearchMode::FuzzyFilter => {
3510                        debug!(target: "search", "FuzzyFilter Apply: Applying filter with pattern '{}'", pattern);
3511                        self.state_container.set_fuzzy_filter_pattern(pattern);
3512                        self.apply_fuzzy_filter();
3513                        debug!(target: "search", "FuzzyFilter Apply: last_query='{}', will restore saved SQL from widget", self.state_container.get_last_query());
3514                    }
3515                    SearchMode::ColumnSearch => {
3516                        // For column search, Apply (Enter key) jumps to the current match and exits
3517
3518                        let column_info = {
3519                            let column_search = self.state_container.column_search();
3520                            if column_search.matching_columns.is_empty() {
3521                                None
3522                            } else {
3523                                let current_match = column_search.current_match;
3524                                Some(column_search.matching_columns[current_match].clone())
3525                            }
3526                        };
3527
3528                        if let Some((col_idx, col_name)) = column_info {
3529                            self.state_container.set_current_column(col_idx);
3530                            self.state_container.set_current_column_buffer(col_idx);
3531
3532                            // Update ViewportManager to ensure the column is visible
3533                            let mut viewport_manager_borrow = self.viewport_manager.borrow_mut();
3534                            if let Some(viewport_manager) = viewport_manager_borrow.as_mut() {
3535                                viewport_manager.set_current_column(col_idx);
3536                            }
3537                            drop(viewport_manager_borrow);
3538
3539                            // CRITICAL: Sync NavigationState with ViewportManager after column navigation
3540                            // This ensures all state systems are consistent (like vim search)
3541                            debug!(target: "column_search_sync", "ColumnSearch Apply: About to call sync_navigation_with_viewport() for column: {}", col_name);
3542                            debug!(target: "column_search_sync", "ColumnSearch Apply: Pre-sync - viewport current_column: {}", 
3543                                if let Ok(vm) = self.viewport_manager.try_borrow() {
3544                                    vm.as_ref().map_or(0, super::viewport_manager::ViewportManager::get_crosshair_col)
3545                                } else { 0 });
3546                            self.sync_navigation_with_viewport();
3547                            debug!(target: "column_search_sync", "ColumnSearch Apply: Post-sync - navigation current_column: {}", 
3548                                self.state_container.navigation().selected_column);
3549                            debug!(target: "column_search_sync", "ColumnSearch Apply: sync_navigation_with_viewport() completed for column: {}", col_name);
3550
3551                            self.state_container
3552                                .set_status_message(format!("Jumped to column: {col_name}"));
3553                        }
3554
3555                        // IMPORTANT: Don't modify input_text when exiting column search!
3556                        // The widget will restore the original SQL that was saved when entering the mode
3557                        debug!(target: "search", "ColumnSearch Apply: Exiting without modifying input_text");
3558                        debug!(target: "search", "ColumnSearch Apply: last_query='{}', will restore saved SQL from widget", self.state_container.get_last_query());
3559                        // Note: Column search state will be cleared by cancel_search_with_refs below
3560                    }
3561                }
3562
3563                // Exit search mode and return to Results
3564                // Try to get saved SQL from widget
3565                let saved_state = self.search_modes_widget.exit_mode();
3566
3567                if let Some((sql, cursor)) = saved_state {
3568                    debug!(target: "search", "Exiting search mode. Original SQL was: '{}', cursor: {}", sql, cursor);
3569                    debug!(target: "buffer", "Returning to Results mode, preserving last_query: '{}'",
3570                           self.state_container.get_last_query());
3571
3572                    // IMPORTANT: Always restore the saved SQL to input_text!
3573                    // This includes empty strings - we need to clear the search term
3574                    debug!(target: "search", "Restoring saved SQL to input_text: '{}'", sql);
3575                    // Use helper to sync all states
3576                    self.set_input_text_with_cursor(sql, cursor);
3577                } else {
3578                    // Widget didn't have saved state - restore appropriate SQL based on mode
3579                    if mode == SearchMode::ColumnSearch {
3580                        // For column search, restore the last executed query or pre-populated query
3581                        let last_query = self.state_container.get_last_query();
3582                        if last_query.is_empty() {
3583                            debug!(target: "search", "Column search: No saved state or last_query, clearing input");
3584                            self.set_input_text(String::new());
3585                        } else {
3586                            debug!(target: "search", "Column search: No saved state, restoring last_query: '{}'", last_query);
3587                            self.set_input_text(last_query);
3588                        }
3589                    } else {
3590                        debug!(target: "search", "No saved state from widget, keeping current SQL");
3591                    }
3592                }
3593
3594                // ALWAYS switch back to Results mode after Apply for all search modes
3595                use crate::ui::state::state_coordinator::StateCoordinator;
3596
3597                // For column search, we cancel completely (no n/N navigation)
3598                // For regular search, we complete but keep pattern for n/N
3599                if mode == SearchMode::ColumnSearch {
3600                    debug!(target: "column_search_sync", "ColumnSearch Apply: Canceling column search completely with cancel_search_with_refs()");
3601
3602                    // Also clear column search in DataView
3603                    if let Some(dataview) = self.state_container.get_buffer_dataview_mut() {
3604                        dataview.clear_column_search();
3605                        debug!(target: "column_search_sync", "ColumnSearch Apply: Cleared column search in DataView");
3606                    }
3607
3608                    StateCoordinator::cancel_search_with_refs(
3609                        &mut self.state_container,
3610                        &self.shadow_state,
3611                        Some(&self.vim_search_adapter),
3612                    );
3613                    // Note: cancel_search_with_refs already switches to Results mode
3614                    debug!(target: "column_search_sync", "ColumnSearch Apply: Column search canceled and mode switched to Results");
3615                } else {
3616                    // For regular search modes, keep pattern for n/N navigation
3617                    debug!(target: "column_search_sync", "Search Apply: About to call StateCoordinator::complete_search_with_refs() for mode: {:?}", mode);
3618                    StateCoordinator::complete_search_with_refs(
3619                        &mut self.state_container,
3620                        &self.shadow_state,
3621                        Some(&self.vim_search_adapter),
3622                        AppMode::Results,
3623                        "search_applied",
3624                    );
3625                    debug!(target: "column_search_sync", "Search Apply: StateCoordinator::complete_search_with_refs() completed - should now be in Results mode");
3626                }
3627
3628                // Show status message
3629                let filter_msg = match mode {
3630                    SearchMode::FuzzyFilter => {
3631                        let query = self.state_container.get_last_query();
3632                        format!(
3633                            "Fuzzy filter applied. Query: '{}'. Press 'f' again to modify.",
3634                            if query.len() > 30 {
3635                                format!("{}...", &query[..30])
3636                            } else {
3637                                query
3638                            }
3639                        )
3640                    }
3641                    SearchMode::Filter => "Filter applied. Press 'F' again to modify.".to_string(),
3642                    SearchMode::Search => {
3643                        let matches = self.state_container.search().matches.len();
3644                        if matches > 0 {
3645                            format!("Found {matches} matches. Use n/N to navigate.")
3646                        } else {
3647                            "No matches found.".to_string()
3648                        }
3649                    }
3650                    SearchMode::ColumnSearch => "Column search complete.".to_string(),
3651                };
3652                self.state_container.set_status_message(filter_msg);
3653            }
3654            SearchModesAction::Cancel => {
3655                // Clear the filter and restore original SQL
3656                let mode = self.shadow_state.borrow().get_mode();
3657                match mode {
3658                    AppMode::FuzzyFilter => {
3659                        // Clear fuzzy filter - must apply empty filter to DataView
3660                        debug!(target: "search", "FuzzyFilter Cancel: Clearing fuzzy filter");
3661                        self.state_container.set_fuzzy_filter_pattern(String::new());
3662                        self.apply_fuzzy_filter(); // This will clear the filter in DataView
3663                        self.state_container.set_fuzzy_filter_indices(Vec::new());
3664                        self.state_container.set_fuzzy_filter_active(false);
3665                    }
3666                    AppMode::Filter => {
3667                        // Clear both local and buffer filter state
3668                        debug!(target: "search", "Filter Cancel: Clearing filter pattern and state");
3669                        self.state_container.filter_mut().clear();
3670                        self.state_container.set_filter_pattern(String::new());
3671                        self.state_container.set_filter_active(false);
3672                        // Re-apply empty filter to restore all results
3673                        self.apply_filter("");
3674                    }
3675                    AppMode::ColumnSearch => {
3676                        // Clear column search state using AppStateContainer
3677                        self.state_container.clear_column_search();
3678                        // The widget will restore the original SQL that was saved when entering the mode
3679                        debug!(target: "search", "ColumnSearch Cancel: Exiting without modifying input_text");
3680                        debug!(target: "search", "ColumnSearch Cancel: last_query='{}', will restore saved SQL from widget", self.state_container.get_last_query());
3681                    }
3682                    _ => {}
3683                }
3684
3685                // Exit mode and restore the saved SQL
3686                if let Some((sql, cursor)) = self.search_modes_widget.exit_mode() {
3687                    debug!(target: "search", "Cancel: Restoring saved SQL: '{}', cursor: {}", sql, cursor);
3688                    if !sql.is_empty() {
3689                        // Use helper to sync all states
3690                        self.set_input_text_with_cursor(sql, cursor);
3691                    }
3692                } else {
3693                    debug!(target: "search", "Cancel: No saved SQL from widget");
3694                }
3695
3696                // Use StateCoordinator to properly cancel search and restore state
3697                // StateCoordinator handles clearing vim search adapter too
3698                use crate::ui::state::state_coordinator::StateCoordinator;
3699                StateCoordinator::cancel_search_with_refs(
3700                    &mut self.state_container,
3701                    &self.shadow_state,
3702                    Some(&self.vim_search_adapter),
3703                );
3704            }
3705            SearchModesAction::NextMatch => {
3706                debug!(target: "search", "NextMatch action, current_mode={:?}, widget_mode={:?}",
3707                       self.shadow_state.borrow().get_mode(), self.search_modes_widget.current_mode());
3708
3709                // Check both shadow state and widget mode for consistency
3710                if self.shadow_state.borrow().is_in_column_search()
3711                    || self.search_modes_widget.current_mode() == Some(SearchMode::ColumnSearch)
3712                {
3713                    debug!(target: "search", "Calling next_column_match");
3714                    // Ensure mode is correctly set
3715                    if !self.shadow_state.borrow().is_in_column_search() {
3716                        debug!(target: "search", "WARNING: Mode mismatch - fixing");
3717                        self.state_container.set_mode(AppMode::ColumnSearch);
3718                        self.shadow_state.borrow_mut().observe_search_start(
3719                            crate::ui::state::shadow_state::SearchType::Column,
3720                            "column_search_mode_fix_next",
3721                        );
3722                    }
3723                    self.next_column_match();
3724                } else {
3725                    debug!(target: "search", "Not in ColumnSearch mode, skipping next_column_match");
3726                }
3727            }
3728            SearchModesAction::PreviousMatch => {
3729                debug!(target: "search", "PreviousMatch action, current_mode={:?}, widget_mode={:?}",
3730                       self.shadow_state.borrow().get_mode(), self.search_modes_widget.current_mode());
3731
3732                // Check both buffer mode and widget mode for consistency
3733                if self.shadow_state.borrow().get_mode() == AppMode::ColumnSearch
3734                    || self.search_modes_widget.current_mode() == Some(SearchMode::ColumnSearch)
3735                {
3736                    debug!(target: "search", "Calling previous_column_match");
3737                    // Ensure mode is correctly set
3738                    if self.shadow_state.borrow().get_mode() != AppMode::ColumnSearch {
3739                        debug!(target: "search", "WARNING: Mode mismatch - fixing");
3740                        self.state_container.set_mode(AppMode::ColumnSearch);
3741                        self.shadow_state.borrow_mut().observe_search_start(
3742                            crate::ui::state::shadow_state::SearchType::Column,
3743                            "column_search_mode_fix_prev",
3744                        );
3745                    }
3746                    self.previous_column_match();
3747                } else {
3748                    debug!(target: "search", "Not in ColumnSearch mode, skipping previous_column_match");
3749                }
3750            }
3751            SearchModesAction::PassThrough => {}
3752        }
3753
3754        // ========== FILTER OPERATIONS ==========
3755
3756        Ok(false)
3757    }
3758
3759    fn handle_help_input(&mut self, key: crossterm::event::KeyEvent) -> Result<bool> {
3760        // Handle help input directly to avoid borrow conflicts
3761
3762        match key.code {
3763            crossterm::event::KeyCode::Esc | crossterm::event::KeyCode::Char('q') => {
3764                self.help_widget.on_exit();
3765                self.state_container.set_help_visible(false);
3766
3767                // Return to Results mode if we have data, otherwise Command mode
3768                let target_mode = if self.state_container.has_dataview() {
3769                    AppMode::Results
3770                } else {
3771                    AppMode::Command
3772                };
3773
3774                // Use proper mode synchronization
3775                self.set_mode_via_shadow_state(target_mode, "escape_from_help");
3776
3777                // Return false to stay in the TUI (not exit)
3778                Ok(false)
3779            }
3780            _ => {
3781                // Delegate other keys to help widget
3782                self.help_widget.handle_key(key);
3783                Ok(false)
3784            }
3785        }
3786    }
3787
3788    // ========== HELP NAVIGATION ==========
3789
3790    fn handle_history_input(&mut self, key: crossterm::event::KeyEvent) -> Result<bool> {
3791        // Handle history input directly to avoid borrow conflicts
3792        use crossterm::event::{KeyCode, KeyModifiers};
3793
3794        let result = match key.code {
3795            KeyCode::Char('c') if key.modifiers.contains(KeyModifiers::CONTROL) => {
3796                crate::ui::input::history_input_handler::HistoryInputResult::Exit
3797            }
3798            KeyCode::Esc => {
3799                // Cancel history search and restore original input
3800                let original_input = self.state_container.cancel_history_search();
3801                if let Some(buffer) = self.state_container.current_buffer_mut() {
3802                    self.shadow_state.borrow_mut().set_mode(
3803                        crate::buffer::AppMode::Command,
3804                        buffer,
3805                        "history_cancelled",
3806                    );
3807                    buffer.set_status_message("History search cancelled".to_string());
3808                }
3809                crate::ui::input::history_input_handler::HistoryInputResult::SwitchToCommand(Some(
3810                    (original_input, 0),
3811                ))
3812            }
3813            KeyCode::Enter => {
3814                // Accept the selected history command
3815                if let Some(command) = self.state_container.accept_history_search() {
3816                    if let Some(buffer) = self.state_container.current_buffer_mut() {
3817                        self.shadow_state.borrow_mut().set_mode(
3818                            crate::buffer::AppMode::Command,
3819                            buffer,
3820                            "history_accepted",
3821                        );
3822                        buffer.set_status_message(
3823                            "Command loaded from history (cursor at start)".to_string(),
3824                        );
3825                    }
3826                    // Return command with cursor at the beginning for better visibility
3827                    crate::ui::input::history_input_handler::HistoryInputResult::SwitchToCommand(
3828                        Some((command, 0)),
3829                    )
3830                } else {
3831                    crate::ui::input::history_input_handler::HistoryInputResult::Continue
3832                }
3833            }
3834            KeyCode::Up => {
3835                self.state_container.history_search_previous();
3836                crate::ui::input::history_input_handler::HistoryInputResult::Continue
3837            }
3838            KeyCode::Down => {
3839                self.state_container.history_search_next();
3840                crate::ui::input::history_input_handler::HistoryInputResult::Continue
3841            }
3842            KeyCode::Char('r') if key.modifiers.contains(KeyModifiers::CONTROL) => {
3843                // Ctrl+R cycles through matches
3844                self.state_container.history_search_next();
3845                crate::ui::input::history_input_handler::HistoryInputResult::Continue
3846            }
3847            KeyCode::Backspace => {
3848                self.state_container.history_search_backspace();
3849                crate::ui::input::history_input_handler::HistoryInputResult::Continue
3850            }
3851            KeyCode::Char(c) => {
3852                self.state_container.history_search_add_char(c);
3853                crate::ui::input::history_input_handler::HistoryInputResult::Continue
3854            }
3855            _ => crate::ui::input::history_input_handler::HistoryInputResult::Continue,
3856        };
3857
3858        // Handle the result
3859        match result {
3860            crate::ui::input::history_input_handler::HistoryInputResult::Exit => return Ok(true),
3861            crate::ui::input::history_input_handler::HistoryInputResult::SwitchToCommand(
3862                input_data,
3863            ) => {
3864                if let Some((text, cursor_pos)) = input_data {
3865                    self.set_input_text_with_cursor(text, cursor_pos);
3866                    // Sync to ensure scroll is reset properly
3867                    self.sync_all_input_states();
3868                }
3869            }
3870            crate::ui::input::history_input_handler::HistoryInputResult::Continue => {
3871                // Update history matches if needed
3872                if crate::ui::input::history_input_handler::key_updates_search(key) {
3873                    self.update_history_matches_in_container();
3874                }
3875            }
3876        }
3877
3878        Ok(false)
3879    }
3880
3881    /// Update history matches in the `AppStateContainer` with schema context
3882    fn update_history_matches_in_container(&mut self) {
3883        // Get current schema columns and data source for better matching
3884        let (current_columns, current_source_str) =
3885            if let Some(dataview) = self.state_container.get_buffer_dataview() {
3886                (
3887                    dataview.column_names(),              // Gets visible columns
3888                    Some(dataview.source().name.clone()), // Gets table name from DataTable
3889                )
3890            } else {
3891                (vec![], None)
3892            };
3893
3894        let current_source = current_source_str.as_deref();
3895        let query = self.state_container.history_search().query.clone();
3896
3897        self.state_container.update_history_search_with_schema(
3898            query,
3899            &current_columns,
3900            current_source,
3901        );
3902    }
3903
3904    fn handle_debug_input(&mut self, key: crossterm::event::KeyEvent) -> Result<bool> {
3905        // Create context and delegate to extracted handler
3906        let mut ctx = crate::ui::input::input_handlers::DebugInputContext {
3907            buffer_manager: self.state_container.buffers_mut(),
3908            debug_widget: &mut self.debug_widget,
3909            shadow_state: &self.shadow_state,
3910        };
3911
3912        let should_quit = crate::ui::input::input_handlers::handle_debug_input(&mut ctx, key)?;
3913
3914        // If the extracted handler didn't handle these special keys, we still do them here
3915        // (until we can extract yank operations too)
3916        if !should_quit {
3917            match key.code {
3918                KeyCode::Char('t') if key.modifiers.contains(KeyModifiers::CONTROL) => {
3919                    // Ctrl+T: "Yank as Test" - capture current session as test case
3920                    self.yank_as_test_case();
3921                }
3922                KeyCode::Char('y') if key.modifiers.contains(KeyModifiers::SHIFT) => {
3923                    // Shift+Y: Yank debug dump with context
3924                    self.yank_debug_with_context();
3925                }
3926                _ => {}
3927            }
3928        }
3929
3930        Ok(should_quit)
3931        // ========== QUERY OPERATIONS ==========
3932    }
3933
3934    fn handle_pretty_query_input(&mut self, key: crossterm::event::KeyEvent) -> Result<bool> {
3935        // Create context and delegate to extracted handler
3936        let mut ctx = crate::ui::input::input_handlers::DebugInputContext {
3937            buffer_manager: self.state_container.buffers_mut(),
3938            debug_widget: &mut self.debug_widget,
3939            shadow_state: &self.shadow_state,
3940        };
3941
3942        crate::ui::input::input_handlers::handle_pretty_query_input(&mut ctx, key)
3943    }
3944
3945    pub fn execute_query_v2(&mut self, query: &str) -> Result<()> {
3946        // Use orchestrator to handle all the query execution logic
3947        let context = self.query_orchestrator.execute_query(
3948            query,
3949            &mut self.state_container,
3950            &self.vim_search_adapter,
3951        );
3952
3953        match context {
3954            Ok(ctx) => {
3955                // Apply the new DataView
3956                self.state_container
3957                    .set_dataview(Some(ctx.result.dataview.clone()));
3958
3959                // Update viewport
3960                self.update_viewport_manager(Some(ctx.result.dataview.clone()));
3961
3962                // Update navigation state
3963                self.state_container
3964                    .update_data_size(ctx.result.stats.row_count, ctx.result.stats.column_count);
3965
3966                // Calculate column widths
3967                self.calculate_optimal_column_widths();
3968
3969                // Update status message
3970                self.state_container
3971                    .set_status_message(ctx.result.status_message());
3972
3973                // Add to history
3974                self.state_container
3975                    .command_history_mut()
3976                    .add_entry_with_schema(
3977                        ctx.query.clone(),
3978                        true,
3979                        Some(ctx.result.stats.execution_time.as_millis() as u64),
3980                        ctx.result.column_names(),
3981                        Some(ctx.result.table_name()),
3982                    )?;
3983
3984                // Switch to results mode - use sync_mode to ensure all state is synchronized
3985                self.sync_mode(AppMode::Results, "execute_query_success");
3986
3987                // Reset table
3988                self.reset_table_state();
3989
3990                Ok(())
3991            }
3992            Err(e) => {
3993                let error_msg = format!("Query error: {e}");
3994                self.state_container.set_status_message(error_msg.clone());
3995
3996                // Add to history as failed
3997                self.state_container
3998                    .command_history_mut()
3999                    .add_entry_with_schema(query.to_string(), false, None, vec![], None)?;
4000
4001                Err(e)
4002            }
4003        }
4004    }
4005
4006    fn handle_completion(&mut self) {
4007        let cursor_pos = self.get_input_cursor();
4008        let query_str = self.get_input_text();
4009        let query = query_str.as_str();
4010
4011        let hybrid_result = self.hybrid_parser.get_completions(query, cursor_pos);
4012        if !hybrid_result.suggestions.is_empty() {
4013            self.state_container.set_status_message(format!(
4014                "Suggestions: {}",
4015                hybrid_result.suggestions.join(", ")
4016            ));
4017        }
4018    }
4019
4020    fn apply_completion(&mut self) {
4021        let cursor_pos = self.get_input_cursor();
4022        let query = self.get_input_text();
4023
4024        // Get the current completion suggestion
4025        let suggestion = match self.get_or_refresh_completion(&query, cursor_pos) {
4026            Some(s) => s,
4027            None => return,
4028        };
4029
4030        // Apply the completion to the text
4031        self.apply_completion_to_input(&query, cursor_pos, &suggestion);
4032    }
4033
4034    /// Get current completion or refresh if context changed
4035    /// Returns None if no completions available
4036    fn get_or_refresh_completion(&mut self, query: &str, cursor_pos: usize) -> Option<String> {
4037        let is_same_context = self
4038            .state_container
4039            .is_same_completion_context(query, cursor_pos);
4040
4041        if !is_same_context {
4042            // New completion context - get fresh suggestions
4043            let hybrid_result = self.hybrid_parser.get_completions(query, cursor_pos);
4044            if hybrid_result.suggestions.is_empty() {
4045                self.state_container
4046                    .set_status_message("No completions available".to_string());
4047                return None;
4048            }
4049
4050            self.state_container
4051                .set_completion_suggestions(hybrid_result.suggestions);
4052        } else if self.state_container.is_completion_active() {
4053            // Cycle to next suggestion
4054            self.state_container.next_completion();
4055        } else {
4056            self.state_container
4057                .set_status_message("No completions available".to_string());
4058            return None;
4059        }
4060
4061        // Get the current suggestion from AppStateContainer
4062        if let Some(sugg) = self.state_container.get_current_completion() {
4063            Some(sugg)
4064        } else {
4065            self.state_container
4066                .set_status_message("No completion selected".to_string());
4067            None
4068        }
4069    }
4070
4071    /// Apply a completion suggestion to the input
4072    fn apply_completion_to_input(&mut self, query: &str, cursor_pos: usize, suggestion: &str) {
4073        let partial_word =
4074            crate::ui::utils::text_operations::extract_partial_word_at_cursor(query, cursor_pos);
4075
4076        if let Some(partial) = partial_word {
4077            self.apply_partial_completion(query, cursor_pos, &partial, suggestion);
4078        } else {
4079            self.apply_full_insertion(query, cursor_pos, suggestion);
4080        }
4081    }
4082
4083    /// Apply completion when we have a partial word to complete
4084    fn apply_partial_completion(
4085        &mut self,
4086        query: &str,
4087        cursor_pos: usize,
4088        partial: &str,
4089        suggestion: &str,
4090    ) {
4091        // Use extracted completion logic
4092        let result = crate::ui::utils::text_operations::apply_completion_to_text(
4093            query, cursor_pos, partial, suggestion,
4094        );
4095
4096        // Use helper to set text and cursor together - this ensures sync
4097        self.set_input_text_with_cursor(result.new_text.clone(), result.new_cursor_position);
4098
4099        // Update completion state for next tab press
4100        self.state_container
4101            .update_completion_context(result.new_text.clone(), result.new_cursor_position);
4102
4103        // Generate status message
4104        let completion = self.state_container.completion();
4105        let suggestion_info = if completion.suggestions.len() > 1 {
4106            format!(
4107                "Completed: {} ({}/{} - Tab for next)",
4108                suggestion,
4109                completion.current_index + 1,
4110                completion.suggestions.len()
4111            )
4112        } else {
4113            format!("Completed: {suggestion}")
4114        };
4115        drop(completion);
4116        self.state_container.set_status_message(suggestion_info);
4117    }
4118
4119    /// Apply completion as a full insertion at cursor position
4120    fn apply_full_insertion(&mut self, query: &str, cursor_pos: usize, suggestion: &str) {
4121        // Just insert the suggestion at cursor position
4122        let before_cursor = &query[..cursor_pos];
4123        let after_cursor = &query[cursor_pos..];
4124        let new_query = format!("{before_cursor}{suggestion}{after_cursor}");
4125
4126        // Special case: if we completed a string method like Contains(''), position cursor inside quotes
4127        let cursor_pos_new = if suggestion.ends_with("('')") {
4128            // Position cursor between the quotes
4129            cursor_pos + suggestion.len() - 2
4130        } else {
4131            cursor_pos + suggestion.len()
4132        };
4133
4134        // Use helper to set text through buffer
4135        self.set_input_text(new_query.clone());
4136
4137        // Set cursor to correct position
4138        if let Some(buffer) = self.state_container.buffers_mut().current_mut() {
4139            buffer.set_input_cursor_position(cursor_pos_new);
4140            // Sync all input states after undo/redo
4141            self.sync_all_input_states();
4142        }
4143
4144        // Update completion state
4145        self.state_container
4146            .update_completion_context(new_query, cursor_pos_new);
4147
4148        self.state_container
4149            .set_status_message(format!("Inserted: {suggestion}"));
4150    }
4151
4152    // Note: expand_asterisk and get_table_columns removed - moved to Buffer and use hybrid_parser directly
4153
4154    // ========== COLUMN INFO ==========
4155
4156    // Helper to get estimated visible rows based on terminal size
4157
4158    fn get_column_count(&self) -> usize {
4159        // Use DataProvider trait for column count (migration step)
4160        if let Some(provider) = self.get_data_provider() {
4161            provider.get_column_count()
4162        } else {
4163            0
4164        }
4165    }
4166
4167    /// Get column count using `DataProvider` trait (new pattern)
4168    /// This demonstrates using the trait-based approach for column information
4169    /// Get column names using `DataProvider` trait
4170    /// Part of the migration to trait-based data access
4171    fn get_column_names_via_provider(&self) -> Vec<String> {
4172        if let Some(provider) = self.get_data_provider() {
4173            provider.get_column_names()
4174        } else {
4175            Vec::new()
4176        }
4177    }
4178
4179    // ========== NAVIGATION METHODS ==========
4180
4181    // ========== COLUMN PIN/HIDE ==========
4182
4183    fn toggle_column_pin_impl(&mut self) {
4184        // Get visual column index from ViewportManager's crosshair
4185        let visual_col_idx = if let Some(ref viewport_manager) = *self.viewport_manager.borrow() {
4186            viewport_manager.get_crosshair_col()
4187        } else {
4188            0
4189        };
4190
4191        // Get column name at visual position
4192        let column_name = if let Some(dataview) = self.state_container.get_buffer_dataview() {
4193            let all_columns = dataview.column_names();
4194            all_columns.get(visual_col_idx).cloned()
4195        } else {
4196            None
4197        };
4198
4199        if let Some(col_name) = column_name {
4200            if let Some(dataview) = self.state_container.get_buffer_dataview_mut() {
4201                // Check if this column name is already pinned
4202                let pinned_names = dataview.get_pinned_column_names();
4203                if pinned_names.contains(&col_name) {
4204                    // Column is already pinned, unpin it
4205                    dataview.unpin_column_by_name(&col_name);
4206                    self.state_container
4207                        .set_status_message(format!("Column '{col_name}' unpinned"));
4208                } else {
4209                    // Try to pin the column by name
4210                    match dataview.pin_column_by_name(&col_name) {
4211                        Ok(()) => {
4212                            self.state_container
4213                                .set_status_message(format!("Column '{col_name}' pinned [P]"));
4214                        }
4215                        Err(e) => {
4216                            self.state_container.set_status_message(e.to_string());
4217                        }
4218                    }
4219                }
4220
4221                // Update ViewportManager with the modified DataView
4222                if let Some(updated_dataview) = self.state_container.get_buffer_dataview() {
4223                    self.update_viewport_manager(Some(updated_dataview.clone()));
4224                }
4225            }
4226        } else {
4227            self.state_container
4228                .set_status_message("No column to pin at current position".to_string());
4229        }
4230    }
4231
4232    fn clear_all_pinned_columns_impl(&mut self) {
4233        if let Some(dataview) = self.state_container.get_buffer_dataview_mut() {
4234            dataview.clear_pinned_columns();
4235        }
4236        self.state_container
4237            .set_status_message("All columns unpinned".to_string());
4238
4239        // Update ViewportManager with the modified DataView
4240        if let Some(updated_dataview) = self.state_container.get_buffer_dataview() {
4241            self.update_viewport_manager(Some(updated_dataview.clone()));
4242        }
4243    }
4244
4245    fn calculate_column_statistics(&mut self) {
4246        use std::time::Instant;
4247
4248        let start_total = Instant::now();
4249
4250        // Collect all data first, then drop the buffer reference before calling analyzer
4251        let (column_name, data_to_analyze) = {
4252            // Get column names using DataProvider trait
4253            let headers = self.get_column_names_via_provider();
4254            if headers.is_empty() {
4255                return;
4256            }
4257
4258            let current_column = self.state_container.get_current_column();
4259            if current_column >= headers.len() {
4260                return;
4261            }
4262
4263            let column_name = headers[current_column].clone();
4264
4265            // Extract column data using DataProvider trait
4266            let data_to_analyze: Vec<String> = if let Some(provider) = self.get_data_provider() {
4267                let row_count = provider.get_row_count();
4268                let mut column_data = Vec::with_capacity(row_count);
4269
4270                for row_idx in 0..row_count {
4271                    if let Some(row) = provider.get_row(row_idx) {
4272                        if current_column < row.len() {
4273                            column_data.push(row[current_column].clone());
4274                        } else {
4275                            // Handle missing column data
4276                            column_data.push(String::new());
4277                        }
4278                    }
4279                }
4280
4281                column_data
4282            } else {
4283                // No data provider available
4284                return;
4285            };
4286
4287            (column_name, data_to_analyze)
4288        };
4289
4290        // Convert to references for the analyzer
4291        let data_refs: Vec<&str> = data_to_analyze
4292            .iter()
4293            .map(std::string::String::as_str)
4294            .collect();
4295
4296        // Use DataAnalyzer to calculate statistics
4297        let analyzer_stats = self
4298            .data_analyzer
4299            .calculate_column_statistics(&column_name, &data_refs);
4300
4301        // Convert from DataAnalyzer's ColumnStatistics to buffer's ColumnStatistics
4302        let stats = ColumnStatistics {
4303            column_name: analyzer_stats.column_name,
4304            column_type: match analyzer_stats.data_type {
4305                data_analyzer::ColumnType::Integer | data_analyzer::ColumnType::Float => {
4306                    ColumnType::Numeric
4307                }
4308                data_analyzer::ColumnType::String
4309                | data_analyzer::ColumnType::Boolean
4310                | data_analyzer::ColumnType::Date => ColumnType::String,
4311                data_analyzer::ColumnType::Mixed => ColumnType::Mixed,
4312                data_analyzer::ColumnType::Unknown => ColumnType::Mixed,
4313            },
4314            total_count: analyzer_stats.total_values,
4315            null_count: analyzer_stats.null_values,
4316            unique_count: analyzer_stats.unique_values,
4317            frequency_map: analyzer_stats.frequency_map.clone(),
4318            // For numeric columns, parse the min/max strings to f64
4319            min: analyzer_stats
4320                .min_value
4321                .as_ref()
4322                .and_then(|s| s.parse::<f64>().ok()),
4323            max: analyzer_stats
4324                .max_value
4325                .as_ref()
4326                .and_then(|s| s.parse::<f64>().ok()),
4327            sum: analyzer_stats.sum_value,
4328            mean: analyzer_stats.avg_value,
4329            median: analyzer_stats.median_value,
4330        };
4331
4332        // Calculate total time
4333        let elapsed = start_total.elapsed();
4334
4335        self.state_container.set_column_stats(Some(stats));
4336
4337        // Show timing in status message
4338        self.state_container.set_status_message(format!(
4339            "Column stats: {:.1}ms for {} values ({} unique)",
4340            elapsed.as_secs_f64() * 1000.0,
4341            data_to_analyze.len(),
4342            analyzer_stats.unique_values
4343        ));
4344
4345        self.state_container.set_mode(AppMode::ColumnStats);
4346        self.shadow_state
4347            .borrow_mut()
4348            .observe_mode_change(AppMode::ColumnStats, "column_stats_requested");
4349    }
4350
4351    fn check_parser_error(&self, query: &str) -> Option<String> {
4352        crate::ui::operations::simple_operations::check_parser_error(query)
4353    }
4354
4355    fn update_viewport_size(&mut self) {
4356        // Update the stored viewport size based on current terminal size
4357        if let Ok((width, height)) = crossterm::terminal::size() {
4358            // Calculate the actual data area height
4359            let data_rows_available = Self::calculate_available_data_rows(height);
4360
4361            // Let ViewportManager handle the calculations
4362            let visible_rows = {
4363                let mut viewport_manager_borrow = self.viewport_manager.borrow_mut();
4364                let viewport_manager = viewport_manager_borrow
4365                    .as_mut()
4366                    .expect("ViewportManager must exist for viewport size update");
4367                viewport_manager.update_terminal_size(width, data_rows_available)
4368            };
4369
4370            // Update buffer's last_visible_rows
4371            self.state_container.set_last_visible_rows(visible_rows);
4372
4373            // Update NavigationState's viewport dimensions
4374            self.state_container
4375                .navigation_mut()
4376                .set_viewport_size(visible_rows, width as usize);
4377
4378            info!(target: "navigation", "update_viewport_size - viewport set to: {}x{} rows", visible_rows, width);
4379        }
4380    }
4381
4382    // ========== SEARCH EXECUTION ==========
4383
4384    // Search and filter functions
4385    fn perform_search(&mut self) {
4386        if let Some(dataview) = self.get_current_data() {
4387            // Convert DataView rows to Vec<Vec<String>> for SearchManager
4388            let data: Vec<Vec<String>> = (0..dataview.row_count())
4389                .filter_map(|i| dataview.get_row(i))
4390                .map(|row| {
4391                    row.values
4392                        .iter()
4393                        .map(std::string::ToString::to_string)
4394                        .collect()
4395                })
4396                .collect();
4397
4398            // Get search pattern from buffer
4399            let pattern = self.state_container.get_search_pattern().to_string();
4400
4401            info!(target: "search", "=== SEARCH START ===");
4402            info!(target: "search", "Pattern: '{}', case_insensitive: {}",
4403                  pattern, self.state_container.is_case_insensitive());
4404            info!(target: "search", "Data dimensions: {} rows x {} columns",
4405                  data.len(), data.first().map_or(0, std::vec::Vec::len));
4406
4407            // Log column names to understand ordering
4408            let column_names = dataview.column_names();
4409            info!(target: "search", "Column names (first 5): {:?}",
4410                  column_names.iter().take(5).collect::<Vec<_>>());
4411
4412            // Log the first few rows of data we're searching
4413            for (i, row) in data.iter().take(10).enumerate() {
4414                info!(target: "search", "  Data row {}: [{}]", i,
4415                      row.iter().take(5).map(|s| format!("'{s}'")).collect::<Vec<_>>().join(", "));
4416            }
4417
4418            // Get visible columns if needed (for now search all columns)
4419            let visible_columns = None;
4420
4421            // Perform search using SearchManager
4422            let match_count = {
4423                let mut search_manager = self.search_manager.borrow_mut();
4424
4425                // IMPORTANT: Clear any previous search results first
4426                search_manager.clear();
4427                info!(target: "search", "Cleared previous search results");
4428
4429                // Update case sensitivity based on current setting
4430                search_manager.set_case_sensitive(!self.state_container.is_case_insensitive());
4431                info!(target: "search", "Set case_sensitive to {}", !self.state_container.is_case_insensitive());
4432
4433                // Perform the search
4434                let count = search_manager.search(&pattern, &data, visible_columns);
4435                info!(target: "search", "SearchManager.search() returned {} matches", count);
4436                count
4437            };
4438
4439            info!(target: "search", "SearchManager found {} matches", match_count);
4440
4441            // Process the matches
4442            if match_count > 0 {
4443                // Get first match for navigation and log details
4444                let (first_row, first_col) = {
4445                    let search_manager = self.search_manager.borrow();
4446                    if let Some(first_match) = search_manager.first_match() {
4447                        info!(target: "search", "FIRST MATCH DETAILS:");
4448                        info!(target: "search", "  Data coordinates: row={}, col={}",
4449                              first_match.row, first_match.column);
4450                        info!(target: "search", "  Matched value: '{}'", first_match.value);
4451                        info!(target: "search", "  Highlight range: {:?}", first_match.highlight_range);
4452
4453                        // Log first 5 matches for debugging
4454                        for (i, m) in search_manager.all_matches().iter().take(5).enumerate() {
4455                            info!(target: "search", "  Match #{}: row={}, col={}, value='{}'",
4456                                  i + 1, m.row, m.column, m.value);
4457                        }
4458
4459                        (first_match.row, first_match.column)
4460                    } else {
4461                        warn!(target: "search", "SearchManager reported matches but first_match() is None!");
4462                        (0, 0)
4463                    }
4464                };
4465
4466                // Update state container and buffer with matches
4467                self.state_container.set_table_selected_row(Some(first_row));
4468
4469                // CRITICAL: Update TableWidgetManager to trigger re-render
4470                info!(target: "search", "Updating TableWidgetManager to navigate to ({}, {})", first_row, first_col);
4471                self.table_widget_manager
4472                    .borrow_mut()
4473                    .navigate_to_search_match(first_row, first_col);
4474
4475                // Log what's actually at the position we're navigating to
4476                if let Some(dataview) = self.get_current_data() {
4477                    if let Some(row_data) = dataview.get_row(first_row) {
4478                        if first_col < row_data.values.len() {
4479                            info!(target: "search", "VALUE AT NAVIGATION TARGET ({}, {}): '{}'",
4480                                  first_row, first_col, row_data.values[first_col]);
4481                        }
4482                    }
4483                }
4484
4485                // Convert matches to buffer format
4486                let buffer_matches: Vec<(usize, usize)> = {
4487                    let search_manager = self.search_manager.borrow();
4488                    search_manager
4489                        .all_matches()
4490                        .iter()
4491                        .map(|m| (m.row, m.column))
4492                        .collect()
4493                };
4494
4495                // Also update AppStateContainer with matches (for compatibility)
4496                // Convert SearchManager matches to state_container format (row_start, col_start, row_end, col_end)
4497                let state_matches: Vec<(usize, usize, usize, usize)> = {
4498                    let search_manager = self.search_manager.borrow();
4499                    search_manager
4500                        .all_matches()
4501                        .iter()
4502                        .map(|m| {
4503                            // For now, treat each match as a single cell
4504                            (m.row, m.column, m.row, m.column)
4505                        })
4506                        .collect()
4507                };
4508                self.state_container.search_mut().matches = state_matches;
4509
4510                self.state_container
4511                    .set_search_matches_with_index(buffer_matches.clone(), 0);
4512                self.state_container
4513                    .set_current_match(Some((first_row, first_col)));
4514                self.state_container
4515                    .set_status_message(format!("Found {match_count} matches"));
4516
4517                info!(target: "search", "Search found {} matches for pattern '{}'", match_count, pattern);
4518            } else {
4519                // Clear search state
4520                self.state_container.search_mut().matches.clear();
4521                self.state_container.clear_search_state();
4522                self.state_container.set_current_match(None);
4523
4524                info!(target: "search", "No matches found for pattern '{}'", pattern);
4525            }
4526        }
4527    }
4528
4529    // --- Vim Search Methods ---
4530
4531    /// Start vim-like forward search (/ key)
4532    fn start_vim_search(&mut self) {
4533        info!(target: "vim_search", "Starting vim search mode");
4534
4535        // Start search mode in VimSearchManager
4536        self.vim_search_adapter.borrow_mut().start_search();
4537
4538        // Observe search start
4539        self.shadow_state.borrow_mut().observe_search_start(
4540            crate::ui::state::shadow_state::SearchType::Vim,
4541            "slash_key_pressed",
4542        );
4543
4544        // Use the existing SearchModesWidget which already has perfect debouncing
4545        self.enter_search_mode(SearchMode::Search);
4546    }
4547
4548    /// Navigate to next vim search match (n key)
4549    fn vim_search_next(&mut self) {
4550        if !self.vim_search_adapter.borrow().is_navigating() {
4551            // Try to resume last search if not currently navigating
4552            let resumed = {
4553                let mut viewport_borrow = self.viewport_manager.borrow_mut();
4554                if let Some(ref mut viewport) = *viewport_borrow {
4555                    if let Some(dataview) = self.state_container.get_buffer_dataview() {
4556                        self.vim_search_adapter
4557                            .borrow_mut()
4558                            .resume_last_search(dataview, viewport)
4559                    } else {
4560                        false
4561                    }
4562                } else {
4563                    false
4564                }
4565            };
4566
4567            if !resumed {
4568                self.state_container
4569                    .set_status_message("No previous search pattern".to_string());
4570                return;
4571            }
4572        }
4573
4574        // Navigate to next match
4575        let result = {
4576            let mut viewport_borrow = self.viewport_manager.borrow_mut();
4577            if let Some(ref mut viewport) = *viewport_borrow {
4578                let search_match = self.vim_search_adapter.borrow_mut().next_match(viewport);
4579                if search_match.is_some() {
4580                    let match_info = self.vim_search_adapter.borrow().get_match_info();
4581                    search_match.map(|m| (m, match_info))
4582                } else {
4583                    None
4584                }
4585            } else {
4586                None
4587            }
4588        }; // Drop viewport_borrow here
4589
4590        // Update selected row AND column AFTER dropping the viewport borrow
4591        if let Some((ref search_match, _)) = result {
4592            // Log what we're updating
4593            info!(target: "search", 
4594                "=== UPDATING TUI STATE FOR VIM SEARCH ===");
4595            info!(target: "search", 
4596                "Setting selected row to: {}", search_match.row);
4597            info!(target: "search", 
4598                "Setting selected column to: {} (visual col)", search_match.col);
4599
4600            // Verify what's actually at this position
4601            if let Some(dataview) = self.state_container.get_buffer_dataview() {
4602                // First, let's verify what row we're actually getting
4603                info!(target: "search",
4604                    "DEBUG: Fetching row {} from dataview with {} total rows",
4605                    search_match.row, dataview.row_count());
4606
4607                if let Some(row_data) = dataview.get_row(search_match.row) {
4608                    // Log ALL values in this row to debug the mismatch
4609                    info!(target: "search",
4610                        "Row {} has {} values, first 5: {:?}",
4611                        search_match.row, row_data.values.len(),
4612                        row_data.values.iter().take(5).map(std::string::ToString::to_string).collect::<Vec<_>>());
4613
4614                    if search_match.col < row_data.values.len() {
4615                        let actual_value = &row_data.values[search_match.col];
4616                        info!(target: "search", 
4617                            "Actual value at row {} col {}: '{}'", 
4618                            search_match.row, search_match.col, actual_value);
4619
4620                        // Check if it actually contains the pattern
4621                        let pattern = self
4622                            .vim_search_adapter
4623                            .borrow()
4624                            .get_pattern()
4625                            .unwrap_or_default();
4626                        let contains_pattern = actual_value
4627                            .to_string()
4628                            .to_lowercase()
4629                            .contains(&pattern.to_lowercase());
4630                        if contains_pattern {
4631                            info!(target: "search", 
4632                                "✓ Confirmed: Cell contains pattern '{}'", pattern);
4633                        } else {
4634                            warn!(target: "search", 
4635                                "WARNING: Cell at ({}, {}) = '{}' does NOT contain pattern '{}'!", 
4636                                search_match.row, search_match.col, actual_value, pattern);
4637                        }
4638                    } else {
4639                        warn!(target: "search", 
4640                            "Column {} is out of bounds for row {} (row has {} values)", 
4641                            search_match.col, search_match.row, row_data.values.len());
4642                    }
4643                } else {
4644                    warn!(target: "search", 
4645                        "Could not get row data for row {}", search_match.row);
4646                }
4647
4648                // Also log display columns to understand the mapping
4649                let display_columns = dataview.get_display_columns();
4650                info!(target: "search", 
4651                    "Display columns mapping (first 10): {:?}", 
4652                    display_columns.iter().take(10).collect::<Vec<_>>());
4653            }
4654
4655            self.state_container
4656                .set_table_selected_row(Some(search_match.row));
4657            self.state_container
4658                .set_selected_row(Some(search_match.row));
4659
4660            // Update the selected column - search_match.col is already in visual coordinates
4661            // Keep everything in visual column indices for consistency
4662            info!(target: "search",
4663                "Setting column to visual index {}",
4664                search_match.col);
4665
4666            // Update all column-related state to the visual column index
4667            self.state_container
4668                .set_current_column_buffer(search_match.col);
4669            self.state_container.navigation_mut().selected_column = search_match.col;
4670
4671            // CRITICAL: Update SelectionState's selected_column too!
4672            self.state_container.select_column(search_match.col);
4673            info!(target: "search", 
4674                "Updated SelectionState column to: {}", search_match.col);
4675
4676            // Log the current state of all column-related fields
4677            info!(target: "search", 
4678                "Column state after update: nav.selected_column={}, buffer.current_column={}, selection.selected_column={}", 
4679                self.state_container.navigation().selected_column,
4680                self.state_container.get_current_column(),
4681                self.state_container.selection().selected_column);
4682
4683            // CRITICAL: Sync NavigationState with ViewportManager after vim search navigation
4684            // ViewportManager has the correct state after navigation, sync it back
4685            self.sync_navigation_with_viewport();
4686
4687            // Also update the buffer scroll offset
4688            let scroll_offset = self.state_container.navigation().scroll_offset;
4689            self.state_container.set_scroll_offset(scroll_offset);
4690
4691            // CRITICAL: Update TableWidgetManager to trigger re-render
4692            info!(target: "search", "Updating TableWidgetManager for vim search navigation to ({}, {})",
4693                  search_match.row, search_match.col);
4694            self.table_widget_manager
4695                .borrow_mut()
4696                .navigate_to(search_match.row, search_match.col);
4697
4698            // Log column state after TableWidgetManager update
4699            info!(target: "search", 
4700                "After TableWidgetManager update: nav.selected_column={}, buffer.current_column={}, selection.selected_column={}", 
4701                self.state_container.navigation().selected_column,
4702                self.state_container.get_current_column(),
4703                self.state_container.selection().selected_column);
4704
4705            // The ViewportManager has already handled all scrolling logic
4706            // Our sync_navigation_with_viewport() call above has updated NavigationState
4707            // No need for additional manual scroll updates
4708        }
4709
4710        // Update status without borrow conflicts
4711        if let Some((search_match, match_info)) = result {
4712            if let Some((current, total)) = match_info {
4713                self.state_container.set_status_message(format!(
4714                    "Match {}/{} at ({}, {})",
4715                    current,
4716                    total,
4717                    search_match.row + 1,
4718                    search_match.col + 1
4719                ));
4720            }
4721
4722            // Final column state logging
4723            info!(target: "search", 
4724                "FINAL vim_search_next state: nav.selected_column={}, buffer.current_column={}, selection.selected_column={}", 
4725                self.state_container.navigation().selected_column,
4726                self.state_container.get_current_column(),
4727                self.state_container.selection().selected_column);
4728
4729            // CRITICAL: Verify what's actually at the final position
4730            if let Some(dataview) = self.state_container.get_buffer_dataview() {
4731                let final_row = self.state_container.navigation().selected_row;
4732                let final_col = self.state_container.navigation().selected_column;
4733
4734                if let Some(row_data) = dataview.get_row(final_row) {
4735                    if final_col < row_data.values.len() {
4736                        let actual_value = &row_data.values[final_col];
4737                        info!(target: "search", 
4738                            "VERIFICATION: Cell at final position ({}, {}) contains: '{}'",
4739                            final_row, final_col, actual_value);
4740
4741                        let pattern = self
4742                            .vim_search_adapter
4743                            .borrow()
4744                            .get_pattern()
4745                            .unwrap_or_default();
4746                        if !actual_value
4747                            .to_string()
4748                            .to_lowercase()
4749                            .contains(&pattern.to_lowercase())
4750                        {
4751                            error!(target: "search",
4752                                "ERROR: Final cell '{}' does NOT contain search pattern '{}'!",
4753                                actual_value, pattern);
4754                        }
4755                    }
4756                }
4757            }
4758        }
4759    }
4760
4761    /// Navigate to previous vim search match (N key)
4762    fn vim_search_previous(&mut self) {
4763        if !self.vim_search_adapter.borrow().is_navigating() {
4764            // Try to resume last search if not currently navigating
4765            let resumed = {
4766                let mut viewport_borrow = self.viewport_manager.borrow_mut();
4767                if let Some(ref mut viewport) = *viewport_borrow {
4768                    if let Some(dataview) = self.state_container.get_buffer_dataview() {
4769                        self.vim_search_adapter
4770                            .borrow_mut()
4771                            .resume_last_search(dataview, viewport)
4772                    } else {
4773                        false
4774                    }
4775                } else {
4776                    false
4777                }
4778            };
4779
4780            if !resumed {
4781                self.state_container
4782                    .set_status_message("No previous search pattern".to_string());
4783                return;
4784            }
4785        }
4786
4787        // Navigate to previous match
4788        let result = {
4789            let mut viewport_borrow = self.viewport_manager.borrow_mut();
4790            if let Some(ref mut viewport) = *viewport_borrow {
4791                let search_match = self
4792                    .vim_search_adapter
4793                    .borrow_mut()
4794                    .previous_match(viewport);
4795                if search_match.is_some() {
4796                    let match_info = self.vim_search_adapter.borrow().get_match_info();
4797                    search_match.map(|m| (m, match_info))
4798                } else {
4799                    None
4800                }
4801            } else {
4802                None
4803            }
4804        }; // Drop viewport_borrow here
4805
4806        // Update selected row AND column AFTER dropping the viewport borrow
4807        if let Some((ref search_match, _)) = result {
4808            self.state_container
4809                .set_table_selected_row(Some(search_match.row));
4810            self.state_container
4811                .set_selected_row(Some(search_match.row));
4812
4813            // Update the selected column - search_match.col is already in visual coordinates
4814            // Keep everything in visual column indices for consistency
4815            info!(target: "search",
4816                "Setting column to visual index {}",
4817                search_match.col);
4818
4819            // Update all column-related state to the visual column index
4820            self.state_container
4821                .set_current_column_buffer(search_match.col);
4822            self.state_container.navigation_mut().selected_column = search_match.col;
4823
4824            // CRITICAL: Update SelectionState's selected_column too!
4825            self.state_container.select_column(search_match.col);
4826            info!(target: "search", 
4827                "Updated SelectionState column to: {}", search_match.col);
4828
4829            // Log the current state of all column-related fields
4830            info!(target: "search", 
4831                "Column state after update: nav.selected_column={}, buffer.current_column={}, selection.selected_column={}", 
4832                self.state_container.navigation().selected_column,
4833                self.state_container.get_current_column(),
4834                self.state_container.selection().selected_column);
4835
4836            // CRITICAL: Sync NavigationState with ViewportManager after vim search navigation
4837            // ViewportManager has the correct state after navigation, sync it back
4838            self.sync_navigation_with_viewport();
4839
4840            // Also update the buffer scroll offset
4841            let scroll_offset = self.state_container.navigation().scroll_offset;
4842            self.state_container.set_scroll_offset(scroll_offset);
4843
4844            // CRITICAL: Update TableWidgetManager to trigger re-render
4845            info!(target: "search", "Updating TableWidgetManager for vim search navigation to ({}, {})",
4846                  search_match.row, search_match.col);
4847            self.table_widget_manager
4848                .borrow_mut()
4849                .navigate_to(search_match.row, search_match.col);
4850
4851            // Log column state after TableWidgetManager update
4852            info!(target: "search", 
4853                "After TableWidgetManager update: nav.selected_column={}, buffer.current_column={}, selection.selected_column={}", 
4854                self.state_container.navigation().selected_column,
4855                self.state_container.get_current_column(),
4856                self.state_container.selection().selected_column);
4857
4858            // The ViewportManager has already handled all scrolling logic
4859            // Our sync_navigation_with_viewport() call above has updated NavigationState
4860            // No need for additional manual scroll updates
4861        }
4862
4863        // Update status without borrow conflicts
4864        if let Some((search_match, match_info)) = result {
4865            if let Some((current, total)) = match_info {
4866                self.state_container.set_status_message(format!(
4867                    "Match {}/{} at ({}, {})",
4868                    current,
4869                    total,
4870                    search_match.row + 1,
4871                    search_match.col + 1
4872                ));
4873            }
4874        }
4875        // ========== FILTER EXECUTION ==========
4876    }
4877
4878    fn apply_filter(&mut self, pattern: &str) {
4879        use std::sync::atomic::{AtomicUsize, Ordering};
4880
4881        // Simple re-entrancy detection without macros
4882        static FILTER_DEPTH: AtomicUsize = AtomicUsize::new(0);
4883        let depth = FILTER_DEPTH.fetch_add(1, Ordering::SeqCst);
4884        if depth > 0 {
4885            eprintln!(
4886                "WARNING: apply_filter re-entrancy detected! depth={}, pattern='{}', thread={:?}",
4887                depth,
4888                pattern,
4889                std::thread::current().id()
4890            );
4891        }
4892
4893        info!(
4894            "Applying filter: '{}' on thread {:?}",
4895            pattern,
4896            std::thread::current().id()
4897        );
4898
4899        // Delegate state coordination to StateCoordinator
4900        use crate::ui::state::state_coordinator::StateCoordinator;
4901        let _rows_after =
4902            StateCoordinator::apply_text_filter_with_refs(&mut self.state_container, pattern);
4903
4904        // Update ViewportManager with the filtered DataView
4905        // Sync the dataview to both managers
4906        self.sync_dataview_to_managers();
4907
4908        // Decrement re-entrancy counter
4909        FILTER_DEPTH.fetch_sub(1, Ordering::SeqCst);
4910    }
4911    fn search_columns(&mut self) {
4912        // Safety: Prevent infinite recursion with a static counter
4913        static SEARCH_DEPTH: std::sync::atomic::AtomicUsize =
4914            std::sync::atomic::AtomicUsize::new(0);
4915        let depth = SEARCH_DEPTH.fetch_add(1, std::sync::atomic::Ordering::SeqCst);
4916
4917        // Guard against excessive recursion
4918        if depth > 10 {
4919            error!(target: "search", "Column search depth exceeded limit, aborting to prevent infinite loop");
4920            SEARCH_DEPTH.store(0, std::sync::atomic::Ordering::SeqCst);
4921            return;
4922        }
4923
4924        // Create a guard that will decrement on drop
4925        struct DepthGuard;
4926        impl Drop for DepthGuard {
4927            fn drop(&mut self) {
4928                SEARCH_DEPTH.fetch_sub(1, std::sync::atomic::Ordering::SeqCst);
4929            }
4930        }
4931        let _guard = DepthGuard;
4932
4933        let pattern = self.state_container.column_search().pattern.clone();
4934        debug!(target: "search", "search_columns called with pattern: '{}', depth: {}", pattern, depth);
4935
4936        if pattern.is_empty() {
4937            debug!(target: "search", "Pattern is empty, skipping column search");
4938            return;
4939        }
4940
4941        // Update DataView's column search and get matches
4942        let matching_columns = if let Some(dataview) =
4943            self.state_container.get_buffer_dataview_mut()
4944        {
4945            dataview.search_columns(&pattern);
4946
4947            // Get matching columns from DataView
4948            let matches = dataview.get_matching_columns().to_vec();
4949            debug!(target: "search", "DataView found {} matching columns", matches.len());
4950            if !matches.is_empty() {
4951                for (idx, (col_idx, col_name)) in matches.iter().enumerate() {
4952                    debug!(target: "search", "  Match {}: '{}' at visual index {}", idx + 1, col_name, col_idx);
4953                }
4954            }
4955
4956            // Also sync with AppStateContainer for compatibility
4957            let columns: Vec<(String, usize)> = matches
4958                .iter()
4959                .map(|(idx, name)| (name.clone(), *idx))
4960                .collect();
4961            self.state_container
4962                .update_column_search_matches(&columns, &pattern);
4963
4964            matches
4965        } else {
4966            debug!(target: "search", "No DataView available for column search");
4967            Vec::new()
4968        };
4969
4970        if matching_columns.is_empty() {
4971            let status_msg = format!("No columns matching '{pattern}'");
4972            debug!(target: "search", "Setting status: {}", status_msg);
4973            self.state_container.set_status_message(status_msg);
4974        } else {
4975            // Move to first match - the index from DataView is already a VISUAL index
4976            let first_match_visual_idx = matching_columns[0].0;
4977            let first_match_name = &matching_columns[0].1;
4978
4979            // Convert visual index to DataTable index for Buffer/AppStateContainer (legacy compatibility)
4980            let datatable_idx = if let Some(dataview) = self.state_container.get_buffer_dataview() {
4981                let display_columns = dataview.get_display_columns();
4982                if first_match_visual_idx < display_columns.len() {
4983                    display_columns[first_match_visual_idx]
4984                } else {
4985                    first_match_visual_idx // Fallback
4986                }
4987            } else {
4988                first_match_visual_idx
4989            };
4990
4991            self.state_container.set_current_column(datatable_idx);
4992            self.state_container
4993                .set_current_column_buffer(datatable_idx);
4994
4995            // Update viewport to show the first match using ViewportManager
4996            // ViewportManager expects VISUAL index
4997            {
4998                let mut viewport_manager_borrow = self.viewport_manager.borrow_mut();
4999                if let Some(viewport_manager) = viewport_manager_borrow.as_mut() {
5000                    let viewport_changed =
5001                        viewport_manager.set_current_column(first_match_visual_idx);
5002
5003                    // Sync navigation state with updated viewport
5004                    if viewport_changed {
5005                        let new_viewport = viewport_manager.viewport_cols().clone();
5006                        let pinned_count =
5007                            if let Some(dv) = self.state_container.get_buffer_dataview() {
5008                                dv.get_pinned_columns().len()
5009                            } else {
5010                                0
5011                            };
5012                        let scrollable_offset = new_viewport.start.saturating_sub(pinned_count);
5013                        self.state_container.navigation_mut().scroll_offset.1 = scrollable_offset;
5014
5015                        debug!(target: "navigation",
5016                            "Column search initial: Jumped to column {} '{}', viewport adjusted to {:?}",
5017                            first_match_visual_idx, first_match_name, new_viewport);
5018                    }
5019                }
5020            }
5021
5022            debug!(target: "search", "Setting current column to visual index {} ('{}')",
5023                   first_match_visual_idx, first_match_name);
5024            let status_msg = format!(
5025                "Found {} columns matching '{}'. Tab/Shift-Tab to navigate.",
5026                matching_columns.len(),
5027                pattern
5028            );
5029            debug!(target: "search", "Setting status: {}", status_msg);
5030            self.state_container.set_status_message(status_msg);
5031
5032            // Column search matches are now managed by AppStateContainer
5033        }
5034
5035        // Matching columns are now stored in AppStateContainer
5036    }
5037
5038    fn next_column_match(&mut self) {
5039        // Use DataView's column search navigation
5040        // Extract all needed data first to avoid borrow conflicts
5041        let column_match_data =
5042            if let Some(dataview) = self.state_container.get_buffer_dataview_mut() {
5043                if let Some(visual_idx) = dataview.next_column_match() {
5044                    // Get the column name and match info
5045                    let matching_columns = dataview.get_matching_columns();
5046                    let current_match_index = dataview.current_column_match_index();
5047                    let current_match = current_match_index + 1;
5048                    let total_matches = matching_columns.len();
5049                    let col_name = matching_columns
5050                        .get(current_match_index)
5051                        .map(|(_, name)| name.clone())
5052                        .unwrap_or_default();
5053
5054                    // Convert visual index to DataTable index for Buffer/AppStateContainer
5055                    // (they still use DataTable indices for now)
5056                    let display_columns = dataview.get_display_columns();
5057                    let datatable_idx = if visual_idx < display_columns.len() {
5058                        display_columns[visual_idx]
5059                    } else {
5060                        visual_idx // Fallback
5061                    };
5062
5063                    Some((
5064                        visual_idx,
5065                        datatable_idx,
5066                        col_name,
5067                        current_match,
5068                        total_matches,
5069                        current_match_index,
5070                    ))
5071                } else {
5072                    None
5073                }
5074            } else {
5075                None
5076            };
5077
5078        // Now process the match data without holding dataview reference
5079        if let Some((
5080            visual_idx,
5081            datatable_idx,
5082            col_name,
5083            current_match,
5084            total_matches,
5085            current_match_index,
5086        )) = column_match_data
5087        {
5088            // Update both AppStateContainer and Buffer with DataTable index (for legacy compatibility)
5089            self.state_container.set_current_column(datatable_idx);
5090            self.state_container
5091                .set_current_column_buffer(datatable_idx);
5092
5093            // Update viewport to show the column using ViewportManager
5094            // ViewportManager's set_current_column now expects VISUAL index
5095            {
5096                let mut viewport_manager_borrow = self.viewport_manager.borrow_mut();
5097                if let Some(viewport_manager) = viewport_manager_borrow.as_mut() {
5098                    viewport_manager.set_current_column(visual_idx);
5099                }
5100            }
5101
5102            // Always sync navigation state with updated viewport (like vim search)
5103            debug!(target: "column_search_sync", "next_column_match: About to call sync_navigation_with_viewport() - visual_idx: {}, datatable_idx: {}", visual_idx, datatable_idx);
5104            debug!(target: "column_search_sync", "next_column_match: Pre-sync - viewport current_column: {}", 
5105                if let Ok(vm) = self.viewport_manager.try_borrow() {
5106                    vm.as_ref().map_or(0, super::viewport_manager::ViewportManager::get_crosshair_col)
5107                } else { 0 });
5108            self.sync_navigation_with_viewport();
5109            debug!(target: "column_search_sync", "next_column_match: Post-sync - navigation current_column: {}", 
5110                self.state_container.navigation().selected_column);
5111            debug!(target: "column_search_sync", "next_column_match: sync_navigation_with_viewport() completed");
5112
5113            debug!(target: "navigation",
5114                "Column search: Jumped to visual column {} (datatable: {}) '{}', synced with viewport",
5115                visual_idx, datatable_idx, col_name);
5116
5117            // CRITICAL: Update AppStateContainer's column_search.current_match
5118            // This ensures Enter key will jump to the correct column
5119            {
5120                let mut column_search = self.state_container.column_search_mut();
5121                column_search.current_match = current_match_index;
5122                debug!(target: "column_search_sync", "next_column_match: Updated AppStateContainer column_search.current_match to {}", column_search.current_match);
5123            }
5124
5125            self.state_container.set_status_message(format!(
5126                "Column {current_match}/{total_matches}: {col_name} - Tab/Shift-Tab to navigate"
5127            ));
5128        }
5129    }
5130
5131    fn previous_column_match(&mut self) {
5132        // Use DataView's column search navigation
5133        // Extract all needed data first to avoid borrow conflicts
5134        let column_match_data =
5135            if let Some(dataview) = self.state_container.get_buffer_dataview_mut() {
5136                if let Some(visual_idx) = dataview.prev_column_match() {
5137                    // Get the column name and match info
5138                    let matching_columns = dataview.get_matching_columns();
5139                    let current_match_index = dataview.current_column_match_index();
5140                    let current_match = current_match_index + 1;
5141                    let total_matches = matching_columns.len();
5142                    let col_name = matching_columns
5143                        .get(current_match_index)
5144                        .map(|(_, name)| name.clone())
5145                        .unwrap_or_default();
5146
5147                    // Convert visual index to DataTable index for Buffer/AppStateContainer
5148                    // (they still use DataTable indices for now)
5149                    let display_columns = dataview.get_display_columns();
5150                    let datatable_idx = if visual_idx < display_columns.len() {
5151                        display_columns[visual_idx]
5152                    } else {
5153                        visual_idx // Fallback
5154                    };
5155
5156                    Some((
5157                        visual_idx,
5158                        datatable_idx,
5159                        col_name,
5160                        current_match,
5161                        total_matches,
5162                        current_match_index,
5163                    ))
5164                } else {
5165                    None
5166                }
5167            } else {
5168                None
5169            };
5170
5171        // Now process the match data without holding dataview reference
5172        if let Some((
5173            visual_idx,
5174            datatable_idx,
5175            col_name,
5176            current_match,
5177            total_matches,
5178            current_match_index,
5179        )) = column_match_data
5180        {
5181            // Update both AppStateContainer and Buffer with DataTable index (for legacy compatibility)
5182            self.state_container.set_current_column(datatable_idx);
5183            self.state_container
5184                .set_current_column_buffer(datatable_idx);
5185
5186            // Update viewport to show the column using ViewportManager
5187            // ViewportManager's set_current_column now expects VISUAL index
5188            {
5189                let mut viewport_manager_borrow = self.viewport_manager.borrow_mut();
5190                if let Some(viewport_manager) = viewport_manager_borrow.as_mut() {
5191                    viewport_manager.set_current_column(visual_idx);
5192                }
5193            }
5194
5195            // Always sync navigation state with updated viewport (like vim search)
5196            debug!(target: "column_search_sync", "previous_column_match: About to call sync_navigation_with_viewport() - visual_idx: {}, datatable_idx: {}", visual_idx, datatable_idx);
5197            debug!(target: "column_search_sync", "previous_column_match: Pre-sync - viewport current_column: {}", 
5198                if let Ok(vm) = self.viewport_manager.try_borrow() {
5199                    vm.as_ref().map_or(0, super::viewport_manager::ViewportManager::get_crosshair_col)
5200                } else { 0 });
5201            self.sync_navigation_with_viewport();
5202            debug!(target: "column_search_sync", "previous_column_match: Post-sync - navigation current_column: {}", 
5203                self.state_container.navigation().selected_column);
5204            debug!(target: "column_search_sync", "previous_column_match: sync_navigation_with_viewport() completed");
5205
5206            debug!(target: "navigation",
5207                "Column search (prev): Jumped to visual column {} (datatable: {}) '{}', synced with viewport",
5208                visual_idx, datatable_idx, col_name);
5209
5210            // CRITICAL: Update AppStateContainer's column_search.current_match
5211            // This ensures Enter key will jump to the correct column
5212            {
5213                let mut column_search = self.state_container.column_search_mut();
5214                column_search.current_match = current_match_index;
5215                debug!(target: "column_search_sync", "previous_column_match: Updated AppStateContainer column_search.current_match to {}", column_search.current_match);
5216            }
5217
5218            self.state_container.set_status_message(format!(
5219                "Column {current_match}/{total_matches}: {col_name} - Tab/Shift-Tab to navigate"
5220            ));
5221        }
5222    }
5223
5224    fn apply_fuzzy_filter(&mut self) {
5225        info!(
5226            "apply_fuzzy_filter called on thread {:?}",
5227            std::thread::current().id()
5228        );
5229
5230        // Delegate all state coordination to StateCoordinator
5231        use crate::ui::state::state_coordinator::StateCoordinator;
5232        let (_match_count, indices) = StateCoordinator::apply_fuzzy_filter_with_refs(
5233            &mut self.state_container,
5234            &self.viewport_manager,
5235        );
5236
5237        // Update fuzzy filter indices for compatibility
5238        self.state_container.set_fuzzy_filter_indices(indices);
5239
5240        // Update ViewportManager with the filtered DataView
5241        // Sync the dataview to both managers
5242        self.sync_dataview_to_managers();
5243    }
5244
5245    fn toggle_sort_current_column(&mut self) {
5246        // Get visual column index from ViewportManager's crosshair
5247        let visual_col_idx = if let Some(ref viewport_manager) = *self.viewport_manager.borrow() {
5248            viewport_manager.get_crosshair_col()
5249        } else {
5250            0
5251        };
5252
5253        if let Some(dataview) = self.state_container.get_buffer_dataview_mut() {
5254            // DataView.toggle_sort expects VISIBLE column index
5255            // Get column name for display
5256            let column_names = dataview.column_names();
5257            let col_name = column_names
5258                .get(visual_col_idx)
5259                .cloned()
5260                .unwrap_or_else(|| format!("Column {visual_col_idx}"));
5261
5262            debug!(
5263                "toggle_sort_current_column: visual_idx={}, column_name={}",
5264                visual_col_idx, col_name
5265            );
5266
5267            if let Err(e) = dataview.toggle_sort(visual_col_idx) {
5268                self.state_container
5269                    .set_status_message(format!("Sort error: {e}"));
5270            } else {
5271                // Get the new sort state for status message
5272                let sort_state = dataview.get_sort_state();
5273                let message = match sort_state.order {
5274                    crate::data::data_view::SortOrder::Ascending => {
5275                        format!("Sorted '{col_name}' ascending ↑")
5276                    }
5277                    crate::data::data_view::SortOrder::Descending => {
5278                        format!("Sorted '{col_name}' descending ↓")
5279                    }
5280                    crate::data::data_view::SortOrder::None => {
5281                        format!("Cleared sort on '{col_name}'")
5282                    }
5283                };
5284                self.state_container.set_status_message(message);
5285
5286                // Update ViewportManager with the sorted DataView to keep them in sync
5287                if let Some(updated_dataview) = self.state_container.get_buffer_dataview() {
5288                    // Update TableWidgetManager with the sorted dataview as well
5289                    self.table_widget_manager
5290                        .borrow_mut()
5291                        .set_dataview(Arc::new(updated_dataview.clone()));
5292
5293                    let mut viewport_manager_borrow = self.viewport_manager.borrow_mut();
5294                    if let Some(ref mut viewport_manager) = *viewport_manager_borrow {
5295                        viewport_manager.set_dataview(Arc::new(updated_dataview.clone()));
5296                        debug!("Updated ViewportManager with sorted DataView");
5297                    }
5298                }
5299            }
5300        } else {
5301            // Could not find display position in DataTable
5302            self.state_container
5303                .set_status_message("Error: Invalid column position".to_string());
5304        }
5305    }
5306
5307    fn get_current_data(&self) -> Option<&DataView> {
5308        self.state_container.get_buffer_dataview()
5309    }
5310
5311    fn get_row_count(&self) -> usize {
5312        // Check if fuzzy filter is active first (most specific filter)
5313        if self.state_container.is_fuzzy_filter_active() {
5314            // Return the count of fuzzy filtered indices
5315            self.state_container.get_fuzzy_filter_indices().len()
5316        } else if let Some(dataview) = self.state_container.get_buffer_dataview() {
5317            // Return count from WHERE clause or other filters
5318            dataview.row_count()
5319        } else if let Some(provider) = self.get_data_provider() {
5320            // Use DataProvider trait for data access (migration step)
5321            provider.get_row_count()
5322        } else {
5323            0
5324        }
5325    }
5326
5327    /// Helper to sync dataview to both `ViewportManager` and `TableWidgetManager`
5328    fn sync_dataview_to_managers(&self) {
5329        if let Some(dataview) = self.state_container.get_buffer_dataview() {
5330            let arc_dataview = Arc::new(dataview.clone());
5331
5332            // Update ViewportManager
5333            if let Some(ref mut viewport_manager) = *self.viewport_manager.borrow_mut() {
5334                viewport_manager.set_dataview(arc_dataview.clone());
5335                debug!(
5336                    "Updated ViewportManager with DataView (row_count={})",
5337                    arc_dataview.row_count()
5338                );
5339            }
5340
5341            // Update TableWidgetManager
5342            self.table_widget_manager
5343                .borrow_mut()
5344                .set_dataview(arc_dataview);
5345            debug!("Updated TableWidgetManager with DataView");
5346        }
5347    }
5348
5349    pub fn reset_table_state(&mut self) {
5350        // Delegate all state reset coordination to StateCoordinator
5351        use crate::ui::state::state_coordinator::StateCoordinator;
5352        StateCoordinator::reset_table_state_with_refs(
5353            &mut self.state_container,
5354            &self.viewport_manager,
5355        );
5356    }
5357
5358    fn update_parser_for_current_buffer(&mut self) {
5359        // Sync input states
5360        self.sync_all_input_states();
5361
5362        // Delegate parser update to StateCoordinator
5363        use crate::ui::state::state_coordinator::StateCoordinator;
5364        StateCoordinator::update_parser_with_refs(&self.state_container, &mut self.hybrid_parser);
5365    }
5366
5367    /// Synchronize all state after buffer switch
5368    /// This should be called after any buffer switch operation to ensure:
5369    /// 1. Viewport is restored from the new buffer
5370    /// 2. Parser schema is updated with the new buffer's columns
5371    fn sync_after_buffer_switch(&mut self) {
5372        // Restore viewport state from new buffer
5373        self.restore_viewport_from_current_buffer();
5374
5375        // Update parser schema for the new buffer
5376        self.update_parser_for_current_buffer();
5377    }
5378
5379    /// Update `ViewportManager` when `DataView` changes
5380    fn update_viewport_manager(&mut self, dataview: Option<DataView>) {
5381        if let Some(dv) = dataview {
5382            // Get current column position to preserve it
5383            let current_column = self.state_container.get_current_column();
5384
5385            // Create new ViewportManager with the new DataView
5386            let mut new_viewport_manager = ViewportManager::new(Arc::new(dv));
5387
5388            // Update terminal size from current terminal
5389            if let Ok((width, height)) = crossterm::terminal::size() {
5390                // Calculate the actual data area height
5391                let data_rows_available = Self::calculate_available_data_rows(height);
5392                new_viewport_manager.update_terminal_size(width, data_rows_available);
5393                debug!(
5394                    "Updated new ViewportManager terminal size: {}x{} (data rows)",
5395                    width, data_rows_available
5396                );
5397            }
5398
5399            // Set the current column position to ensure proper viewport initialization
5400            // This is crucial for SELECT queries that subset columns
5401            if current_column < new_viewport_manager.dataview().column_count() {
5402                new_viewport_manager.set_current_column(current_column);
5403            } else {
5404                // If current column is out of bounds, reset to first column
5405                new_viewport_manager.set_current_column(0);
5406                self.state_container.set_current_column_buffer(0);
5407            }
5408
5409            *self.viewport_manager.borrow_mut() = Some(new_viewport_manager);
5410            debug!(
5411                "ViewportManager updated with new DataView, current_column={}",
5412                current_column
5413            );
5414        } else {
5415            // Clear ViewportManager if no DataView
5416            *self.viewport_manager.borrow_mut() = None;
5417            debug!("ViewportManager cleared (no DataView)");
5418        }
5419    }
5420
5421    pub fn calculate_optimal_column_widths(&mut self) {
5422        // Delegate to ViewportManager for optimal column width calculations
5423        let widths_from_viewport = {
5424            let mut viewport_opt = self.viewport_manager.borrow_mut();
5425            (*viewport_opt)
5426                .as_mut()
5427                .map(super::viewport_manager::ViewportManager::calculate_optimal_column_widths)
5428        };
5429
5430        if let Some(widths) = widths_from_viewport {
5431            self.state_container.set_column_widths(widths);
5432        }
5433    }
5434
5435    /// Centralized method for setting status messages
5436    /// Ensures consistent logging and state synchronization
5437    pub fn set_status_message(&mut self, message: impl Into<String>) {
5438        let msg = message.into();
5439        debug!("Status: {}", msg);
5440        self.state_container.set_status_message(msg.clone());
5441        // Future: Could also sync to state_container if needed
5442        // self.state_container.set_status(msg);
5443    }
5444
5445    /// Set error status message with consistent formatting
5446    fn set_error_status(&mut self, context: &str, error: impl std::fmt::Display) {
5447        let msg = format!("{context}: {error}");
5448        debug!("Error status: {}", msg);
5449        self.set_status_message(msg);
5450    }
5451
5452    fn export_to_csv(&mut self) {
5453        let result = {
5454            let ctx = crate::ui::operations::data_export_operations::DataExportContext {
5455                data_provider: self.get_data_provider(),
5456            };
5457            crate::ui::operations::data_export_operations::export_to_csv(&ctx)
5458        };
5459
5460        match result {
5461            crate::ui::operations::data_export_operations::ExportResult::Success(message) => {
5462                self.set_status_message(message);
5463            }
5464            crate::ui::operations::data_export_operations::ExportResult::Error(error) => {
5465                self.set_error_status("Export failed", error);
5466            }
5467        }
5468    }
5469
5470    // ========== YANK OPERATIONS ==========
5471
5472    // Yank operations are provided by the YankBehavior trait in traits/yank_ops.rs
5473    // The trait provides: yank_cell, yank_row, yank_column, yank_all, yank_query,
5474    // yank_as_test_case, and yank_debug_with_context
5475
5476    fn paste_from_clipboard(&mut self) {
5477        // Paste from system clipboard into the current input field
5478        match self.state_container.read_from_clipboard() {
5479            Ok(text) => {
5480                let mode = self.shadow_state.borrow().get_mode();
5481                match mode {
5482                    AppMode::Command => {
5483                        // Always use single-line mode paste
5484                        // Get current cursor position
5485                        let cursor_pos = self.get_input_cursor();
5486                        let current_value = self.get_input_text();
5487
5488                        // Insert at cursor position
5489                        let mut new_value = String::new();
5490                        new_value.push_str(&current_value[..cursor_pos]);
5491                        new_value.push_str(&text);
5492                        new_value.push_str(&current_value[cursor_pos..]);
5493
5494                        self.set_input_text_with_cursor(new_value, cursor_pos + text.len());
5495
5496                        self.state_container
5497                            .set_status_message(format!("Pasted {} characters", text.len()));
5498                    }
5499                    AppMode::Filter
5500                    | AppMode::FuzzyFilter
5501                    | AppMode::Search
5502                    | AppMode::ColumnSearch => {
5503                        // For search/filter modes, append to current pattern
5504                        let cursor_pos = self.get_input_cursor();
5505                        let current_value = self.get_input_text();
5506
5507                        let mut new_value = String::new();
5508                        new_value.push_str(&current_value[..cursor_pos]);
5509                        new_value.push_str(&text);
5510                        new_value.push_str(&current_value[cursor_pos..]);
5511
5512                        self.set_input_text_with_cursor(new_value, cursor_pos + text.len());
5513
5514                        // Update the appropriate filter/search state (reuse the mode we already have)
5515                        match mode {
5516                            AppMode::Filter => {
5517                                let pattern = self.get_input_text();
5518                                self.state_container.filter_mut().pattern = pattern.clone();
5519                                self.apply_filter(&pattern);
5520                            }
5521                            AppMode::FuzzyFilter => {
5522                                let input_text = self.get_input_text();
5523                                self.state_container.set_fuzzy_filter_pattern(input_text);
5524                                self.apply_fuzzy_filter();
5525                            }
5526                            AppMode::Search => {
5527                                let search_text = self.get_input_text();
5528                                self.state_container.set_search_pattern(search_text);
5529                                // TODO: self.search_in_results();
5530                            }
5531                            AppMode::ColumnSearch => {
5532                                let input_text = self.get_input_text();
5533                                self.state_container.start_column_search(input_text);
5534                                // Column search pattern is now in AppStateContainer
5535                            }
5536                            _ => {}
5537                        }
5538                    }
5539                    _ => {
5540                        self.state_container
5541                            .set_status_message("Paste not available in this mode".to_string());
5542                    }
5543                }
5544            }
5545            Err(e) => {
5546                self.state_container
5547                    .set_status_message(format!("Failed to paste: {e}"));
5548            }
5549        }
5550    }
5551
5552    fn export_to_json(&mut self) {
5553        // TODO: Handle filtered data in future DataView implementation
5554        let result = {
5555            let ctx = crate::ui::operations::data_export_operations::DataExportContext {
5556                data_provider: self.get_data_provider(),
5557            };
5558            crate::ui::operations::data_export_operations::export_to_json(&ctx)
5559        };
5560
5561        match result {
5562            crate::ui::operations::data_export_operations::ExportResult::Success(message) => {
5563                self.set_status_message(message);
5564            }
5565            crate::ui::operations::data_export_operations::ExportResult::Error(error) => {
5566                self.set_error_status("Export failed", error);
5567            }
5568        }
5569    }
5570
5571    fn get_horizontal_scroll_offset(&self) -> u16 {
5572        // Delegate to cursor_manager (incremental refactoring)
5573        let (horizontal, _vertical) = self.cursor_manager.scroll_offsets();
5574        horizontal
5575    }
5576
5577    fn update_horizontal_scroll(&mut self, terminal_width: u16) {
5578        let inner_width = terminal_width.saturating_sub(3) as usize; // Account for borders + 1 char padding
5579        let cursor_pos = self.get_input_cursor();
5580
5581        // Update cursor_manager scroll (incremental refactoring)
5582        self.cursor_manager
5583            .update_horizontal_scroll(cursor_pos, terminal_width.saturating_sub(3));
5584
5585        // Update scroll state in container
5586        let mut scroll = self.state_container.scroll_mut();
5587        if cursor_pos < scroll.input_scroll_offset as usize {
5588            scroll.input_scroll_offset = cursor_pos as u16;
5589        }
5590        // If cursor is after the scroll window, scroll right
5591        else if cursor_pos >= scroll.input_scroll_offset as usize + inner_width {
5592            scroll.input_scroll_offset = (cursor_pos + 1).saturating_sub(inner_width) as u16;
5593        }
5594    }
5595
5596    fn get_cursor_token_position(&self) -> (usize, usize) {
5597        let ctx = crate::ui::operations::simple_operations::TextNavigationContext {
5598            query: &self.get_input_text(),
5599            cursor_pos: self.get_input_cursor(),
5600        };
5601        crate::ui::operations::simple_operations::get_cursor_token_position(&ctx)
5602    }
5603
5604    fn get_token_at_cursor(&self) -> Option<String> {
5605        let ctx = crate::ui::operations::simple_operations::TextNavigationContext {
5606            query: &self.get_input_text(),
5607            cursor_pos: self.get_input_cursor(),
5608        };
5609        crate::ui::operations::simple_operations::get_token_at_cursor(&ctx)
5610    }
5611
5612    /// Debug method to dump current buffer state (disabled to prevent TUI corruption)
5613    #[allow(dead_code)]
5614    fn ui(&mut self, f: &mut Frame) {
5615        // Always use single-line mode input height
5616        let input_height = INPUT_AREA_HEIGHT;
5617
5618        // Always show tab bar for consistent layout
5619        let buffer_count = self.state_container.buffers().all_buffers().len();
5620        let tab_bar_height = 2; // Always reserve space for tab bar
5621
5622        let chunks = Layout::default()
5623            .direction(Direction::Vertical)
5624            .constraints(
5625                [
5626                    Constraint::Length(tab_bar_height),    // Tab bar (always shown)
5627                    Constraint::Length(input_height),      // Command input area
5628                    Constraint::Min(0),                    // Results
5629                    Constraint::Length(STATUS_BAR_HEIGHT), // Status bar
5630                ]
5631                .as_ref(),
5632            )
5633            .split(f.area());
5634
5635        // Always render tab bar (even with single buffer)
5636        if buffer_count > 0 {
5637            let buffer_names: Vec<String> = self
5638                .state_container
5639                .buffers()
5640                .all_buffers()
5641                .iter()
5642                .map(super::super::buffer::BufferAPI::get_name)
5643                .collect();
5644            let current_index = self.state_container.buffers().current_index();
5645
5646            let tab_widget = TabBarWidget::new(current_index, buffer_names);
5647            tab_widget.render(f, chunks[0]);
5648        }
5649
5650        // Fixed chunk indices since tab bar is always present
5651        let input_chunk_idx = 1;
5652        let results_chunk_idx = 2;
5653        let status_chunk_idx = 3;
5654
5655        // Update horizontal scroll based on actual terminal width
5656        self.update_horizontal_scroll(chunks[input_chunk_idx].width);
5657
5658        // Command input area
5659        // Get the current input text length and cursor position for display
5660        let input_text_for_count = self.get_input_text();
5661        let char_count = input_text_for_count.len();
5662        let cursor_pos = self.get_input_cursor();
5663        let char_count_display = if char_count > 0 {
5664            format!(" [{cursor_pos}/{char_count} chars]")
5665        } else {
5666            String::new()
5667        };
5668
5669        let scroll_offset = self.get_horizontal_scroll_offset();
5670        let scroll_indicator = if scroll_offset > 0 {
5671            " ◀ " // Indicate text is scrolled (text hidden to the left)
5672        } else {
5673            ""
5674        };
5675
5676        let input_title = match self.shadow_state.borrow().get_mode() {
5677            AppMode::Command => format!("SQL Query{char_count_display}{scroll_indicator}"),
5678            AppMode::Results => format!(
5679                "SQL Query (Results Mode - Press ↑ to edit){char_count_display}{scroll_indicator}"
5680            ),
5681            AppMode::Search => format!("Search Pattern{char_count_display}{scroll_indicator}"),
5682            AppMode::Filter => format!("Filter Pattern{char_count_display}{scroll_indicator}"),
5683            AppMode::FuzzyFilter => {
5684                format!("Fuzzy Filter{char_count_display}{scroll_indicator}")
5685            }
5686            AppMode::ColumnSearch => {
5687                format!("Column Search{char_count_display}{scroll_indicator}")
5688            }
5689            AppMode::Help => "Help".to_string(),
5690            AppMode::History => {
5691                let query = self.state_container.history_search().query.clone();
5692                format!("History Search: '{query}' (Esc to cancel)")
5693            }
5694            AppMode::Debug => "Parser Debug (F5)".to_string(),
5695            AppMode::PrettyQuery => "Pretty Query View (F6)".to_string(),
5696            AppMode::JumpToRow => format!("Jump to row: {}", self.get_jump_to_row_input()),
5697            AppMode::ColumnStats => "Column Statistics (S to close)".to_string(),
5698        };
5699
5700        let input_block = Block::default().borders(Borders::ALL).title(input_title);
5701
5702        // Check if we should use the search modes widget for rendering
5703        let use_search_widget = matches!(
5704            self.shadow_state.borrow().get_mode(),
5705            AppMode::Search | AppMode::Filter | AppMode::FuzzyFilter | AppMode::ColumnSearch
5706        ) && self.search_modes_widget.is_active();
5707
5708        if use_search_widget {
5709            // Let the search modes widget render the input field with debounce indicator
5710            self.search_modes_widget.render(f, chunks[input_chunk_idx]);
5711        } else {
5712            // Always get input text through the buffer API for consistency
5713            let input_text_string = self.get_input_text();
5714
5715            // Debug log to track rendering issues
5716            trace!(target: "render", "Rendering input: text='{}', mode={:?}, cursor={}",
5717                   if input_text_string.len() > 50 {
5718                       format!("{}...", &input_text_string[..50])
5719                   } else {
5720                       input_text_string.clone()
5721                   },
5722                   self.shadow_state.borrow().get_mode(),
5723                   self.get_input_cursor());
5724
5725            // Get history search query if in history mode
5726            let history_query_string = if self.shadow_state.borrow().is_in_history_mode() {
5727                self.state_container.history_search().query.clone()
5728            } else {
5729                String::new()
5730            };
5731
5732            let input_text = match self.shadow_state.borrow().get_mode() {
5733                AppMode::History => &history_query_string,
5734                _ => &input_text_string,
5735            };
5736
5737            let input_paragraph = match self.shadow_state.borrow().get_mode() {
5738                AppMode::Command => {
5739                    match self.state_container.get_edit_mode() {
5740                        Some(EditMode::SingleLine) => {
5741                            // Use syntax highlighting for SQL command input with horizontal scrolling
5742                            let highlighted_line =
5743                                self.sql_highlighter.simple_sql_highlight(input_text);
5744                            Paragraph::new(Text::from(vec![highlighted_line]))
5745                                .block(input_block)
5746                                .scroll((0, self.get_horizontal_scroll_offset()))
5747                        }
5748                        Some(EditMode::MultiLine) => {
5749                            // MultiLine mode is no longer supported, always use single-line
5750                            let highlighted_line =
5751                                self.sql_highlighter.simple_sql_highlight(input_text);
5752                            Paragraph::new(Text::from(vec![highlighted_line]))
5753                                .block(input_block)
5754                                .scroll((0, self.get_horizontal_scroll_offset()))
5755                        }
5756                        None => {
5757                            // Default to single-line mode
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                    }
5765                }
5766                _ => {
5767                    // Plain text for other modes
5768                    Paragraph::new(input_text.as_str())
5769                        .block(input_block)
5770                        .style(match self.shadow_state.borrow().get_mode() {
5771                            AppMode::Results => Style::default().fg(Color::DarkGray),
5772                            AppMode::Search => Style::default().fg(Color::Yellow),
5773                            AppMode::Filter => Style::default().fg(Color::Cyan),
5774                            AppMode::FuzzyFilter => Style::default().fg(Color::Magenta),
5775                            AppMode::ColumnSearch => Style::default().fg(Color::Green),
5776                            AppMode::Help => Style::default().fg(Color::DarkGray),
5777                            AppMode::History => Style::default().fg(Color::Magenta),
5778                            AppMode::Debug => Style::default().fg(Color::Yellow),
5779                            AppMode::PrettyQuery => Style::default().fg(Color::Green),
5780                            AppMode::JumpToRow => Style::default().fg(Color::Magenta),
5781                            AppMode::ColumnStats => Style::default().fg(Color::Cyan),
5782                            _ => Style::default(),
5783                        })
5784                        .scroll((0, self.get_horizontal_scroll_offset()))
5785                }
5786            };
5787
5788            // Render the input paragraph (single-line mode)
5789            f.render_widget(input_paragraph, chunks[input_chunk_idx]);
5790        }
5791        let results_area = chunks[results_chunk_idx];
5792
5793        // Set cursor position for input modes (skip if search widget is handling it)
5794        if !use_search_widget {
5795            match self.shadow_state.borrow().get_mode() {
5796                AppMode::Command => {
5797                    // Always use single-line cursor handling
5798                    // Calculate cursor position with horizontal scrolling
5799                    let inner_width = chunks[input_chunk_idx].width.saturating_sub(2) as usize;
5800                    let cursor_pos = self.get_visual_cursor().1; // Get column position for single-line
5801                    let scroll_offset = self.get_horizontal_scroll_offset() as usize;
5802
5803                    // Calculate visible cursor position
5804                    if cursor_pos >= scroll_offset && cursor_pos < scroll_offset + inner_width {
5805                        let visible_pos = cursor_pos - scroll_offset;
5806                        f.set_cursor_position((
5807                            chunks[input_chunk_idx].x + visible_pos as u16 + 1,
5808                            chunks[input_chunk_idx].y + 1,
5809                        ));
5810                    }
5811                }
5812                AppMode::Search => {
5813                    f.set_cursor_position((
5814                        chunks[input_chunk_idx].x + self.get_input_cursor() as u16 + 1,
5815                        chunks[input_chunk_idx].y + 1,
5816                    ));
5817                }
5818                AppMode::Filter => {
5819                    f.set_cursor_position((
5820                        chunks[input_chunk_idx].x + self.get_input_cursor() as u16 + 1,
5821                        chunks[input_chunk_idx].y + 1,
5822                    ));
5823                }
5824                AppMode::FuzzyFilter => {
5825                    f.set_cursor_position((
5826                        chunks[input_chunk_idx].x + self.get_input_cursor() as u16 + 1,
5827                        chunks[input_chunk_idx].y + 1,
5828                    ));
5829                }
5830                AppMode::ColumnSearch => {
5831                    f.set_cursor_position((
5832                        chunks[input_chunk_idx].x + self.get_input_cursor() as u16 + 1,
5833                        chunks[input_chunk_idx].y + 1,
5834                    ));
5835                }
5836                AppMode::JumpToRow => {
5837                    f.set_cursor_position((
5838                        chunks[input_chunk_idx].x + self.get_jump_to_row_input().len() as u16 + 1,
5839                        chunks[input_chunk_idx].y + 1,
5840                    ));
5841                }
5842                AppMode::History => {
5843                    let query_len = self.state_container.history_search().query.len();
5844                    f.set_cursor_position((
5845                        chunks[input_chunk_idx].x + query_len as u16 + 1,
5846                        chunks[input_chunk_idx].y + 1,
5847                    ));
5848                }
5849                _ => {}
5850            }
5851        }
5852
5853        // Results area - render based on mode to reduce complexity
5854        let mode = self.shadow_state.borrow().get_mode();
5855        match mode {
5856            AppMode::Help => self.render_help(f, results_area),
5857            AppMode::History => self.render_history(f, results_area),
5858            AppMode::Debug => self.render_debug(f, results_area),
5859            AppMode::PrettyQuery => self.render_pretty_query(f, results_area),
5860            AppMode::ColumnStats => self.render_column_stats(f, results_area),
5861            _ if self.state_container.has_dataview() => {
5862                // Calculate viewport using DataView
5863                // V50: Render using DataProvider which works with DataTable
5864                if let Some(provider) = self.get_data_provider() {
5865                    self.render_table_with_provider(f, results_area, provider.as_ref());
5866                }
5867            }
5868            _ => {
5869                // Simple placeholder - reduced text to improve rendering speed
5870                let placeholder = Paragraph::new("Enter SQL query and press Enter\n\nTip: Use Tab for completion, Ctrl+R for history")
5871                    .block(Block::default().borders(Borders::ALL).title("Results"))
5872                    .style(Style::default().fg(Color::DarkGray));
5873                f.render_widget(placeholder, results_area);
5874            }
5875        }
5876
5877        // Render mode-specific status line
5878        self.render_status_line(f, chunks[status_chunk_idx]);
5879        // ========== RENDERING ==========
5880    }
5881
5882    /// Add mode styling and indicator to status spans
5883    fn add_mode_styling(&self, spans: &mut Vec<Span>) -> (Style, Color) {
5884        // Determine the mode color
5885        let (status_style, mode_color) = match self.shadow_state.borrow().get_mode() {
5886            AppMode::Command => (Style::default().fg(Color::Green), Color::Green),
5887            AppMode::Results => (Style::default().fg(Color::Blue), Color::Blue),
5888            AppMode::Search => (Style::default().fg(Color::Yellow), Color::Yellow),
5889            AppMode::Filter => (Style::default().fg(Color::Cyan), Color::Cyan),
5890            AppMode::FuzzyFilter => (Style::default().fg(Color::Magenta), Color::Magenta),
5891            AppMode::ColumnSearch => (Style::default().fg(Color::Green), Color::Green),
5892            AppMode::Help => (Style::default().fg(Color::Magenta), Color::Magenta),
5893            AppMode::History => (Style::default().fg(Color::Magenta), Color::Magenta),
5894            AppMode::Debug => (Style::default().fg(Color::Yellow), Color::Yellow),
5895            AppMode::PrettyQuery => (Style::default().fg(Color::Green), Color::Green),
5896            AppMode::JumpToRow => (Style::default().fg(Color::Magenta), Color::Magenta),
5897            AppMode::ColumnStats => (Style::default().fg(Color::Cyan), Color::Cyan),
5898        };
5899
5900        let mode_indicator = match self.shadow_state.borrow().get_mode() {
5901            AppMode::Command => "CMD",
5902            AppMode::Results => "NAV",
5903            AppMode::Search => "SEARCH",
5904            AppMode::Filter => "FILTER",
5905            AppMode::FuzzyFilter => "FUZZY",
5906            AppMode::ColumnSearch => "COL",
5907            AppMode::Help => "HELP",
5908            AppMode::History => "HISTORY",
5909            AppMode::Debug => "DEBUG",
5910            AppMode::PrettyQuery => "PRETTY",
5911            AppMode::JumpToRow => "JUMP",
5912            AppMode::ColumnStats => "STATS",
5913        };
5914
5915        // Mode indicator with color
5916        spans.push(Span::styled(
5917            format!("[{mode_indicator}]"),
5918            Style::default().fg(mode_color).add_modifier(Modifier::BOLD),
5919        ));
5920
5921        (status_style, mode_color)
5922    }
5923
5924    /// Add data source display to status spans
5925    fn add_data_source_display(&self, _spans: &mut Vec<Span>) {
5926        // Skip showing data source in status line since tab bar shows file names
5927        // This avoids redundancy like "[trades.csv] [1/2] trades.csv"
5928    }
5929
5930    /// Add buffer information to status spans
5931    fn add_buffer_information(&self, spans: &mut Vec<Span>) {
5932        let index = self.state_container.buffers().current_index();
5933        let total = self.state_container.buffers().all_buffers().len();
5934
5935        // Show buffer indicator if multiple buffers
5936        if total > 1 {
5937            spans.push(Span::raw(" "));
5938            spans.push(Span::styled(
5939                format!("[{}/{}]", index + 1, total),
5940                Style::default().fg(Color::Yellow),
5941            ));
5942        }
5943
5944        // Show table name from current query (simplified)
5945        // Since tab bar shows file names, we just show the table being queried
5946        if let Some(buffer) = self.state_container.buffers().current() {
5947            let query = buffer.get_input_text();
5948            // Simple extraction of table name from "SELECT ... FROM table" pattern
5949            if let Some(from_pos) = query.to_uppercase().find(" FROM ") {
5950                let after_from = &query[from_pos + 6..];
5951                // Take the first word after FROM as the table name
5952                if let Some(table_name) = after_from.split_whitespace().next() {
5953                    // Clean up the table name (remove quotes, etc.)
5954                    let clean_name = table_name
5955                        .trim_matches('"')
5956                        .trim_matches('\'')
5957                        .trim_matches('`');
5958
5959                    spans.push(Span::raw(" "));
5960                    spans.push(Span::styled(
5961                        clean_name.to_string(),
5962                        Style::default().fg(Color::Cyan),
5963                    ));
5964                }
5965            }
5966        }
5967    }
5968
5969    /// Add mode-specific information to status spans
5970    fn add_mode_specific_info(&self, spans: &mut Vec<Span>, mode_color: Color, area: Rect) {
5971        match self.shadow_state.borrow().get_mode() {
5972            AppMode::Command => {
5973                // In command mode, show editing-related info
5974                if !self.get_input_text().trim().is_empty() {
5975                    let (token_pos, total_tokens) = self.get_cursor_token_position();
5976                    spans.push(Span::raw(" | "));
5977                    spans.push(Span::styled(
5978                        format!("Token {token_pos}/{total_tokens}"),
5979                        Style::default().fg(Color::DarkGray),
5980                    ));
5981
5982                    // Show current token if available
5983                    if let Some(token) = self.get_token_at_cursor() {
5984                        spans.push(Span::raw(" "));
5985                        spans.push(Span::styled(
5986                            format!("[{token}]"),
5987                            Style::default().fg(Color::Cyan),
5988                        ));
5989                    }
5990
5991                    // Check for parser errors
5992                    if let Some(error_msg) = self.check_parser_error(&self.get_input_text()) {
5993                        spans.push(Span::raw(" | "));
5994                        spans.push(Span::styled(
5995                            format!("{} {}", self.config.display.icons.warning, error_msg),
5996                            Style::default().fg(Color::Red).add_modifier(Modifier::BOLD),
5997                        ));
5998                    }
5999                }
6000            }
6001            AppMode::Results => {
6002                // Extract this separately due to its size
6003                self.add_results_mode_info(spans, area);
6004            }
6005            AppMode::Search | AppMode::Filter | AppMode::FuzzyFilter | AppMode::ColumnSearch => {
6006                // Show the pattern being typed - always use input for consistency
6007                let pattern = self.get_input_text();
6008                if !pattern.is_empty() {
6009                    spans.push(Span::raw(" | Pattern: "));
6010                    spans.push(Span::styled(pattern, Style::default().fg(mode_color)));
6011                }
6012            }
6013            _ => {}
6014        }
6015    }
6016
6017    /// Add Results mode specific information (restored critical navigation info)
6018    fn add_results_mode_info(&self, spans: &mut Vec<Span>, area: Rect) {
6019        let total_rows = self.get_row_count();
6020        if total_rows > 0 {
6021            // Get selected row directly from navigation state (0-indexed) and add 1 for display
6022            let selected = self.state_container.navigation().selected_row + 1;
6023            spans.push(Span::raw(" | "));
6024
6025            // Show selection mode
6026            let selection_mode = self.get_selection_mode();
6027            let mode_text = match selection_mode {
6028                SelectionMode::Cell => "CELL",
6029                SelectionMode::Row => "ROW",
6030                SelectionMode::Column => "COL",
6031            };
6032            spans.push(Span::styled(
6033                format!("[{mode_text}]"),
6034                Style::default()
6035                    .fg(Color::Cyan)
6036                    .add_modifier(Modifier::BOLD),
6037            ));
6038
6039            spans.push(Span::raw(" "));
6040            spans.push(Span::styled(
6041                format!("Row {selected}/{total_rows}"),
6042                Style::default().fg(Color::White),
6043            ));
6044
6045            // Add cursor coordinates (x,y) - column and row position
6046            // Use ViewportManager's visual column position (1-based for display)
6047            let visual_col_display =
6048                if let Some(ref viewport_manager) = *self.viewport_manager.borrow() {
6049                    viewport_manager.get_crosshair_col() + 1
6050                } else {
6051                    1
6052                };
6053            spans.push(Span::raw(" "));
6054            spans.push(Span::styled(
6055                format!("({visual_col_display},{selected})"),
6056                Style::default().fg(Color::DarkGray),
6057            ));
6058
6059            // Add actual terminal cursor position if we can calculate it
6060            if let Some(ref mut viewport_manager) = *self.viewport_manager.borrow_mut() {
6061                let available_width = area.width.saturating_sub(TABLE_BORDER_WIDTH);
6062                // Use ViewportManager's crosshair column position
6063                let visual_col = viewport_manager.get_crosshair_col();
6064                if let Some(x_pos) =
6065                    viewport_manager.get_column_x_position(visual_col, available_width)
6066                {
6067                    // Add 2 for left border and padding, add 3 for header rows
6068                    let terminal_x = x_pos + 2;
6069                    let terminal_y = (selected as u16)
6070                        .saturating_sub(self.state_container.get_scroll_offset().0 as u16)
6071                        + 3;
6072                    spans.push(Span::raw(" "));
6073                    spans.push(Span::styled(
6074                        format!("[{terminal_x}x{terminal_y}]"),
6075                        Style::default().fg(Color::DarkGray),
6076                    ));
6077                }
6078            }
6079
6080            // Column information
6081            if let Some(dataview) = self.state_container.get_buffer_dataview() {
6082                let headers = dataview.column_names();
6083
6084                // Get ViewportManager's crosshair position (visual coordinates)
6085                // and use it to get the correct column name
6086                let (visual_row, visual_col) =
6087                    if let Some(ref viewport_manager) = *self.viewport_manager.borrow() {
6088                        (
6089                            viewport_manager.get_crosshair_row(),
6090                            viewport_manager.get_crosshair_col(),
6091                        )
6092                    } else {
6093                        (0, 0)
6094                    };
6095
6096                // Use ViewportManager's visual column index to get the correct column name
6097                if visual_col < headers.len() {
6098                    spans.push(Span::raw(" | Col: "));
6099                    spans.push(Span::styled(
6100                        headers[visual_col].clone(),
6101                        Style::default().fg(Color::Cyan),
6102                    ));
6103
6104                    // Show ViewportManager's crosshair position and viewport size
6105                    let viewport_info =
6106                        if let Some(ref viewport_manager) = *self.viewport_manager.borrow() {
6107                            let viewport_rows = viewport_manager.get_viewport_rows();
6108                            let viewport_height = viewport_rows.end - viewport_rows.start;
6109                            format!("[V:{visual_row},{visual_col} @ {viewport_height}r]")
6110                        } else {
6111                            format!("[V:{visual_row},{visual_col}]")
6112                        };
6113                    spans.push(Span::raw(" "));
6114                    spans.push(Span::styled(
6115                        viewport_info,
6116                        Style::default().fg(Color::Magenta),
6117                    ));
6118                }
6119            }
6120        }
6121    }
6122
6123    fn render_status_line(&self, f: &mut Frame, area: Rect) {
6124        let mut spans = Vec::new();
6125
6126        // Add mode styling and indicator
6127        let (status_style, mode_color) = self.add_mode_styling(&mut spans);
6128
6129        // Add data source display
6130        self.add_data_source_display(&mut spans);
6131
6132        // Add buffer information
6133        self.add_buffer_information(&mut spans);
6134
6135        // Add mode-specific information
6136        self.add_mode_specific_info(&mut spans, mode_color, area);
6137
6138        // Add query source indicator
6139        self.add_query_source_indicator(&mut spans);
6140
6141        // Add case sensitivity indicator
6142        self.add_case_sensitivity_indicator(&mut spans);
6143
6144        // Add column packing mode indicator
6145        self.add_column_packing_indicator(&mut spans);
6146
6147        // Add status message
6148        self.add_status_message(&mut spans);
6149
6150        // Determine help text based on current mode
6151        let help_text = self.get_help_text_for_mode();
6152
6153        self.add_global_indicators(&mut spans);
6154
6155        // Add shadow state display
6156        self.add_shadow_state_display(&mut spans);
6157
6158        self.add_help_text_display(&mut spans, help_text, area);
6159
6160        let status_line = Line::from(spans);
6161        let status = Paragraph::new(status_line)
6162            .block(Block::default().borders(Borders::ALL))
6163            .style(status_style);
6164        f.render_widget(status, area);
6165    }
6166
6167    /// Build a `TableRenderContext` with all data needed for rendering
6168    /// This collects all the scattered data into a single struct
6169    fn build_table_context(
6170        &self,
6171        area: Rect,
6172        provider: &dyn DataProvider,
6173    ) -> crate::ui::rendering::table_render_context::TableRenderContext {
6174        use crate::ui::rendering::table_render_context::TableRenderContextBuilder;
6175
6176        let row_count = provider.get_row_count();
6177        let available_width = area.width.saturating_sub(TABLE_BORDER_WIDTH);
6178        // The area passed here is already the table area
6179        // The Table widget itself handles borders and header
6180        let available_height = area.height;
6181
6182        // Get headers from ViewportManager (single source of truth)
6183        let headers = {
6184            let viewport_manager = self.viewport_manager.borrow();
6185            let viewport_manager = viewport_manager
6186                .as_ref()
6187                .expect("ViewportManager must exist");
6188            viewport_manager.get_column_names_ordered()
6189        };
6190
6191        // Update ViewportManager with current terminal dimensions
6192        {
6193            let mut viewport_opt = self.viewport_manager.borrow_mut();
6194            if let Some(ref mut viewport_manager) = *viewport_opt {
6195                // Calculate data rows for the table area
6196                let data_rows = Self::calculate_table_data_rows(available_height);
6197                viewport_manager.update_terminal_size(available_width, data_rows);
6198                let _ = viewport_manager.get_column_widths(); // Trigger recalculation
6199            }
6200        }
6201
6202        // Get structured column information from ViewportManager
6203        let (pinned_visual_positions, crosshair_column_position, _) = {
6204            let mut viewport_manager_borrow = self.viewport_manager.borrow_mut();
6205            let viewport_manager = viewport_manager_borrow
6206                .as_mut()
6207                .expect("ViewportManager must exist for rendering");
6208            let info = viewport_manager.get_visible_columns_info(available_width);
6209
6210            // info.0 = visible_indices (all visible column source indices)
6211            // info.1 = pinned_visible (pinned column source indices)
6212            // info.2 = scrollable_visible (scrollable column source indices)
6213            let visible_indices = &info.0;
6214            let pinned_source_indices = &info.1;
6215
6216            // Convert pinned source indices to visual positions
6217            // The TableRenderContext expects visual positions (0-based positions in the visible array)
6218            let mut pinned_visual_positions = Vec::new();
6219            for &source_idx in pinned_source_indices {
6220                if let Some(visual_pos) = visible_indices.iter().position(|&x| x == source_idx) {
6221                    pinned_visual_positions.push(visual_pos);
6222                }
6223            }
6224
6225            // Get the crosshair's viewport-relative position for rendering
6226            // The viewport manager stores crosshair in absolute coordinates
6227            // but we need viewport-relative for rendering
6228            let crosshair_column_position =
6229                if let Some((_, col_pos)) = viewport_manager.get_crosshair_viewport_position() {
6230                    col_pos
6231                } else {
6232                    // Crosshair is outside viewport, default to 0
6233                    0
6234                };
6235
6236            let crosshair_visual = viewport_manager.get_crosshair_col();
6237
6238            (
6239                pinned_visual_positions,
6240                crosshair_column_position,
6241                crosshair_visual,
6242            )
6243        };
6244
6245        // Calculate row viewport
6246        let row_viewport_start = self
6247            .state_container
6248            .navigation()
6249            .scroll_offset
6250            .0
6251            .min(row_count.saturating_sub(1));
6252        let row_viewport_end = (row_viewport_start + available_height as usize).min(row_count);
6253        let visible_row_indices: Vec<usize> = (row_viewport_start..row_viewport_end).collect();
6254
6255        // Get the visual display data from ViewportManager
6256        let (column_headers, data_to_display, column_widths_visual) = {
6257            let mut viewport_manager_borrow = self.viewport_manager.borrow_mut();
6258            if let Some(ref mut viewport_manager) = *viewport_manager_borrow {
6259                viewport_manager.get_visual_display(available_width, &visible_row_indices)
6260            } else {
6261                // Fallback
6262                let visible_rows = provider
6263                    .get_visible_rows(row_viewport_start, row_viewport_end - row_viewport_start);
6264                let widths = vec![15u16; headers.len()];
6265                (headers.clone(), visible_rows, widths)
6266            }
6267        };
6268
6269        // Get sort state
6270        let sort_state = self
6271            .buffer()
6272            .get_dataview()
6273            .map(|dv| dv.get_sort_state().clone());
6274
6275        // Get filter info
6276        let fuzzy_filter_pattern = if self.state_container.is_fuzzy_filter_active() {
6277            let pattern = self.state_container.get_fuzzy_filter_pattern();
6278            if pattern.is_empty() {
6279                None
6280            } else {
6281                Some(pattern)
6282            }
6283        } else {
6284            None
6285        };
6286
6287        // Build the context
6288        let selected_row = self.state_container.navigation().selected_row;
6289        let selected_col = crosshair_column_position;
6290
6291        // Log what we're passing to the renderer
6292        trace!(target: "search", "Building TableRenderContext: selected_row={}, selected_col={}, mode={:?}",
6293               selected_row, selected_col, self.state_container.get_selection_mode());
6294
6295        TableRenderContextBuilder::new()
6296            .row_count(row_count)
6297            .visible_rows(visible_row_indices.clone(), data_to_display)
6298            .columns(column_headers, column_widths_visual)
6299            .pinned_columns(pinned_visual_positions)
6300            .selection(
6301                selected_row,
6302                selected_col,
6303                self.state_container.get_selection_mode(),
6304            )
6305            .row_viewport(row_viewport_start..row_viewport_end)
6306            .sort_state(sort_state)
6307            .display_options(
6308                self.state_container.is_show_row_numbers(),
6309                self.shadow_state.borrow().get_mode(),
6310            )
6311            .filter(
6312                fuzzy_filter_pattern,
6313                self.state_container.is_case_insensitive(),
6314            )
6315            .dimensions(available_width, available_height)
6316            .build()
6317    }
6318
6319    /// New trait-based table rendering method
6320    /// This uses `DataProvider` trait instead of directly accessing `QueryResponse`
6321    fn render_table_with_provider(&self, f: &mut Frame, area: Rect, provider: &dyn DataProvider) {
6322        // Build the context with all data needed for rendering
6323        let context = self.build_table_context(area, provider);
6324
6325        // Use the pure table renderer
6326        crate::ui::rendering::table_renderer::render_table(f, area, &context);
6327    }
6328
6329    fn render_help(&mut self, f: &mut Frame, area: Rect) {
6330        // Delegate to the help widget which handles tabs, scrolling, and sections
6331        self.help_widget.render(f, area);
6332    }
6333
6334    fn render_help_two_column(&self, f: &mut Frame, area: Rect) {
6335        // Create two-column layout
6336        let chunks = Layout::default()
6337            .direction(Direction::Horizontal)
6338            .constraints([Constraint::Percentage(50), Constraint::Percentage(50)])
6339            .split(area);
6340
6341        // Get help content from HelpText module
6342        let left_content = HelpText::left_column();
6343        let right_content = HelpText::right_column();
6344
6345        // Calculate visible area for scrolling
6346        let visible_height = area.height.saturating_sub(2) as usize; // Account for borders
6347        let left_total_lines = left_content.len();
6348        let right_total_lines = right_content.len();
6349        let max_lines = left_total_lines.max(right_total_lines);
6350
6351        // Apply scroll offset (from state container or local)
6352        let scroll_offset = { self.state_container.help_scroll_offset() as usize };
6353
6354        // Get visible portions with scrolling
6355        let left_visible: Vec<Line> = left_content
6356            .into_iter()
6357            .skip(scroll_offset)
6358            .take(visible_height)
6359            .collect();
6360
6361        let right_visible: Vec<Line> = right_content
6362            .into_iter()
6363            .skip(scroll_offset)
6364            .take(visible_height)
6365            .collect();
6366
6367        // Create scroll indicator in title
6368        let scroll_indicator = if max_lines > visible_height {
6369            format!(
6370                " (↓/↑ to scroll, {}/{})",
6371                scroll_offset + 1,
6372                max_lines.saturating_sub(visible_height) + 1
6373            )
6374        } else {
6375            String::new()
6376        };
6377
6378        // Render left column
6379        let left_paragraph = Paragraph::new(Text::from(left_visible))
6380            .block(
6381                Block::default()
6382                    .borders(Borders::ALL)
6383                    .title(format!("Help - Commands{scroll_indicator}")),
6384            )
6385            .style(Style::default());
6386
6387        // Render right column
6388        let right_paragraph = Paragraph::new(Text::from(right_visible))
6389            .block(
6390                Block::default()
6391                    .borders(Borders::ALL)
6392                    .title("Help - Navigation & Features"),
6393            )
6394            .style(Style::default());
6395
6396        f.render_widget(left_paragraph, chunks[0]);
6397        f.render_widget(right_paragraph, chunks[1]);
6398    }
6399
6400    fn render_debug(&self, f: &mut Frame, area: Rect) {
6401        <Self as DebugContext>::render_debug(self, f, area);
6402    }
6403
6404    fn render_pretty_query(&self, f: &mut Frame, area: Rect) {
6405        <Self as DebugContext>::render_pretty_query(self, f, area);
6406    }
6407
6408    fn render_history(&self, f: &mut Frame, area: Rect) {
6409        // Get history state from AppStateContainer
6410        let history_search = self.state_container.history_search();
6411        let matches_empty = history_search.matches.is_empty();
6412        let search_query_empty = history_search.query.is_empty();
6413
6414        if matches_empty {
6415            let no_history = if search_query_empty {
6416                "No command history found.\nExecute some queries to build history."
6417            } else {
6418                "No matches found for your search.\nTry a different search term."
6419            };
6420
6421            let placeholder = Paragraph::new(no_history)
6422                .block(
6423                    Block::default()
6424                        .borders(Borders::ALL)
6425                        .title("Command History"),
6426                )
6427                .style(Style::default().fg(Color::DarkGray));
6428            f.render_widget(placeholder, area);
6429            return;
6430        }
6431
6432        // Split the area to show selected command details
6433        let chunks = Layout::default()
6434            .direction(Direction::Vertical)
6435            .constraints([
6436                Constraint::Percentage(50), // History list - 50% of space
6437                Constraint::Percentage(50), // Selected command preview - 50% of space
6438            ])
6439            .split(area);
6440
6441        self.render_history_list(f, chunks[0]);
6442        self.render_selected_command_preview(f, chunks[1]);
6443    }
6444
6445    fn render_history_list(&self, f: &mut Frame, area: Rect) {
6446        // Get history data from AppStateContainer
6447        let history_search = self.state_container.history_search();
6448        let matches = history_search.matches.clone();
6449        let selected_index = history_search.selected_index;
6450        let match_count = matches.len();
6451
6452        // Create more compact history list - just show essential info
6453        let history_items: Vec<Line> = matches
6454            .iter()
6455            .enumerate()
6456            .map(|(i, history_match)| {
6457                let entry = &history_match.entry;
6458                let is_selected = i == selected_index;
6459
6460                let success_indicator = if entry.success { "✓" } else { "✗" };
6461                let time_ago = {
6462                    let elapsed = chrono::Utc::now() - entry.timestamp;
6463                    if elapsed.num_days() > 0 {
6464                        format!("{}d", elapsed.num_days())
6465                    } else if elapsed.num_hours() > 0 {
6466                        format!("{}h", elapsed.num_hours())
6467                    } else if elapsed.num_minutes() > 0 {
6468                        format!("{}m", elapsed.num_minutes())
6469                    } else {
6470                        "now".to_string()
6471                    }
6472                };
6473
6474                // Use more space for the command, less for metadata
6475                let terminal_width = area.width as usize;
6476                let metadata_space = 15; // Reduced metadata: " ✓ 2x 1h"
6477                let available_for_command = terminal_width.saturating_sub(metadata_space).max(50);
6478
6479                let command_text = if entry.command.len() > available_for_command {
6480                    format!(
6481                        "{}…",
6482                        &entry.command[..available_for_command.saturating_sub(1)]
6483                    )
6484                } else {
6485                    entry.command.clone()
6486                };
6487
6488                let line_text = format!(
6489                    "{} {} {} {}x {}",
6490                    if is_selected { "►" } else { " " },
6491                    command_text,
6492                    success_indicator,
6493                    entry.execution_count,
6494                    time_ago
6495                );
6496
6497                let mut style = Style::default();
6498                if is_selected {
6499                    style = style.bg(Color::DarkGray).add_modifier(Modifier::BOLD);
6500                }
6501                if !entry.success {
6502                    style = style.fg(Color::Red);
6503                }
6504
6505                // Highlight matching characters for fuzzy search
6506                if !history_match.indices.is_empty() && is_selected {
6507                    style = style.fg(Color::Yellow);
6508                }
6509
6510                Line::from(line_text).style(style)
6511            })
6512            .collect();
6513
6514        let history_paragraph = Paragraph::new(history_items)
6515            .block(Block::default().borders(Borders::ALL).title(format!(
6516                "History ({match_count} matches) - j/k to navigate, Enter to select"
6517            )))
6518            .wrap(ratatui::widgets::Wrap { trim: false });
6519
6520        f.render_widget(history_paragraph, area);
6521    }
6522
6523    fn render_selected_command_preview(&self, f: &mut Frame, area: Rect) {
6524        // Get the selected match from AppStateContainer
6525        let history_search = self.state_container.history_search();
6526        let selected_match = history_search
6527            .matches
6528            .get(history_search.selected_index)
6529            .cloned();
6530
6531        if let Some(selected_match) = selected_match {
6532            let entry = &selected_match.entry;
6533
6534            // Pretty format the SQL command - adjust compactness based on available space
6535            use crate::recursive_parser::format_sql_pretty_compact;
6536
6537            // Calculate how many columns we can fit per line
6538            let available_width = area.width.saturating_sub(6) as usize; // Account for indentation and borders
6539            let avg_col_width = 15; // Assume average column name is ~15 chars
6540            let cols_per_line = (available_width / avg_col_width).max(3).min(12); // Between 3-12 columns per line
6541
6542            let mut pretty_lines = format_sql_pretty_compact(&entry.command, cols_per_line);
6543
6544            // If too many lines for the area, use a more compact format
6545            let max_lines = area.height.saturating_sub(2) as usize; // Account for borders
6546            if pretty_lines.len() > max_lines && cols_per_line < 12 {
6547                // Try with more columns per line
6548                pretty_lines = format_sql_pretty_compact(&entry.command, 15);
6549            }
6550
6551            // Convert to Text with syntax highlighting
6552            let mut highlighted_lines = Vec::new();
6553            for line in pretty_lines {
6554                highlighted_lines.push(self.sql_highlighter.simple_sql_highlight(&line));
6555            }
6556
6557            let preview_text = Text::from(highlighted_lines);
6558
6559            let duration_text = entry
6560                .duration_ms
6561                .map_or_else(|| "?ms".to_string(), |d| format!("{d}ms"));
6562
6563            let success_text = if entry.success {
6564                "✓ Success"
6565            } else {
6566                "✗ Failed"
6567            };
6568
6569            let preview = Paragraph::new(preview_text)
6570                .block(Block::default().borders(Borders::ALL).title(format!(
6571                    "Pretty SQL Preview: {} | {} | Used {}x",
6572                    success_text, duration_text, entry.execution_count
6573                )))
6574                .scroll((0, 0)); // Allow scrolling if needed
6575
6576            f.render_widget(preview, area);
6577        } else {
6578            let empty_preview = Paragraph::new("No command selected")
6579                .block(Block::default().borders(Borders::ALL).title("Preview"))
6580                .style(Style::default().fg(Color::DarkGray));
6581            f.render_widget(empty_preview, area);
6582        }
6583    }
6584
6585    fn handle_column_stats_input(&mut self, key: crossterm::event::KeyEvent) -> Result<bool> {
6586        // Create context and delegate to extracted handler
6587        let mut ctx = crate::ui::input::input_handlers::StatsInputContext {
6588            buffer_manager: self.state_container.buffers_mut(),
6589            stats_widget: &mut self.stats_widget,
6590            shadow_state: &self.shadow_state,
6591        };
6592
6593        let result = crate::ui::input::input_handlers::handle_column_stats_input(&mut ctx, key)?;
6594
6595        // Check if mode changed to Results (happens when stats view is closed)
6596        if self.shadow_state.borrow().get_mode() == AppMode::Results {
6597            self.shadow_state
6598                .borrow_mut()
6599                .observe_mode_change(AppMode::Results, "column_stats_closed");
6600        }
6601
6602        Ok(result)
6603    }
6604
6605    fn handle_jump_to_row_input(&mut self, key: crossterm::event::KeyEvent) -> Result<bool> {
6606        match key.code {
6607            KeyCode::Enter => {
6608                // Use NavigationBehavior trait's complete_jump_to_row method
6609                let input = self.get_jump_to_row_input();
6610                self.complete_jump_to_row(&input);
6611            }
6612            _ => {
6613                // Use InputBehavior trait's process_jump_to_row_key for other keys
6614                self.process_jump_to_row_key(key);
6615            }
6616        }
6617        Ok(false)
6618    }
6619
6620    fn render_column_stats(&self, f: &mut Frame, area: Rect) {
6621        // Delegate to the stats widget
6622        self.stats_widget.render(
6623            f,
6624            area,
6625            self.state_container
6626                .current_buffer()
6627                .expect("Buffer should exist"),
6628        );
6629    }
6630
6631    // === Editor Widget Helper Methods ===
6632    // ========== QUERY EXECUTION ==========
6633
6634    // These methods handle the actions returned by the editor widget
6635
6636    fn handle_execute_query(&mut self) -> Result<bool> {
6637        use crate::ui::state::state_coordinator::StateCoordinator;
6638
6639        let query = self.get_input_text().trim().to_string();
6640        debug!(target: "action", "Executing query: {}", query);
6641
6642        // Use StateCoordinator to handle special commands and state changes
6643        let should_exit = StateCoordinator::handle_execute_query_with_refs(
6644            &mut self.state_container,
6645            &self.shadow_state,
6646            &query,
6647        )?;
6648
6649        if should_exit {
6650            return Ok(true);
6651        }
6652
6653        // If not a special command and not empty, execute the SQL query
6654        if !query.is_empty() && !query.starts_with(':') {
6655            if let Err(e) = self.execute_query_v2(&query) {
6656                self.state_container
6657                    .set_status_message(format!("Error executing query: {e}"));
6658            }
6659            // Don't clear input - preserve query for editing
6660        }
6661
6662        Ok(false) // Continue running, don't exit
6663    }
6664
6665    fn handle_buffer_action(&mut self, action: BufferAction) -> Result<bool> {
6666        match action {
6667            BufferAction::NextBuffer => {
6668                let message = self
6669                    .buffer_handler
6670                    .next_buffer(self.state_container.buffers_mut());
6671                debug!("{}", message);
6672                // Sync all state after buffer switch
6673                self.sync_after_buffer_switch();
6674                Ok(false)
6675            }
6676            BufferAction::PreviousBuffer => {
6677                let message = self
6678                    .buffer_handler
6679                    .previous_buffer(self.state_container.buffers_mut());
6680                debug!("{}", message);
6681                // Sync all state after buffer switch
6682                self.sync_after_buffer_switch();
6683                Ok(false)
6684            }
6685            BufferAction::QuickSwitch => {
6686                let message = self
6687                    .buffer_handler
6688                    .quick_switch(self.state_container.buffers_mut());
6689                debug!("{}", message);
6690                // Sync all state after buffer switch
6691                self.sync_after_buffer_switch();
6692                Ok(false)
6693            }
6694            BufferAction::NewBuffer => {
6695                let message = self
6696                    .buffer_handler
6697                    .new_buffer(self.state_container.buffers_mut(), &self.config);
6698                debug!("{}", message);
6699                Ok(false)
6700            }
6701            BufferAction::CloseBuffer => {
6702                let (success, message) = self
6703                    .buffer_handler
6704                    .close_buffer(self.state_container.buffers_mut());
6705                debug!("{}", message);
6706                Ok(!success) // Exit if we couldn't close (only one left)
6707            }
6708            BufferAction::ListBuffers => {
6709                let buffer_list = self
6710                    .buffer_handler
6711                    .list_buffers(self.state_container.buffers());
6712                // For now, just log the list - later we can show a popup
6713                for line in &buffer_list {
6714                    debug!("{}", line);
6715                }
6716                Ok(false)
6717            }
6718            BufferAction::SwitchToBuffer(buffer_index) => {
6719                let message = self
6720                    .buffer_handler
6721                    .switch_to_buffer(self.state_container.buffers_mut(), buffer_index);
6722                debug!("{}", message);
6723
6724                // Sync all state after buffer switch
6725                self.sync_after_buffer_switch();
6726
6727                Ok(false)
6728            }
6729        }
6730    }
6731
6732    fn handle_expand_asterisk(&mut self) -> Result<bool> {
6733        if let Some(buffer) = self.state_container.buffers_mut().current_mut() {
6734            if buffer.expand_asterisk(&self.hybrid_parser) {
6735                // Sync for rendering if needed
6736                if buffer.get_edit_mode() == EditMode::SingleLine {
6737                    let text = buffer.get_input_text();
6738                    let cursor = buffer.get_input_cursor_position();
6739                    self.set_input_text_with_cursor(text, cursor);
6740                }
6741            }
6742        }
6743        Ok(false)
6744    }
6745
6746    pub(crate) fn toggle_debug_mode(&mut self) {
6747        // Use the DebugContext trait which has all the logic
6748        DebugContext::toggle_debug_mode(self);
6749    }
6750
6751    // ==================== Debug Helper Methods ====================
6752    // These are kept in the TUI to avoid regressions from moving data access
6753
6754    /// Generate the parser debug section
6755    pub(crate) fn debug_generate_parser_info(&self, query: &str) -> String {
6756        self.hybrid_parser
6757            .get_detailed_debug_info(query, query.len())
6758    }
6759
6760    fn debug_generate_navigation_state(&self) -> String {
6761        let mut debug_info = String::new();
6762        debug_info.push_str("\n========== NAVIGATION DEBUG ==========\n");
6763        let current_column = self.state_container.get_current_column();
6764        let scroll_offset = self.state_container.get_scroll_offset();
6765        let nav_state = self.state_container.navigation();
6766
6767        debug_info.push_str(&format!("Buffer Column Position: {current_column}\n"));
6768        debug_info.push_str(&format!(
6769            "Buffer Scroll Offset: row={}, col={}\n",
6770            scroll_offset.0, scroll_offset.1
6771        ));
6772        debug_info.push_str(&format!(
6773            "NavigationState Column: {}\n",
6774            nav_state.selected_column
6775        ));
6776        debug_info.push_str(&format!(
6777            "NavigationState Row: {:?}\n",
6778            nav_state.selected_row
6779        ));
6780        debug_info.push_str(&format!(
6781            "NavigationState Scroll Offset: row={}, col={}\n",
6782            nav_state.scroll_offset.0, nav_state.scroll_offset.1
6783        ));
6784
6785        // Show if synchronization is correct
6786        if current_column != nav_state.selected_column {
6787            debug_info.push_str(&format!(
6788                "⚠️  WARNING: Column mismatch! Buffer={}, Nav={}\n",
6789                current_column, nav_state.selected_column
6790            ));
6791        }
6792        if scroll_offset.1 != nav_state.scroll_offset.1 {
6793            debug_info.push_str(&format!(
6794                "⚠️  WARNING: Scroll column mismatch! Buffer={}, Nav={}\n",
6795                scroll_offset.1, nav_state.scroll_offset.1
6796            ));
6797        }
6798
6799        debug_info.push_str("\n--- Navigation Flow ---\n");
6800        debug_info.push_str(
6801            "(Enable RUST_LOG=sql_cli::ui::viewport_manager=debug,navigation=debug to see flow)\n",
6802        );
6803
6804        // Show pinned column info for navigation context
6805        if let Some(dataview) = self.state_container.get_buffer_dataview() {
6806            let pinned_count = dataview.get_pinned_columns().len();
6807            let pinned_names = dataview.get_pinned_column_names();
6808            debug_info.push_str(&format!("Pinned Column Count: {pinned_count}\n"));
6809            if !pinned_names.is_empty() {
6810                debug_info.push_str(&format!("Pinned Column Names: {pinned_names:?}\n"));
6811            }
6812            debug_info.push_str(&format!("First Scrollable Column: {pinned_count}\n"));
6813
6814            // Show if current column is in pinned or scrollable area
6815            if current_column < pinned_count {
6816                debug_info.push_str(&format!(
6817                    "Current Position: PINNED area (column {current_column})\n"
6818                ));
6819            } else {
6820                debug_info.push_str(&format!(
6821                    "Current Position: SCROLLABLE area (column {}, scrollable index {})\n",
6822                    current_column,
6823                    current_column - pinned_count
6824                ));
6825            }
6826
6827            // Show display column order
6828            let display_columns = dataview.get_display_columns();
6829            debug_info.push_str("\n--- COLUMN ORDERING ---\n");
6830            debug_info.push_str(&format!(
6831                "Display column order (first 10): {:?}\n",
6832                &display_columns[..display_columns.len().min(10)]
6833            ));
6834            if display_columns.len() > 10 {
6835                debug_info.push_str(&format!(
6836                    "... and {} more columns\n",
6837                    display_columns.len() - 10
6838                ));
6839            }
6840
6841            // Find current column in display order
6842            if let Some(display_idx) = display_columns
6843                .iter()
6844                .position(|&idx| idx == current_column)
6845            {
6846                debug_info.push_str(&format!(
6847                    "Current column {} is at display index {}/{}\n",
6848                    current_column,
6849                    display_idx,
6850                    display_columns.len()
6851                ));
6852
6853                // Show what happens on next move
6854                if display_idx + 1 < display_columns.len() {
6855                    let next_col = display_columns[display_idx + 1];
6856                    debug_info.push_str(&format!(
6857                        "Next 'l' press should move to column {} (display index {})\n",
6858                        next_col,
6859                        display_idx + 1
6860                    ));
6861                } else {
6862                    debug_info.push_str("Next 'l' press should wrap to first column\n");
6863                }
6864            } else {
6865                debug_info.push_str(&format!(
6866                    "WARNING: Current column {current_column} not found in display order!\n"
6867                ));
6868            }
6869        }
6870        debug_info.push_str("==========================================\n");
6871        debug_info
6872    }
6873
6874    fn debug_generate_column_search_state(&self) -> String {
6875        let mut debug_info = String::new();
6876        let show_column_search = self.shadow_state.borrow().get_mode() == AppMode::ColumnSearch
6877            || !self.state_container.column_search().pattern.is_empty();
6878        if show_column_search {
6879            let column_search = self.state_container.column_search();
6880            debug_info.push_str("\n========== COLUMN SEARCH STATE ==========\n");
6881            debug_info.push_str(&format!("Pattern: '{}'\n", column_search.pattern));
6882            debug_info.push_str(&format!(
6883                "Matching Columns: {} found\n",
6884                column_search.matching_columns.len()
6885            ));
6886            if !column_search.matching_columns.is_empty() {
6887                debug_info.push_str("Matches:\n");
6888                for (idx, (col_idx, col_name)) in column_search.matching_columns.iter().enumerate()
6889                {
6890                    let marker = if idx == column_search.current_match {
6891                        " <--"
6892                    } else {
6893                        ""
6894                    };
6895                    debug_info
6896                        .push_str(&format!("  [{idx}] {col_name} (index {col_idx}){marker}\n"));
6897                }
6898            }
6899            debug_info.push_str(&format!(
6900                "Current Match Index: {}\n",
6901                column_search.current_match
6902            ));
6903            debug_info.push_str(&format!(
6904                "Current Column: {}\n",
6905                self.state_container.get_current_column()
6906            ));
6907            debug_info.push_str("==========================================\n");
6908        }
6909        debug_info
6910    }
6911
6912    pub(crate) fn debug_generate_trace_logs(&self) -> String {
6913        let mut debug_info = String::from("\n========== TRACE LOGS ==========\n");
6914        debug_info.push_str("(Most recent at bottom, last 100 entries)\n");
6915
6916        if let Some(ref log_buffer) = self.log_buffer {
6917            let recent_logs = log_buffer.get_recent(100);
6918            for entry in recent_logs {
6919                debug_info.push_str(&entry.format_for_display());
6920                debug_info.push('\n');
6921            }
6922            debug_info.push_str(&format!("Total log entries: {}\n", log_buffer.len()));
6923        } else {
6924            debug_info.push_str("Log buffer not initialized\n");
6925        }
6926        debug_info.push_str("================================\n");
6927
6928        debug_info
6929    }
6930
6931    /// Generate the state change logs debug section
6932    pub(crate) fn debug_generate_state_logs(&self) -> String {
6933        let mut debug_info = String::new();
6934
6935        if let Some(ref debug_service) = self.debug_service {
6936            debug_info.push_str("\n========== STATE CHANGE LOGS ==========\n");
6937            debug_info.push_str("(Most recent at bottom, from DebugService)\n");
6938            let debug_entries = debug_service.get_entries();
6939            let recent = debug_entries.iter().rev().take(50).rev();
6940            for entry in recent {
6941                debug_info.push_str(&format!(
6942                    "[{}] {:?} [{}]: {}\n",
6943                    entry.timestamp, entry.level, entry.component, entry.message
6944                ));
6945            }
6946            debug_info.push_str(&format!(
6947                "Total state change entries: {}\n",
6948                debug_entries.len()
6949            ));
6950            debug_info.push_str("================================\n");
6951        } else {
6952            debug_info.push_str("\n========== STATE CHANGE LOGS ==========\n");
6953            debug_info.push_str("DebugService not available (service_container is None)\n");
6954            debug_info.push_str("================================\n");
6955        }
6956
6957        debug_info
6958    }
6959
6960    /// Extract time in milliseconds from a timing string
6961    pub(crate) fn debug_extract_timing(&self, s: &str) -> Option<f64> {
6962        crate::ui::rendering::ui_layout_utils::extract_timing_from_debug_string(s)
6963    }
6964
6965    fn show_pretty_query(&mut self) {
6966        if let Some(buffer) = self.state_container.buffers_mut().current_mut() {
6967            self.shadow_state.borrow_mut().set_mode(
6968                AppMode::PrettyQuery,
6969                buffer,
6970                "pretty_query_show",
6971            );
6972            let query = buffer.get_input_text();
6973            self.debug_widget.generate_pretty_sql(&query);
6974        }
6975    }
6976
6977    /// Add global indicators like key sequence display
6978    fn add_global_indicators(&self, spans: &mut Vec<Span>) {
6979        if self.key_sequence_renderer.has_content() {
6980            let key_display = self.key_sequence_renderer.get_display();
6981            if !key_display.is_empty() {
6982                spans.push(Span::raw(" | Keys: "));
6983                spans.push(Span::styled(
6984                    key_display,
6985                    Style::default()
6986                        .fg(Color::Cyan)
6987                        .add_modifier(Modifier::ITALIC),
6988                ));
6989            }
6990        }
6991    }
6992
6993    fn add_query_source_indicator(&self, spans: &mut Vec<Span>) {
6994        if let Some(source) = self.state_container.get_last_query_source() {
6995            spans.push(Span::raw(" | "));
6996            let (icon, label, color) = match source.as_str() {
6997                "cache" => (
6998                    &self.config.display.icons.cache,
6999                    "CACHE".to_string(),
7000                    Color::Cyan,
7001                ),
7002                "file" | "FileDataSource" => (
7003                    &self.config.display.icons.file,
7004                    "FILE".to_string(),
7005                    Color::Green,
7006                ),
7007                "SqlServerDataSource" => (
7008                    &self.config.display.icons.database,
7009                    "SQL".to_string(),
7010                    Color::Blue,
7011                ),
7012                "PublicApiDataSource" => (
7013                    &self.config.display.icons.api,
7014                    "API".to_string(),
7015                    Color::Yellow,
7016                ),
7017                _ => (
7018                    &self.config.display.icons.api,
7019                    source.clone(),
7020                    Color::Magenta,
7021                ),
7022            };
7023            spans.push(Span::raw(format!("{icon} ")));
7024            spans.push(Span::styled(label, Style::default().fg(color)));
7025        }
7026    }
7027
7028    fn add_case_sensitivity_indicator(&self, spans: &mut Vec<Span>) {
7029        let case_insensitive = self.state_container.is_case_insensitive();
7030        if case_insensitive {
7031            spans.push(Span::raw(" | "));
7032            let icon = self.config.display.icons.case_insensitive.clone();
7033            spans.push(Span::styled(
7034                format!("{icon} CASE"),
7035                Style::default().fg(Color::Cyan),
7036            ));
7037        }
7038    }
7039
7040    fn add_column_packing_indicator(&self, spans: &mut Vec<Span>) {
7041        if let Some(ref viewport_manager) = *self.viewport_manager.borrow() {
7042            let packing_mode = viewport_manager.get_packing_mode();
7043            spans.push(Span::raw(" | "));
7044            let (text, color) = match packing_mode {
7045                ColumnPackingMode::DataFocus => ("DATA", Color::Cyan),
7046                ColumnPackingMode::HeaderFocus => ("HEADER", Color::Yellow),
7047                ColumnPackingMode::Balanced => ("BALANCED", Color::Green),
7048            };
7049            spans.push(Span::styled(text, Style::default().fg(color)));
7050        }
7051    }
7052
7053    fn add_status_message(&self, spans: &mut Vec<Span>) {
7054        let status_msg = self.state_container.get_buffer_status_message();
7055        if !status_msg.is_empty() {
7056            spans.push(Span::raw(" | "));
7057            spans.push(Span::styled(
7058                status_msg,
7059                Style::default()
7060                    .fg(Color::Yellow)
7061                    .add_modifier(Modifier::BOLD),
7062            ));
7063        }
7064    }
7065
7066    fn get_help_text_for_mode(&self) -> &str {
7067        match self.shadow_state.borrow().get_mode() {
7068            AppMode::Command => "Enter:Run | Tab:Complete | ↓:Results | F1:Help",
7069            AppMode::Results => match self.get_selection_mode() {
7070                SelectionMode::Cell => "v:Row mode | y:Yank cell | ↑:Edit | F1:Help",
7071                SelectionMode::Row => "v:Cell mode | y:Yank | f:Filter | ↑:Edit | F1:Help",
7072                SelectionMode::Column => "v:Cell mode | y:Yank col | ↑:Edit | F1:Help",
7073            },
7074            AppMode::Search | AppMode::Filter | AppMode::FuzzyFilter | AppMode::ColumnSearch => {
7075                "Enter:Apply | Esc:Cancel"
7076            }
7077            AppMode::Help | AppMode::Debug | AppMode::PrettyQuery | AppMode::ColumnStats => {
7078                "Esc:Close"
7079            }
7080            AppMode::History => "Enter:Select | Esc:Cancel",
7081            AppMode::JumpToRow => "Enter:Jump | Esc:Cancel",
7082        }
7083    }
7084
7085    fn add_shadow_state_display(&self, spans: &mut Vec<Span>) {
7086        let shadow_display = self.shadow_state.borrow().status_display();
7087        spans.push(Span::raw(" "));
7088        spans.push(Span::styled(
7089            shadow_display,
7090            Style::default().fg(Color::Cyan),
7091        ));
7092    }
7093
7094    /// Add right-aligned help text if space allows
7095    fn add_help_text_display<'a>(&self, spans: &mut Vec<Span<'a>>, help_text: &'a str, area: Rect) {
7096        let current_length: usize = spans.iter().map(|s| s.content.len()).sum();
7097        let available_width = area.width.saturating_sub(TABLE_BORDER_WIDTH) as usize;
7098        let help_length = help_text.len();
7099
7100        if current_length + help_length + 3 < available_width {
7101            let padding = available_width - current_length - help_length - 3;
7102            spans.push(Span::raw(" ".repeat(padding)));
7103            spans.push(Span::raw(" | "));
7104            spans.push(Span::styled(
7105                help_text,
7106                Style::default().fg(Color::DarkGray),
7107            ));
7108        }
7109    }
7110}
7111
7112// Implement ActionHandlerContext trait for EnhancedTuiApp
7113impl ActionHandlerContext for EnhancedTuiApp {
7114    // Navigation methods - delegate to trait implementations
7115    fn previous_row(&mut self) {
7116        <Self as NavigationBehavior>::previous_row(self);
7117
7118        // Update TableWidgetManager for rendering
7119        let current_row = self.state_container.navigation().selected_row;
7120        let current_col = self.state_container.navigation().selected_column;
7121        self.table_widget_manager
7122            .borrow_mut()
7123            .navigate_to(current_row, current_col);
7124        info!(target: "navigation", "previous_row: Updated TableWidgetManager to ({}, {})", current_row, current_col);
7125    }
7126
7127    fn next_row(&mut self) {
7128        info!(target: "navigation", "next_row called - calling NavigationBehavior::next_row");
7129        <Self as NavigationBehavior>::next_row(self);
7130
7131        // Update TableWidgetManager for rendering
7132        let current_row = self.state_container.navigation().selected_row;
7133        let current_col = self.state_container.navigation().selected_column;
7134        self.table_widget_manager
7135            .borrow_mut()
7136            .navigate_to(current_row, current_col);
7137        info!(target: "navigation", "next_row: Updated TableWidgetManager to ({}, {})", current_row, current_col);
7138    }
7139
7140    fn move_column_left(&mut self) {
7141        <Self as ColumnBehavior>::move_column_left(self);
7142
7143        // Update TableWidgetManager for rendering
7144        let current_row = self.state_container.navigation().selected_row;
7145        let current_col = self.state_container.navigation().selected_column;
7146        self.table_widget_manager
7147            .borrow_mut()
7148            .navigate_to(current_row, current_col);
7149        info!(target: "navigation", "move_column_left: Updated TableWidgetManager to ({}, {})", current_row, current_col);
7150    }
7151
7152    fn move_column_right(&mut self) {
7153        <Self as ColumnBehavior>::move_column_right(self);
7154
7155        // Update TableWidgetManager for rendering
7156        let current_row = self.state_container.navigation().selected_row;
7157        let current_col = self.state_container.navigation().selected_column;
7158        self.table_widget_manager
7159            .borrow_mut()
7160            .navigate_to(current_row, current_col);
7161        info!(target: "navigation", "move_column_right: Updated TableWidgetManager to ({}, {})", current_row, current_col);
7162    }
7163
7164    fn page_up(&mut self) {
7165        <Self as NavigationBehavior>::page_up(self);
7166    }
7167
7168    fn page_down(&mut self) {
7169        <Self as NavigationBehavior>::page_down(self);
7170    }
7171
7172    fn goto_first_row(&mut self) {
7173        use crate::ui::state::state_coordinator::StateCoordinator;
7174
7175        // Always perform the normal goto first row behavior
7176        <Self as NavigationBehavior>::goto_first_row(self);
7177
7178        // Use StateCoordinator to handle vim search coordination
7179        StateCoordinator::goto_first_row_with_refs(
7180            &mut self.state_container,
7181            Some(&self.vim_search_adapter),
7182            Some(&self.viewport_manager),
7183        );
7184    }
7185
7186    fn goto_last_row(&mut self) {
7187        use crate::ui::state::state_coordinator::StateCoordinator;
7188
7189        // Perform the normal goto last row behavior
7190        <Self as NavigationBehavior>::goto_last_row(self);
7191
7192        // Use StateCoordinator for additional coordination
7193        StateCoordinator::goto_last_row_with_refs(&mut self.state_container);
7194    }
7195
7196    fn goto_first_column(&mut self) {
7197        <Self as ColumnBehavior>::goto_first_column(self);
7198    }
7199
7200    fn goto_last_column(&mut self) {
7201        <Self as ColumnBehavior>::goto_last_column(self);
7202    }
7203
7204    fn goto_row(&mut self, row: usize) {
7205        use crate::ui::state::state_coordinator::StateCoordinator;
7206
7207        // Perform the normal goto line behavior
7208        <Self as NavigationBehavior>::goto_line(self, row + 1); // Convert to 1-indexed
7209
7210        // Use StateCoordinator for additional coordination
7211        StateCoordinator::goto_row_with_refs(&mut self.state_container, row);
7212    }
7213
7214    fn goto_column(&mut self, col: usize) {
7215        // For now, implement basic column navigation
7216        // TODO: Implement proper goto_column functionality
7217        let current_col = self.state_container.get_current_column();
7218        if col < current_col {
7219            for _ in 0..(current_col - col) {
7220                <Self as ColumnBehavior>::move_column_left(self);
7221            }
7222        } else if col > current_col {
7223            for _ in 0..(col - current_col) {
7224                <Self as ColumnBehavior>::move_column_right(self);
7225            }
7226        }
7227    }
7228
7229    // Mode and UI state
7230    fn set_mode(&mut self, mode: AppMode) {
7231        // Use the proper synchronization method that updates both buffer and shadow_state
7232        self.set_mode_via_shadow_state(mode, "action_handler");
7233    }
7234
7235    fn get_mode(&self) -> AppMode {
7236        self.shadow_state.borrow().get_mode()
7237    }
7238
7239    fn set_status_message(&mut self, message: String) {
7240        self.state_container.set_status_message(message);
7241    }
7242
7243    // Column operations - delegate to trait implementations
7244    fn toggle_column_pin(&mut self) {
7245        // Call the existing toggle_column_pin implementation directly
7246        self.toggle_column_pin_impl();
7247    }
7248
7249    fn hide_current_column(&mut self) {
7250        <Self as ColumnBehavior>::hide_current_column(self);
7251    }
7252
7253    fn unhide_all_columns(&mut self) {
7254        <Self as ColumnBehavior>::unhide_all_columns(self);
7255    }
7256
7257    fn clear_all_pinned_columns(&mut self) {
7258        // Call the existing clear_all_pinned_columns implementation directly
7259        self.clear_all_pinned_columns_impl();
7260    }
7261
7262    // Export operations
7263    fn export_to_csv(&mut self) {
7264        // For now, just set a status message - actual implementation will be added later
7265        self.state_container
7266            .set_status_message("CSV export not yet implemented".to_string());
7267    }
7268
7269    fn export_to_json(&mut self) {
7270        // For now, just set a status message - actual implementation will be added later
7271        self.state_container
7272            .set_status_message("JSON export not yet implemented".to_string());
7273    }
7274
7275    // Yank operations - delegate to trait implementations
7276    fn yank_cell(&mut self) {
7277        YankBehavior::yank_cell(self);
7278    }
7279
7280    fn yank_row(&mut self) {
7281        YankBehavior::yank_row(self);
7282    }
7283
7284    fn yank_column(&mut self) {
7285        YankBehavior::yank_column(self);
7286    }
7287
7288    fn yank_all(&mut self) {
7289        YankBehavior::yank_all(self);
7290    }
7291
7292    fn yank_query(&mut self) {
7293        YankBehavior::yank_query(self);
7294    }
7295
7296    // Toggle operations
7297    fn toggle_selection_mode(&mut self) {
7298        self.state_container.toggle_selection_mode();
7299        let new_mode = self.state_container.get_selection_mode();
7300        let msg = match new_mode {
7301            SelectionMode::Cell => "Cell mode - Navigate to select individual cells",
7302            SelectionMode::Row => "Row mode - Navigate to select rows",
7303            SelectionMode::Column => "Column mode - Navigate to select columns",
7304        };
7305        self.state_container.set_status_message(msg.to_string());
7306    }
7307
7308    fn toggle_row_numbers(&mut self) {
7309        let current = self.state_container.is_show_row_numbers();
7310        self.state_container.set_show_row_numbers(!current);
7311        let message = if current {
7312            "Row numbers: OFF".to_string()
7313        } else {
7314            "Row numbers: ON (showing line numbers)".to_string()
7315        };
7316        self.state_container.set_status_message(message);
7317        // Recalculate column widths with new mode
7318        self.calculate_optimal_column_widths();
7319    }
7320
7321    fn toggle_compact_mode(&mut self) {
7322        let current_mode = self.state_container.is_compact_mode();
7323        self.state_container.set_compact_mode(!current_mode);
7324        let message = if current_mode {
7325            "Compact mode disabled"
7326        } else {
7327            "Compact mode enabled"
7328        };
7329        self.state_container.set_status_message(message.to_string());
7330    }
7331
7332    fn toggle_case_insensitive(&mut self) {
7333        let current = self.state_container.is_case_insensitive();
7334        self.state_container.set_case_insensitive(!current);
7335        self.state_container.set_status_message(format!(
7336            "Case-insensitive string comparisons: {}",
7337            if current { "OFF" } else { "ON" }
7338        ));
7339    }
7340
7341    fn toggle_key_indicator(&mut self) {
7342        let enabled = !self.key_indicator.enabled;
7343        self.key_indicator.set_enabled(enabled);
7344        self.key_sequence_renderer.set_enabled(enabled);
7345        self.state_container.set_status_message(format!(
7346            "Key press indicator {}",
7347            if enabled { "enabled" } else { "disabled" }
7348        ));
7349    }
7350
7351    // Clear operations
7352    fn clear_filter(&mut self) {
7353        // Check if we have an active filter to clear
7354        if let Some(dataview) = self.state_container.get_buffer_dataview() {
7355            if dataview.has_filter() {
7356                // Clear the filter
7357                if let Some(dataview_mut) = self.state_container.get_buffer_dataview_mut() {
7358                    dataview_mut.clear_filter();
7359                    self.state_container
7360                        .set_status_message("Filter cleared".to_string());
7361                }
7362
7363                // Update ViewportManager after clearing filter
7364                // Sync the dataview to both managers
7365                self.sync_dataview_to_managers();
7366            } else {
7367                self.state_container
7368                    .set_status_message("No active filter to clear".to_string());
7369            }
7370        } else {
7371            self.state_container
7372                .set_status_message("No data loaded".to_string());
7373        }
7374    }
7375
7376    fn clear_line(&mut self) {
7377        self.state_container.clear_line();
7378    }
7379
7380    // Mode operations
7381    fn start_search(&mut self) {
7382        self.start_vim_search();
7383    }
7384
7385    fn start_column_search(&mut self) {
7386        self.enter_search_mode(SearchMode::ColumnSearch);
7387    }
7388
7389    fn start_filter(&mut self) {
7390        self.enter_search_mode(SearchMode::Filter);
7391    }
7392
7393    fn start_fuzzy_filter(&mut self) {
7394        self.enter_search_mode(SearchMode::FuzzyFilter);
7395    }
7396
7397    fn exit_current_mode(&mut self) {
7398        // Handle escape based on current mode.
7399        // All transitions must go through set_mode_via_shadow_state so that
7400        // shadow_state and the buffer mode stay in sync. Updating only
7401        // state_container.set_mode(...) leaves shadow_state stale, which
7402        // causes rendering (which reads shadow_state) to diverge from key
7403        // dispatch (which reads state_container) — e.g. grey input box while
7404        // j/k get appended to the query.
7405        let mode = self.shadow_state.borrow().get_mode();
7406        match mode {
7407            AppMode::Results => {
7408                // VimSearchAdapter now handles Escape in Results mode when search is active
7409                // If we get here, it means search wasn't active, so switch to Command mode
7410                self.set_mode_via_shadow_state(AppMode::Command, "escape_from_results");
7411            }
7412            AppMode::Command => {
7413                self.set_mode_via_shadow_state(AppMode::Results, "escape_from_command");
7414            }
7415            AppMode::Help => {
7416                self.set_mode_via_shadow_state(AppMode::Results, "escape_from_help");
7417            }
7418            AppMode::JumpToRow => {
7419                self.set_mode_via_shadow_state(AppMode::Results, "escape_from_jump_to_row");
7420                <Self as InputBehavior>::clear_jump_to_row_input(self);
7421                // Clear jump-to-row state (can mutate directly now)
7422                self.state_container.jump_to_row_mut().is_active = false;
7423                self.state_container
7424                    .set_status_message("Jump to row cancelled".to_string());
7425            }
7426            _ => {
7427                // For any other mode, return to Results
7428                self.set_mode_via_shadow_state(AppMode::Results, "escape_to_results");
7429            }
7430        }
7431    }
7432
7433    fn toggle_debug_mode(&mut self) {
7434        // Use the DebugContext trait
7435        <Self as DebugContext>::toggle_debug_mode(self);
7436    }
7437
7438    // Column arrangement operations
7439    fn move_current_column_left(&mut self) {
7440        <Self as ColumnBehavior>::move_current_column_left(self);
7441    }
7442
7443    fn move_current_column_right(&mut self) {
7444        <Self as ColumnBehavior>::move_current_column_right(self);
7445    }
7446
7447    // Search navigation
7448    fn next_search_match(&mut self) {
7449        self.vim_search_next();
7450    }
7451
7452    fn previous_search_match(&mut self) {
7453        if self.vim_search_adapter.borrow().is_active()
7454            || self.vim_search_adapter.borrow().get_pattern().is_some()
7455        {
7456            self.vim_search_previous();
7457        }
7458    }
7459
7460    // Statistics and display
7461    fn show_column_statistics(&mut self) {
7462        self.calculate_column_statistics();
7463    }
7464
7465    fn cycle_column_packing(&mut self) {
7466        let message = {
7467            let mut viewport_manager_borrow = self.viewport_manager.borrow_mut();
7468            let viewport_manager = viewport_manager_borrow
7469                .as_mut()
7470                .expect("ViewportManager must exist");
7471            let new_mode = viewport_manager.cycle_packing_mode();
7472            format!("Column packing: {}", new_mode.display_name())
7473        };
7474        self.state_container.set_status_message(message);
7475    }
7476
7477    // Viewport navigation
7478    fn navigate_to_viewport_top(&mut self) {
7479        let result = {
7480            let mut viewport_manager_borrow = self.viewport_manager.borrow_mut();
7481            (*viewport_manager_borrow)
7482                .as_mut()
7483                .map(super::viewport_manager::ViewportManager::navigate_to_viewport_top)
7484        };
7485
7486        if let Some(result) = result {
7487            // ViewportManager has updated, sync NavigationState from it
7488            self.sync_navigation_with_viewport();
7489
7490            // Update buffer's selected row
7491            self.state_container
7492                .set_selected_row(Some(result.row_position));
7493
7494            // Update buffer's scroll offset
7495            if result.viewport_changed {
7496                let scroll_offset = self.state_container.navigation().scroll_offset;
7497                self.state_container.set_scroll_offset(scroll_offset);
7498            }
7499        }
7500    }
7501
7502    fn navigate_to_viewport_middle(&mut self) {
7503        let result = {
7504            let mut viewport_manager_borrow = self.viewport_manager.borrow_mut();
7505            (*viewport_manager_borrow)
7506                .as_mut()
7507                .map(super::viewport_manager::ViewportManager::navigate_to_viewport_middle)
7508        };
7509
7510        if let Some(result) = result {
7511            // ViewportManager has updated, sync NavigationState from it
7512            self.sync_navigation_with_viewport();
7513
7514            // Update buffer's selected row
7515            self.state_container
7516                .set_selected_row(Some(result.row_position));
7517
7518            // Update buffer's scroll offset
7519            if result.viewport_changed {
7520                let scroll_offset = self.state_container.navigation().scroll_offset;
7521                self.state_container.set_scroll_offset(scroll_offset);
7522            }
7523        }
7524    }
7525
7526    fn navigate_to_viewport_bottom(&mut self) {
7527        let result = {
7528            let mut viewport_manager_borrow = self.viewport_manager.borrow_mut();
7529            (*viewport_manager_borrow)
7530                .as_mut()
7531                .map(super::viewport_manager::ViewportManager::navigate_to_viewport_bottom)
7532        };
7533
7534        if let Some(result) = result {
7535            // ViewportManager has updated, sync NavigationState from it
7536            self.sync_navigation_with_viewport();
7537
7538            // Update buffer's selected row
7539            self.state_container
7540                .set_selected_row(Some(result.row_position));
7541
7542            // Update buffer's scroll offset
7543            if result.viewport_changed {
7544                let scroll_offset = self.state_container.navigation().scroll_offset;
7545                self.state_container.set_scroll_offset(scroll_offset);
7546            }
7547        }
7548    }
7549
7550    // Input and text editing methods
7551    fn move_input_cursor_left(&mut self) {
7552        self.state_container.move_input_cursor_left();
7553    }
7554
7555    fn move_input_cursor_right(&mut self) {
7556        self.state_container.move_input_cursor_right();
7557    }
7558
7559    fn move_input_cursor_home(&mut self) {
7560        self.state_container.set_input_cursor_position(0);
7561    }
7562
7563    fn move_input_cursor_end(&mut self) {
7564        let text_len = self.state_container.get_input_text().chars().count();
7565        self.state_container.set_input_cursor_position(text_len);
7566    }
7567
7568    fn backspace(&mut self) {
7569        self.state_container.backspace();
7570    }
7571
7572    fn delete(&mut self) {
7573        self.state_container.delete();
7574    }
7575
7576    fn undo(&mut self) {
7577        self.state_container.perform_undo();
7578    }
7579
7580    fn redo(&mut self) {
7581        self.state_container.perform_redo();
7582    }
7583
7584    fn start_jump_to_row(&mut self) {
7585        self.state_container.set_mode(AppMode::JumpToRow);
7586        self.shadow_state
7587            .borrow_mut()
7588            .observe_mode_change(AppMode::JumpToRow, "jump_to_row_requested");
7589        <Self as InputBehavior>::clear_jump_to_row_input(self);
7590
7591        // Set jump-to-row state as active (can mutate directly now)
7592        self.state_container.jump_to_row_mut().is_active = true;
7593
7594        self.state_container
7595            .set_status_message("Enter row number (1-based):".to_string());
7596    }
7597
7598    fn clear_jump_to_row_input(&mut self) {
7599        <Self as InputBehavior>::clear_jump_to_row_input(self);
7600    }
7601
7602    fn toggle_cursor_lock(&mut self) {
7603        // Toggle cursor lock in ViewportManager
7604        let is_locked = {
7605            let mut viewport_manager_borrow = self.viewport_manager.borrow_mut();
7606            if let Some(ref mut viewport_manager) = *viewport_manager_borrow {
7607                viewport_manager.toggle_cursor_lock();
7608                Some(viewport_manager.is_cursor_locked())
7609            } else {
7610                None
7611            }
7612        };
7613
7614        if let Some(is_locked) = is_locked {
7615            let msg = if is_locked {
7616                "Cursor lock ON - cursor stays in viewport position while scrolling"
7617            } else {
7618                "Cursor lock OFF"
7619            };
7620            self.state_container.set_status_message(msg.to_string());
7621
7622            // Log for shadow state learning (not tracking as state change yet)
7623            info!(target: "shadow_state",
7624                "Cursor lock toggled: {} (in {:?} mode)",
7625                if is_locked { "ON" } else { "OFF" },
7626                self.shadow_state.borrow().get_mode()
7627            );
7628        }
7629    }
7630
7631    fn toggle_viewport_lock(&mut self) {
7632        // Toggle viewport lock in ViewportManager
7633        let is_locked = {
7634            let mut viewport_manager_borrow = self.viewport_manager.borrow_mut();
7635            if let Some(ref mut viewport_manager) = *viewport_manager_borrow {
7636                viewport_manager.toggle_viewport_lock();
7637                Some(viewport_manager.is_viewport_locked())
7638            } else {
7639                None
7640            }
7641        };
7642
7643        if let Some(is_locked) = is_locked {
7644            let msg = if is_locked {
7645                "Viewport lock ON - navigation constrained to current viewport"
7646            } else {
7647                "Viewport lock OFF"
7648            };
7649            self.state_container.set_status_message(msg.to_string());
7650
7651            // Log for shadow state learning (not tracking as state change yet)
7652            info!(target: "shadow_state",
7653                "Viewport lock toggled: {} (in {:?} mode)",
7654                if is_locked { "ON" } else { "OFF" },
7655                self.shadow_state.borrow().get_mode()
7656            );
7657        }
7658    }
7659
7660    // Debug and development operations
7661    fn show_debug_info(&mut self) {
7662        <Self as DebugContext>::toggle_debug_mode(self);
7663    }
7664
7665    fn show_pretty_query(&mut self) {
7666        self.show_pretty_query();
7667    }
7668
7669    fn show_help(&mut self) {
7670        self.state_container.set_help_visible(true);
7671        self.set_mode_via_shadow_state(AppMode::Help, "help_requested");
7672        self.help_widget.on_enter();
7673    }
7674
7675    // Text editing operations
7676    fn kill_line(&mut self) {
7677        use crate::ui::traits::input_ops::InputBehavior;
7678        InputBehavior::kill_line(self);
7679        let message = if self.state_container.is_kill_ring_empty() {
7680            "Kill line - nothing to kill".to_string()
7681        } else {
7682            let kill_ring = self.state_container.get_kill_ring();
7683            format!(
7684                "Killed to end of line - {} chars in kill ring",
7685                kill_ring.len()
7686            )
7687        };
7688        self.state_container.set_status_message(message);
7689    }
7690
7691    fn kill_line_backward(&mut self) {
7692        use crate::ui::traits::input_ops::InputBehavior;
7693        InputBehavior::kill_line_backward(self);
7694        let message = if self.state_container.is_kill_ring_empty() {
7695            "Kill line backward - nothing to kill".to_string()
7696        } else {
7697            let kill_ring = self.state_container.get_kill_ring();
7698            format!(
7699                "Killed to beginning of line - {} chars in kill ring",
7700                kill_ring.len()
7701            )
7702        };
7703        self.state_container.set_status_message(message);
7704    }
7705
7706    fn delete_word_backward(&mut self) {
7707        use crate::ui::traits::input_ops::InputBehavior;
7708        InputBehavior::delete_word_backward(self);
7709    }
7710
7711    fn delete_word_forward(&mut self) {
7712        use crate::ui::traits::input_ops::InputBehavior;
7713        InputBehavior::delete_word_forward(self);
7714    }
7715
7716    fn jump_to_prev_token(&mut self) {
7717        use crate::ui::traits::input_ops::InputBehavior;
7718        InputBehavior::jump_to_prev_token(self);
7719    }
7720
7721    fn jump_to_next_token(&mut self) {
7722        use crate::ui::traits::input_ops::InputBehavior;
7723        InputBehavior::jump_to_next_token(self);
7724    }
7725
7726    fn expand_asterisk(&mut self) {
7727        if let Some(buffer) = self.state_container.buffers_mut().current_mut() {
7728            if buffer.expand_asterisk(&self.hybrid_parser) {
7729                // Sync for rendering if needed
7730                if buffer.get_edit_mode() == EditMode::SingleLine {
7731                    let text = buffer.get_input_text();
7732                    let cursor = buffer.get_input_cursor_position();
7733                    self.set_input_text_with_cursor(text, cursor);
7734                }
7735            }
7736        }
7737    }
7738
7739    fn expand_asterisk_visible(&mut self) {
7740        if let Some(buffer) = self.state_container.buffers_mut().current_mut() {
7741            if buffer.expand_asterisk_visible() {
7742                // Sync for rendering if needed
7743                if buffer.get_edit_mode() == EditMode::SingleLine {
7744                    let text = buffer.get_input_text();
7745                    let cursor = buffer.get_input_cursor_position();
7746                    self.set_input_text_with_cursor(text, cursor);
7747                }
7748            }
7749        }
7750    }
7751
7752    fn previous_history_command(&mut self) {
7753        let history_entries = self
7754            .state_container
7755            .command_history()
7756            .get_navigation_entries();
7757        let history_commands: Vec<String> =
7758            history_entries.iter().map(|e| e.command.clone()).collect();
7759
7760        if let Some(buffer) = self.state_container.buffers_mut().current_mut() {
7761            if buffer.navigate_history_up(&history_commands) {
7762                self.sync_all_input_states();
7763                self.state_container
7764                    .set_status_message("Previous command from history".to_string());
7765            }
7766        }
7767    }
7768
7769    fn next_history_command(&mut self) {
7770        let history_entries = self
7771            .state_container
7772            .command_history()
7773            .get_navigation_entries();
7774        let history_commands: Vec<String> =
7775            history_entries.iter().map(|e| e.command.clone()).collect();
7776
7777        if let Some(buffer) = self.state_container.buffers_mut().current_mut() {
7778            if buffer.navigate_history_down(&history_commands) {
7779                self.sync_all_input_states();
7780                self.state_container
7781                    .set_status_message("Next command from history".to_string());
7782            }
7783        }
7784    }
7785}
7786
7787// Implement NavigationBehavior trait for EnhancedTuiApp
7788impl NavigationBehavior for EnhancedTuiApp {
7789    fn viewport_manager(&self) -> &RefCell<Option<ViewportManager>> {
7790        &self.viewport_manager
7791    }
7792
7793    fn buffer_mut(&mut self) -> &mut dyn BufferAPI {
7794        self.state_container
7795            .current_buffer_mut()
7796            .expect("Buffer should exist")
7797    }
7798
7799    fn buffer(&self) -> &dyn BufferAPI {
7800        self.state_container
7801            .current_buffer()
7802            .expect("Buffer should exist")
7803    }
7804
7805    fn state_container(&self) -> &AppStateContainer {
7806        &self.state_container
7807    }
7808
7809    fn state_container_mut(&mut self) -> &mut AppStateContainer {
7810        &mut self.state_container
7811    }
7812
7813    fn get_row_count(&self) -> usize {
7814        self.get_row_count()
7815    }
7816
7817    fn set_mode_with_sync(&mut self, mode: AppMode, trigger: &str) {
7818        // Use the existing method that properly synchronizes both buffer and shadow_state
7819        self.set_mode_via_shadow_state(mode, trigger);
7820    }
7821}
7822
7823// Implement ColumnBehavior trait for EnhancedTuiApp
7824impl ColumnBehavior for EnhancedTuiApp {
7825    fn viewport_manager(&self) -> &RefCell<Option<ViewportManager>> {
7826        &self.viewport_manager
7827    }
7828
7829    fn buffer_mut(&mut self) -> &mut dyn BufferAPI {
7830        self.state_container
7831            .current_buffer_mut()
7832            .expect("Buffer should exist")
7833    }
7834
7835    fn buffer(&self) -> &dyn BufferAPI {
7836        self.state_container
7837            .current_buffer()
7838            .expect("Buffer should exist")
7839    }
7840
7841    fn state_container(&self) -> &AppStateContainer {
7842        &self.state_container
7843    }
7844
7845    fn is_in_results_mode(&self) -> bool {
7846        self.shadow_state.borrow().is_in_results_mode()
7847    }
7848}
7849
7850impl InputBehavior for EnhancedTuiApp {
7851    fn buffer_manager(&mut self) -> &mut BufferManager {
7852        self.state_container.buffers_mut()
7853    }
7854
7855    fn cursor_manager(&mut self) -> &mut CursorManager {
7856        &mut self.cursor_manager
7857    }
7858
7859    fn set_input_text_with_cursor(&mut self, text: String, cursor: usize) {
7860        self.set_input_text_with_cursor(text, cursor);
7861    }
7862
7863    fn state_container(&self) -> &AppStateContainer {
7864        &self.state_container
7865    }
7866
7867    fn state_container_mut(&mut self) -> &mut AppStateContainer {
7868        &mut self.state_container
7869    }
7870
7871    fn buffer_mut(&mut self) -> &mut dyn BufferAPI {
7872        self.state_container
7873            .current_buffer_mut()
7874            .expect("Buffer should exist")
7875    }
7876
7877    fn set_mode_with_sync(&mut self, mode: AppMode, trigger: &str) {
7878        // Use the existing method that properly synchronizes both buffer and shadow_state
7879        self.set_mode_via_shadow_state(mode, trigger);
7880    }
7881}
7882
7883impl YankBehavior for EnhancedTuiApp {
7884    fn buffer(&self) -> &dyn BufferAPI {
7885        self.state_container
7886            .current_buffer()
7887            .expect("Buffer should exist")
7888    }
7889
7890    fn buffer_mut(&mut self) -> &mut dyn BufferAPI {
7891        self.state_container
7892            .current_buffer_mut()
7893            .expect("Buffer should exist")
7894    }
7895
7896    fn state_container(&self) -> &AppStateContainer {
7897        &self.state_container
7898    }
7899
7900    fn set_status_message(&mut self, message: String) {
7901        self.state_container.set_status_message(message);
7902    }
7903
7904    fn set_error_status(&mut self, prefix: &str, error: anyhow::Error) {
7905        self.set_error_status(prefix, error);
7906    }
7907}
7908
7909impl BufferManagementBehavior for EnhancedTuiApp {
7910    fn buffer_manager(&mut self) -> &mut BufferManager {
7911        self.state_container.buffers_mut()
7912    }
7913
7914    fn buffer_handler(&mut self) -> &mut BufferHandler {
7915        &mut self.buffer_handler
7916    }
7917
7918    fn buffer(&self) -> &dyn BufferAPI {
7919        self.state_container
7920            .current_buffer()
7921            .expect("Buffer should exist")
7922    }
7923
7924    fn buffer_mut(&mut self) -> &mut dyn BufferAPI {
7925        self.state_container
7926            .current_buffer_mut()
7927            .expect("Buffer should exist")
7928    }
7929
7930    fn config(&self) -> &Config {
7931        &self.config
7932    }
7933
7934    fn cursor_manager(&mut self) -> &mut CursorManager {
7935        &mut self.cursor_manager
7936    }
7937
7938    fn set_input_text_with_cursor(&mut self, text: String, cursor: usize) {
7939        self.set_input_text_with_cursor(text, cursor);
7940    }
7941
7942    fn next_buffer(&mut self) -> String {
7943        // Save current viewport state to current buffer before switching
7944        self.save_viewport_to_current_buffer();
7945
7946        let result = self
7947            .buffer_handler
7948            .next_buffer(self.state_container.buffers_mut());
7949
7950        // Sync all state after buffer switch
7951        self.sync_after_buffer_switch();
7952
7953        result
7954    }
7955
7956    fn previous_buffer(&mut self) -> String {
7957        // Save current viewport state to current buffer before switching
7958        self.save_viewport_to_current_buffer();
7959
7960        let result = self
7961            .buffer_handler
7962            .previous_buffer(self.state_container.buffers_mut());
7963
7964        // Sync all state after buffer switch
7965        self.sync_after_buffer_switch();
7966
7967        result
7968    }
7969
7970    fn quick_switch_buffer(&mut self) -> String {
7971        // Save current viewport state to current buffer before switching
7972        self.save_viewport_to_current_buffer();
7973
7974        let result = self
7975            .buffer_handler
7976            .quick_switch(self.state_container.buffers_mut());
7977
7978        // Sync all state after buffer switch
7979        self.sync_after_buffer_switch();
7980
7981        result
7982    }
7983
7984    fn close_buffer(&mut self) -> (bool, String) {
7985        self.buffer_handler
7986            .close_buffer(self.state_container.buffers_mut())
7987    }
7988
7989    fn switch_to_buffer(&mut self, index: usize) -> String {
7990        // Save current viewport state to current buffer before switching
7991        self.save_viewport_to_current_buffer();
7992
7993        // Switch buffer
7994        let result = self
7995            .buffer_handler
7996            .switch_to_buffer(self.state_container.buffers_mut(), index);
7997
7998        // Sync all state after buffer switch
7999        self.sync_after_buffer_switch();
8000
8001        result
8002    }
8003
8004    fn buffer_count(&self) -> usize {
8005        self.state_container.buffers().all_buffers().len()
8006    }
8007
8008    fn current_buffer_index(&self) -> usize {
8009        self.state_container.buffers().current_index()
8010    }
8011}
8012
8013pub fn run_enhanced_tui_multi(api_url: &str, data_files: Vec<&str>) -> Result<()> {
8014    // Set up panic hook to restore terminal on panic
8015    let original_hook = std::panic::take_hook();
8016    std::panic::set_hook(Box::new(move |panic_info| {
8017        // Restore terminal
8018        let _ = disable_raw_mode();
8019        let _ = execute!(
8020            io::stdout(),
8021            LeaveAlternateScreen,
8022            DisableMouseCapture,
8023            crossterm::cursor::Show
8024        );
8025
8026        // Call the original panic hook
8027        original_hook(panic_info);
8028    }));
8029
8030    let app = if data_files.is_empty() {
8031        EnhancedTuiApp::new(api_url)
8032    } else {
8033        // Use ApplicationOrchestrator for clean data source separation
8034        use crate::services::ApplicationOrchestrator;
8035
8036        // Get config for orchestrator setup
8037        let config = Config::default();
8038        let orchestrator = ApplicationOrchestrator::new(
8039            config.behavior.case_insensitive_default,
8040            config.behavior.hide_empty_columns,
8041        );
8042
8043        // Load the first file through the orchestrator
8044        let mut app = orchestrator.create_tui_with_file(data_files[0])?;
8045
8046        // Load additional files into separate buffers
8047        for file_path in data_files.iter().skip(1) {
8048            if let Err(e) = orchestrator.load_additional_file(&mut app, file_path) {
8049                app.state_container
8050                    .set_status_message(format!("Error loading {file_path}: {e}"));
8051                continue;
8052            }
8053        }
8054
8055        // Switch back to the first buffer if we loaded multiple
8056        if data_files.len() > 1 {
8057            app.state_container.buffers_mut().switch_to(0);
8058            // Sync all state after buffer switch
8059            app.sync_after_buffer_switch();
8060            app.state_container.set_status_message(format!(
8061                "Loaded {} files into separate buffers. Use Alt+Tab to switch.",
8062                data_files.len()
8063            ));
8064        } else if data_files.len() == 1 {
8065            // Even for single file, ensure parser is initialized
8066            app.update_parser_for_current_buffer();
8067        }
8068
8069        app
8070    };
8071
8072    let result = app.run();
8073
8074    // Restore the original panic hook
8075    let _ = std::panic::take_hook(); // Drop our hook
8076                                     // Note: We can't restore the original hook here as it was moved into the closure
8077
8078    result
8079}
8080
8081pub fn run_enhanced_tui(api_url: &str, data_file: Option<&str>) -> Result<()> {
8082    // For backward compatibility, convert single file to vec and call multi version
8083    let files = if let Some(file) = data_file {
8084        vec![file]
8085    } else {
8086        vec![]
8087    };
8088    run_enhanced_tui_multi(api_url, files)
8089}