//! Keybinding editor action handling
//!
//! This module provides the action handlers for the keybinding editor modal.

use super::keybinding_editor::KeybindingEditor;
use super::Editor;
use crate::input::handler::InputResult;
use crate::view::keybinding_editor::{handle_keybinding_editor_input, KeybindingEditorAction};
use crate::view::ui::point_in_rect;
use crossterm::event::{KeyEvent, MouseButton, MouseEvent, MouseEventKind};

impl Editor {
    /// Open the keybinding editor modal
    pub fn open_keybinding_editor(&mut self) {
        use crate::config::MenuExt;
        let config_path = self.dir_context.config_path().display().to_string();
        let cmd_registry = self.command_registry.read().unwrap();
        let keybindings = self.keybindings.read().unwrap();
        // Enumerate top-level menu ids (File, Edit, …, plus plugin menus) so
        // the action dropdown can offer `menu_open:<name>` variants instead of
        // one un-parseable bare `menu_open` row.
        let menu_names: Vec<String> = self
            .menus
            .menus
            .iter()
            .chain(self.menu_state.plugin_menus.iter())
            .map(|m| m.match_id().to_string())
            .collect();
        self.keybinding_editor = Some(KeybindingEditor::new(
            &self.config,
            &keybindings,
            &self.mode_registry,
            &cmd_registry,
            config_path,
            &menu_names,
        ));
    }

    /// Handle input when keybinding editor is active
    pub fn handle_keybinding_editor_input(&mut self, event: &KeyEvent) -> InputResult {
        let mut editor = match self.keybinding_editor.take() {
            Some(e) => e,
            None => return InputResult::Ignored,
        };

        let action = handle_keybinding_editor_input(&mut editor, event);

        match action {
            KeybindingEditorAction::Consumed => {
                self.keybinding_editor = Some(editor);
                InputResult::Consumed
            }
            KeybindingEditorAction::Close => {
                // Close without saving
                self.set_status_message("Keybinding editor closed".to_string());
                InputResult::Consumed
            }
            KeybindingEditorAction::SaveAndClose => {
                // Save custom bindings to config
                self.save_keybinding_editor_changes(&editor);
                InputResult::Consumed
            }
            KeybindingEditorAction::StatusMessage(msg) => {
                self.set_status_message(msg);
                self.keybinding_editor = Some(editor);
                InputResult::Consumed
            }
        }
    }

    /// Save keybinding editor changes to config
    fn save_keybinding_editor_changes(&mut self, editor: &KeybindingEditor) {
        if !editor.has_changes {
            return;
        }

        // Remove deleted custom bindings from config
        for remove in editor.get_pending_removes() {
            self.config_mut().keybindings.retain(|kb| {
                !(kb.action == remove.action
                    && kb.key == remove.key
                    && kb.modifiers == remove.modifiers
                    && kb.when == remove.when)
            });
        }

        // Add new custom bindings
        let new_bindings = editor.get_custom_bindings();
        for binding in new_bindings {
            self.config_mut().keybindings.push(binding);
        }

        // Rebuild the keybinding resolver, keeping plugin-contributed
        // bindings alive across the rebuild (#2307).
        self.keybindings
            .write()
            .unwrap()
            .reload_from_config(&self.config);

        // Save to config file via the pending changes mechanism
        let config_value = match serde_json::to_value(&self.config.keybindings) {
            Ok(v) => v,
            Err(e) => {
                self.set_status_message(format!("Failed to serialize keybindings: {}", e));
                return;
            }
        };

        let mut changes = std::collections::HashMap::new();
        changes.insert("/keybindings".to_string(), config_value);

        let resolver = crate::config_io::ConfigResolver::new(
            self.dir_context.clone(),
            self.working_dir().to_path_buf(),
        );

        match resolver.save_changes_to_layer(
            &changes,
            &std::collections::HashSet::new(),
            crate::config_io::ConfigLayer::User,
        ) {
            Ok(()) => {
                self.set_status_message("Keybinding changes saved".to_string());
            }
            Err(e) => {
                self.set_status_message(format!("Failed to save keybindings: {}", e));
            }
        }
    }

    /// Check if keybinding editor is active
    pub fn is_keybinding_editor_active(&self) -> bool {
        self.keybinding_editor.is_some()
    }

    /// Handle mouse events when keybinding editor is active
    /// Returns Ok(true) if a re-render is needed
    pub fn handle_keybinding_editor_mouse(
        &mut self,
        mouse_event: MouseEvent,
    ) -> anyhow::Result<bool> {
        let mut editor = match self.keybinding_editor.take() {
            Some(e) => e,
            None => return Ok(false),
        };

        let col = mouse_event.column;
        let row = mouse_event.row;
        let layout = &editor.layout;

        // All mouse events inside modal are consumed (masked from reaching underlying editor)
        // Events outside the modal are ignored (but still consumed to prevent leaking)
        if !point_in_rect(layout.modal_area, col, row) {
            self.keybinding_editor = Some(editor);
            return Ok(false);
        }

        match mouse_event.kind {
            // Scroll the viewport without touching selection. Coupling
            // wheel to selection meant any prior scrollbar drag snapped
            // back via `ensure_visible` on the next wheel tick. Three
            // rows per tick matches the settings modal.
            MouseEventKind::ScrollUp
                if editor.edit_dialog.is_none() && !editor.showing_confirm_dialog =>
            {
                editor.scroll.scroll_by(-3);
            }
            MouseEventKind::ScrollDown
                if editor.edit_dialog.is_none() && !editor.showing_confirm_dialog =>
            {
                editor.scroll.scroll_by(3);
            }
            MouseEventKind::Drag(MouseButton::Left) => {
                // Continue dragging the scrollbar thumb (no selection or
                // dialog disambiguation needed: the press that started the
                // drag already gated those).
                if let Some(sb) = editor.layout.table_scrollbar {
                    let sb_state = scrollbar_state_for(&editor);
                    if let Some(new_offset) = editor.scrollbar_mouse.drag(sb_state, sb, row) {
                        editor.scroll.offset = new_offset as u16;
                    }
                }
            }
            MouseEventKind::Up(MouseButton::Left) => {
                editor.scrollbar_mouse.release();
            }
            MouseEventKind::Down(MouseButton::Left) => {
                // Handle confirm dialog clicks first
                if editor.showing_confirm_dialog {
                    if let Some((save_r, discard_r, cancel_r)) = layout.confirm_buttons {
                        if point_in_rect(save_r, col, row) {
                            self.save_keybinding_editor_changes(&editor);
                            return Ok(true);
                        } else if point_in_rect(discard_r, col, row) {
                            self.set_status_message("Keybinding editor closed".to_string());
                            return Ok(true);
                        } else if point_in_rect(cancel_r, col, row) {
                            editor.showing_confirm_dialog = false;
                        }
                    }
                    self.keybinding_editor = Some(editor);
                    return Ok(true);
                }

                // Handle edit dialog clicks
                if editor.edit_dialog.is_some() {
                    // Button clicks
                    if let Some((save_r, cancel_r)) = layout.dialog_buttons {
                        if point_in_rect(save_r, col, row) {
                            // Save button
                            if let Some(err) = editor.apply_edit_dialog() {
                                self.set_status_message(err);
                            }
                            self.keybinding_editor = Some(editor);
                            return Ok(true);
                        } else if point_in_rect(cancel_r, col, row) {
                            // Cancel button - close dialog
                            editor.edit_dialog = None;
                            self.keybinding_editor = Some(editor);
                            return Ok(true);
                        }
                    }
                    // Field clicks
                    if let Some(r) = layout.dialog_key_field {
                        if point_in_rect(r, col, row) {
                            if let Some(ref mut dialog) = editor.edit_dialog {
                                dialog.focus_area = 0;
                                dialog.mode = crate::app::keybinding_editor::EditMode::RecordingKey;
                            }
                        }
                    }
                    if let Some(r) = layout.dialog_action_field {
                        if point_in_rect(r, col, row) {
                            if let Some(ref mut dialog) = editor.edit_dialog {
                                dialog.focus_area = 1;
                                dialog.mode =
                                    crate::app::keybinding_editor::EditMode::EditingAction;
                            }
                        }
                    }
                    if let Some(r) = layout.dialog_context_field {
                        if point_in_rect(r, col, row) {
                            if let Some(ref mut dialog) = editor.edit_dialog {
                                dialog.focus_area = 2;
                                dialog.mode =
                                    crate::app::keybinding_editor::EditMode::EditingContext;
                            }
                        }
                    }
                    self.keybinding_editor = Some(editor);
                    return Ok(true);
                }

                // Click on search bar to focus it
                if let Some(search_r) = layout.search_bar {
                    if point_in_rect(search_r, col, row) {
                        editor.start_search();
                        self.keybinding_editor = Some(editor);
                        return Ok(true);
                    }
                }

                // Press on the scrollbar — delegate to the shared widget
                // so press-on-thumb (no jump), press-on-track (recentre),
                // and the follow-up drag all run through the same well-
                // tested math. Checked before the row-click branch because
                // the scrollbar overlaps the rightmost column of `table_area`.
                if let Some(sb) = layout.table_scrollbar {
                    let sb_state = scrollbar_state_for(&editor);
                    if let Some(new_offset) = editor.scrollbar_mouse.press(sb_state, sb, col, row) {
                        editor.scroll.offset = new_offset as u16;
                        self.keybinding_editor = Some(editor);
                        return Ok(true);
                    }
                }

                // Click on table row to select (or toggle section header)
                let table_area = layout.table_area;
                let first_row_y = layout.table_first_row_y;
                if point_in_rect(table_area, col, row) && row >= first_row_y {
                    let clicked_row = (row - first_row_y) as usize;
                    let new_selected = editor.scroll.offset as usize + clicked_row;
                    if new_selected < editor.display_rows.len() {
                        editor.selected = new_selected;
                        if editor.selected_is_section_header() {
                            editor.toggle_section_at_selected();
                        }
                    }
                }
            }
            _ => {}
        }

        self.keybinding_editor = Some(editor);
        Ok(true)
    }

    /// Select a display row by index (and toggle it if it's a section header) —
    /// the same effect as a TUI click on that table row. Used by the web
    /// `/kbedit` route so a native row click selects through the real editor.
    #[cfg(feature = "web")]
    pub(crate) fn kbedit_select_display_row(&mut self, idx: usize) {
        if let Some(ed) = self.keybinding_editor.as_mut() {
            if idx < ed.display_rows.len() {
                ed.selected = idx;
                if ed.selected_is_section_header() {
                    ed.toggle_section_at_selected();
                }
            }
        }
    }
}

/// Snapshot the keybinding editor's scroll state as a `ScrollbarState`,
/// so we can call into the shared scrollbar widget for click/drag math.
fn scrollbar_state_for(editor: &KeybindingEditor) -> crate::view::ui::scrollbar::ScrollbarState {
    crate::view::ui::scrollbar::ScrollbarState::new(
        editor.scroll.content_height as usize,
        editor.scroll.viewport as usize,
        editor.scroll.offset as usize,
    )
}
