// src/modes/canvas/edit.rs use crate::config::binds::config::Config; use crate::services::grpc_client::GrpcClient; use crate::state::pages::{ auth::{LoginState, RegisterState}, canvas_state::CanvasState, }; use crate::state::pages::form::FormState; // <<< ADD THIS LINE // AddLogicState is already imported // AddTableState is already imported use crate::state::pages::admin::AdminState; use crate::modes::handlers::event::EventOutcome; use crate::functions::modes::edit::{add_logic_e, auth_e, form_e, add_table_e}; use crate::state::app::state::AppState; use anyhow::Result; use crossterm::event::KeyEvent; // Removed KeyCode, KeyModifiers as they were unused use tracing::debug; #[derive(Debug, Clone, PartialEq, Eq)] pub enum EditEventOutcome { Message(String), ExitEditMode, } pub async fn handle_edit_event( key: KeyEvent, config: &Config, form_state: &mut FormState, // Now FormState is in scope login_state: &mut LoginState, register_state: &mut RegisterState, admin_state: &mut AdminState, ideal_cursor_column: &mut usize, current_position: &mut u64, total_count: u64, grpc_client: &mut GrpcClient, app_state: &AppState, ) -> Result { // --- Global command mode check --- if let Some("enter_command_mode") = config.get_action_for_key_in_mode( &config.keybindings.global, // Assuming command mode can be entered globally key.code, key.modifiers, ) { // This check might be redundant if EventHandler already prevents entering Edit mode // when command_mode is true. However, it's a safeguard. return Ok(EditEventOutcome::Message( "Cannot enter command mode from edit mode here.".to_string(), )); } // --- Common actions (save, revert) --- if let Some(action) = config.get_action_for_key_in_mode( &config.keybindings.common, key.code, key.modifiers, ).as_deref() { if matches!(action, "save" | "revert") { let message_string: String = if app_state.ui.show_login { auth_e::execute_common_action(action, login_state, grpc_client, current_position, total_count).await? } else if app_state.ui.show_register { auth_e::execute_common_action(action, register_state, grpc_client, current_position, total_count).await? } else if app_state.ui.show_add_table { // TODO: Implement common actions for AddTable if needed format!("Action '{}' not implemented for Add Table in edit mode.", action) } else if app_state.ui.show_add_logic { // TODO: Implement common actions for AddLogic if needed format!("Action '{}' not implemented for Add Logic in edit mode.", action) } else { // Assuming Form view let outcome = form_e::execute_common_action(action, form_state, grpc_client, current_position, total_count).await?; match outcome { EventOutcome::Ok(msg) | EventOutcome::DataSaved(_, msg) => msg, _ => format!("Unexpected outcome from common action: {:?}", outcome), } }; return Ok(EditEventOutcome::Message(message_string)); } } // --- Edit-specific actions --- if let Some(action_str) = config.get_edit_action_for_key(key.code, key.modifiers).as_deref() { // --- Handle "enter_decider" (Enter key) --- if action_str == "enter_decider" { let effective_action = if app_state.ui.show_register && register_state.in_suggestion_mode && register_state.current_field() == 4 { // Role field "select_suggestion" } else if app_state.ui.show_add_logic && admin_state.add_logic_state.in_target_column_suggestion_mode && admin_state.add_logic_state.current_field() == 1 { // Target Column field "select_suggestion" } else { "next_field" // Default action for Enter }; let msg = if app_state.ui.show_login { auth_e::execute_edit_action(effective_action, key, login_state, ideal_cursor_column).await? } else if app_state.ui.show_add_table { add_table_e::execute_edit_action(effective_action, key, &mut admin_state.add_table_state, ideal_cursor_column).await? } else if app_state.ui.show_add_logic { add_logic_e::execute_edit_action(effective_action, key, &mut admin_state.add_logic_state, ideal_cursor_column).await? } else if app_state.ui.show_register { auth_e::execute_edit_action(effective_action, key, register_state, ideal_cursor_column).await? } else { // Form view form_e::execute_edit_action(effective_action, key, form_state, ideal_cursor_column).await? }; return Ok(EditEventOutcome::Message(msg)); } // --- Handle "exit" (Escape key) --- if action_str == "exit" { if app_state.ui.show_register && register_state.in_suggestion_mode { let msg = auth_e::execute_edit_action("exit_suggestion_mode", key, register_state, ideal_cursor_column).await?; return Ok(EditEventOutcome::Message(msg)); } else if app_state.ui.show_add_logic && admin_state.add_logic_state.in_target_column_suggestion_mode { admin_state.add_logic_state.in_target_column_suggestion_mode = false; admin_state.add_logic_state.show_target_column_suggestions = false; admin_state.add_logic_state.selected_target_column_suggestion_index = None; return Ok(EditEventOutcome::Message("Exited column suggestions".to_string())); } else { return Ok(EditEventOutcome::ExitEditMode); } } // --- Autocomplete for AddLogicState Target Column --- if app_state.ui.show_add_logic && admin_state.add_logic_state.current_field() == 1 { // Target Column field if action_str == "suggestion_down" { // "Tab" is mapped to suggestion_down if !admin_state.add_logic_state.in_target_column_suggestion_mode { // Attempt to open suggestions if let Some(profile_name) = admin_state.add_logic_state.profile_name.clone().into() { if let Some(table_name) = admin_state.add_logic_state.selected_table_name.clone() { debug!("Fetching table structure for autocomplete: Profile='{}', Table='{}'", profile_name, table_name); match grpc_client.get_table_structure(profile_name, table_name).await { Ok(ts_response) => { admin_state.add_logic_state.table_columns_for_suggestions = ts_response.columns.into_iter().map(|c| c.name).collect(); admin_state.add_logic_state.update_target_column_suggestions(); if !admin_state.add_logic_state.target_column_suggestions.is_empty() { admin_state.add_logic_state.in_target_column_suggestion_mode = true; // update_target_column_suggestions handles initial selection return Ok(EditEventOutcome::Message("Column suggestions shown".to_string())); } else { return Ok(EditEventOutcome::Message("No column suggestions for current input".to_string())); } } Err(e) => { debug!("Error fetching table structure: {}", e); admin_state.add_logic_state.table_columns_for_suggestions.clear(); // Clear old data on error admin_state.add_logic_state.update_target_column_suggestions(); return Ok(EditEventOutcome::Message(format!("Error fetching columns: {}", e))); } } } else { return Ok(EditEventOutcome::Message("No table selected for column suggestions".to_string())); } } else { // Should not happen if AddLogic is properly initialized return Ok(EditEventOutcome::Message("Profile name missing for column suggestions".to_string())); } } else { // Already in suggestion mode, navigate down let msg = add_logic_e::execute_edit_action(action_str, key, &mut admin_state.add_logic_state, ideal_cursor_column).await?; return Ok(EditEventOutcome::Message(msg)); } } else if admin_state.add_logic_state.in_target_column_suggestion_mode && action_str == "suggestion_up" { let msg = add_logic_e::execute_edit_action(action_str, key, &mut admin_state.add_logic_state, ideal_cursor_column).await?; return Ok(EditEventOutcome::Message(msg)); } } // --- Autocomplete for RegisterState Role Field --- if app_state.ui.show_register && register_state.current_field() == 4 { // Role field if !register_state.in_suggestion_mode && action_str == "suggestion_down" { // Tab register_state.update_role_suggestions(); if !register_state.role_suggestions.is_empty() { register_state.in_suggestion_mode = true; // update_role_suggestions should handle initial selection return Ok(EditEventOutcome::Message("Role suggestions shown".to_string())); } else { // If Tab doesn't open suggestions, it might fall through to "next_field" // or you might want specific behavior. For now, let it fall through. } } if register_state.in_suggestion_mode && matches!(action_str, "suggestion_down" | "suggestion_up") { let msg = auth_e::execute_edit_action(action_str, key, register_state, ideal_cursor_column).await?; return Ok(EditEventOutcome::Message(msg)); } } // --- Dispatch other edit actions --- let msg = if app_state.ui.show_login { auth_e::execute_edit_action(action_str, key, login_state, ideal_cursor_column).await? } else if app_state.ui.show_add_table { add_table_e::execute_edit_action(action_str, key, &mut admin_state.add_table_state, ideal_cursor_column).await? } else if app_state.ui.show_add_logic { // If not a suggestion action handled above for AddLogic if !(admin_state.add_logic_state.in_target_column_suggestion_mode && matches!(action_str, "suggestion_down" | "suggestion_up")) { add_logic_e::execute_edit_action(action_str, key, &mut admin_state.add_logic_state, ideal_cursor_column).await? } else { String::new() /* Already handled */ } } else if app_state.ui.show_register { if !(register_state.in_suggestion_mode && matches!(action_str, "suggestion_down" | "suggestion_up")) { auth_e::execute_edit_action(action_str, key, register_state, ideal_cursor_column).await? } else { String::new() /* Already handled */ } } else { // Form view form_e::execute_edit_action(action_str, key, form_state, ideal_cursor_column).await? }; return Ok(EditEventOutcome::Message(msg)); } // --- Character insertion --- // If character insertion happens while in suggestion mode, exit suggestion mode first. let mut exited_suggestion_mode_for_typing = false; if app_state.ui.show_register && register_state.in_suggestion_mode { register_state.in_suggestion_mode = false; register_state.show_role_suggestions = false; register_state.selected_suggestion_index = None; exited_suggestion_mode_for_typing = true; } if app_state.ui.show_add_logic && admin_state.add_logic_state.in_target_column_suggestion_mode { admin_state.add_logic_state.in_target_column_suggestion_mode = false; admin_state.add_logic_state.show_target_column_suggestions = false; admin_state.add_logic_state.selected_target_column_suggestion_index = None; exited_suggestion_mode_for_typing = true; } let mut char_insert_msg = if app_state.ui.show_login { auth_e::execute_edit_action("insert_char", key, login_state, ideal_cursor_column).await? } else if app_state.ui.show_add_table { add_table_e::execute_edit_action("insert_char", key, &mut admin_state.add_table_state, ideal_cursor_column).await? } else if app_state.ui.show_add_logic { add_logic_e::execute_edit_action("insert_char", key, &mut admin_state.add_logic_state, ideal_cursor_column).await? } else if app_state.ui.show_register { auth_e::execute_edit_action("insert_char", key, register_state, ideal_cursor_column).await? } else { // Form view form_e::execute_edit_action("insert_char", key, form_state, ideal_cursor_column).await? }; // After character insertion, update suggestions if applicable if app_state.ui.show_register && register_state.current_field() == 4 { register_state.update_role_suggestions(); // If we just exited suggestion mode by typing, don't immediately show them again unless Tab is pressed. // However, update_role_suggestions will set show_role_suggestions if matches are found. // This is fine, as the render logic checks in_suggestion_mode. } if app_state.ui.show_add_logic && admin_state.add_logic_state.current_field() == 1 { admin_state.add_logic_state.update_target_column_suggestions(); } if exited_suggestion_mode_for_typing && char_insert_msg.is_empty() { char_insert_msg = "Suggestions hidden".to_string(); } Ok(EditEventOutcome::Message(char_insert_msg)) }