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