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