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