use crate::app::{Action, reducer};
use crate::input::InputEvent;
use crate::errors::ApplicationError;
use crate::app::Application;
use crate::config;
use crate::async_result::AsyncResult;
use crate::app::workflow::WorkflowState;
use std::fs;
use ratatui::backend::CrosstermBackend;
use ratatui::Terminal;
use tokio::time::Duration;
use futures::StreamExt;

/// Central event loop runner extracted from `Application::run`.
pub async fn run(
    app: &mut Application,
    terminal: &mut Terminal<CrosstermBackend<std::io::Stdout>>,
) -> Result<(), ApplicationError> {
    // Initialize all components
    for component in app.component_manager.components.values_mut() {
        component
            .init(&app.state)
            .map_err(ApplicationError::ComponentInitFailed)?;
    }

    // Initial data loading - refresh status
    app.state = reducer(app.state.clone(), Action::SetRefreshing(true));
    if let Err(e) = app.component_manager.update(Action::RefreshStatus, &mut app.state) {
            tracing::warn!(error = %e, "Initial status refresh failed");
    }
    app.state = reducer(app.state.clone(), Action::SetRefreshing(false));
    
    // Initialize all data sources - these are non-critical and can fail gracefully
    // Log errors but don't block application startup
    initialize_data_sources(app);
    
    app.component_manager.sync_status_selection(&mut app.state);
    // Initialize workflow context
    app.state = reducer(app.state.clone(), Action::UpdateWorkflowContext);

    // If a rebase is already in progress, load the existing todo into the builder.
    if matches!(
        app.state
            .workflow_context
            .as_ref()
            .map(|ctx| ctx.state.clone()),
        Some(WorkflowState::RebaseInProgress)
    ) {
        match app.git_service.read_rebase_todo(&app.state.repo_path) {
            Ok((lines, _path)) => {
                app.state = reducer(app.state.clone(), Action::RebaseLoadTodo(lines));
                app.state = reducer(app.state.clone(), Action::AppendOpLog("Loaded existing rebase todo".into()));
            }
            Err(e) => {
                tracing::warn!(error = %e, "Failed to load existing rebase todo on startup");
            }
        }
    }

    while app.state.running && !*app.cancel_rx.borrow() {
        // 1. Render
        terminal
            .draw(|frame| {
                if let Err(e) = app.component_manager.render(frame, &app.state) {
                    tracing::error!(error = %e, "Render failed");
                }
            })
            .map_err(ApplicationError::IoError)?;

        // 2. Wait for event (with timeout to prevent hanging)
        let event = tokio::select! {
            event = app.event_stream.next() => {
                match event {
                    Some(Ok(e)) => Some(InputEvent::from(e)),
                    Some(Err(e)) => {
                        tracing::error!(error = %e, "Event stream error");
                        None
                    }
                    None => return Err(ApplicationError::EventStreamClosed),
                }
            }
            async_result = app.async_rx.recv() => {
                match async_result {
                    Some(result) => {
                        // Handle async result immediately
                        app.handle_async_result(result).await?;
                        continue; // Skip to next iteration
                    }
                    None => None, // Channel closed
                }
            }
            _ = app.cancel_rx.changed() => {
                if *app.cancel_rx.borrow() {
                    tracing::info!("Application cancelled");
                    break;
                }
                None
            }
            _ = tokio::time::sleep(Duration::from_millis(100)) => {
                // Timeout to prevent hanging
                None
            }
        };

        if let Some(input_event) = event {
            // 3. Handle event (component returns action)
            match app.component_manager.handle_event(input_event, &app.state) {
                Ok(Some(action)) => {
                    if let Action::ApplyTheme(ref name) = action {
                        let theme = config::load_theme_by_name(name);
                        // Use reducer to ensure state consistency
                        app.state = reducer(app.state.clone(), action.clone());
                        app.state.theme = theme.clone();
                        app.state.theme_picker = false;
                        let _ = config::persist_theme(name);
                        // Don't use continue - let the normal event loop handle rendering
                        // This ensures immediate UI refresh
                    }
                    if let Action::ReloadTheme = action {
                        match config::load() {
                            Ok(settings) => {
                                app.state.theme = settings.theme_config.clone();
                                app.state.theme_picker = false;
                                app.state = reducer(app.state.clone(), Action::HidePalette);
                            }
                            Err(e) => {
                                app.state = reducer(
                                    app.state.clone(),
                                    Action::SetStatusError(Some(format!("theme reload failed: {e}")))
                                );
                            }
                        }
                        continue;
                    }
                    if let Action::ReloadConfig = action {
                        match config::load() {
                            Ok(settings) => {
                                // Reload all configuration values
                                app.state.theme = settings.theme_config.clone();
                                app.state.available_themes = config::list_themes();
                                app.state.commit_template = settings.commit_template.clone();
                                app.state.custom_commands = settings.custom_commands.clone();
                                // Update editor_command if it exists in AppState
                                // Note: editor_command is currently only in Settings, not AppState
                                // This ensures all config is refreshed immediately
                                app.state = reducer(app.state.clone(), Action::SetFeedback(Some("Configuration reloaded".to_string())));
                            }
                            Err(e) => {
                                app.state = reducer(
                                    app.state.clone(),
                                    Action::SetStatusError(Some(format!("config reload failed: {e}")))
                                );
                            }
                        }
                        continue;
                    }
                    // 4. Handle ExecuteLogAction - convert to appropriate action and close menu
        let action = match &action {
                        Action::ExecuteLogAction(log_action) => {
                            // Save the commit hash before closing the menu (reducer clears it)
                            let commit_hash = app.state.log_action_commit_hash.clone();
                            // Close the menu
                            app.state = reducer(app.state.clone(), Action::HideLogActionMenu);
                            if let Some(hash) = commit_hash {
                                match log_action {
                                    crate::app::actions::LogAction::ShowDetail => {
                                        Action::ShowCommitDetail(hash.clone())
                                    }
                                    crate::app::actions::LogAction::CherryPick => {
                    if let Some(_commit) = app.state.commits.iter().find(|c| c.hash == hash) {
                        let msg = format!("Cherry-pick {} - {} ?", _commit.short_hash, _commit.message);
                                            Action::ShowConfirm(
                            Box::new(Action::CherryPickCommit(_commit.short_hash.clone())),
                                                msg,
                                            )
                                        } else {
                                            continue; // Invalid commit
                                        }
                                    }
                                    crate::app::actions::LogAction::Revert => {
                    if let Some(_commit) = app.state.commits.iter().find(|c| c.hash == hash) {
                        let msg = format!("Revert {} - {} ?", _commit.short_hash, _commit.message);
                                            Action::ShowConfirm(
                            Box::new(Action::RevertCommit(_commit.short_hash.clone())),
                                                msg,
                                            )
                                        } else {
                                            continue; // Invalid commit
                                        }
                                    }
                                    crate::app::actions::LogAction::Amend => {
                                        Action::StartAmend
                                    }
                                crate::app::actions::LogAction::ResetSoft => {
                                    // For reset, we need to create a ResetCommand with the target commit
                                    // Store the target in state temporarily, or create command directly
                                    if let Some(_commit) = app.state.commits.iter().find(|c| c.hash == hash) {
                                        // Create ResetCommand directly - we'll need to handle this differently
                                        // For now, use the existing reset actions which reset to HEAD~1
                                        // TODO: Enhance to support resetting to arbitrary commits
                                        Action::RequestResetSoft
                                    } else {
                                        continue; // Invalid commit
                                    }
                                }
                                crate::app::actions::LogAction::ResetMixed => {
                                    if let Some(_commit) = app.state.commits.iter().find(|c| c.hash == hash) {
                                        Action::RequestResetMixed
                                    } else {
                                        continue; // Invalid commit
                                    }
                                }
                                crate::app::actions::LogAction::ResetHard => {
                                    if let Some(_commit) = app.state.commits.iter().find(|c| c.hash == hash) {
                                        Action::RequestResetHard
                                    } else {
                                        continue; // Invalid commit
                                    }
                                }
                                    crate::app::actions::LogAction::CreateBranch => {
                                        // TODO: Store target commit hash for branch creation
                                        Action::StartBranchCreate
                                    }
                                    crate::app::actions::LogAction::CreateTag => {
                                        // TODO: Implement tag creation
                                        app.state = reducer(app.state.clone(), Action::SetStatusError(Some("Tag creation not yet implemented".to_string())));
                                        continue;
                                    }
                                }
                            } else {
                                continue; // No commit hash
                            }
                        }
                        _ => action.clone(),
                    };

                    // 5. Execute command if needed
                    let was_palette_open = app.state.command_palette;
                    if matches!(action, Action::StartAmend) {
                        if let Err(msg) = preflight_amend(&app.state) {
                            app.state = reducer(
                                app.state.clone(),
                                Action::SetStatusError(Some(msg)),
                            );
                            continue;
                        }
                    }

                    if matches!(action, Action::CherryPickCommit(_))
                        || matches!(action, Action::CherryPickSelected)
                    {
                        if let Err(msg) = preflight_pick_revert(&app.state, "Cherry-pick") {
                            app.state = reducer(
                                app.state.clone(),
                                Action::SetStatusError(Some(msg)),
                            );
                            continue;
                        }
                    }

                    if matches!(action, Action::RevertCommit(_)) || matches!(action, Action::RevertSelected) {
                        if let Err(msg) = preflight_pick_revert(&app.state, "Revert") {
                            app.state = reducer(
                                app.state.clone(),
                                Action::SetStatusError(Some(msg)),
                            );
                            continue;
                        }
                    }

                    // Confirm modal execution
                    let action = if matches!(action, Action::ConfirmExecute) {
                        if let Some(next) = app.state.confirm_action.clone() {
                            app.state = reducer(app.state.clone(), Action::ConfirmCancel);
                            next
                        } else {
                            app.state = reducer(app.state.clone(), Action::ConfirmCancel);
                            continue;
                        }
                    } else {
                        action
                    };

                    if matches!(action, Action::RebaseAbortFromBuilder) {
                        if matches!(
                            app.state
                                .workflow_context
                                .as_ref()
                                .map(|ctx| ctx.state.clone()),
                            Some(WorkflowState::RebaseInProgress)
                        ) {
                            match app.git_service.rebase_abort(&app.state.repo_path) {
                                Ok(_) => {
                                    app.state = reducer(
                                        app.state.clone(),
                                        Action::AppendOpLog("rebase --abort".into()),
                                    );
                                    app.state = reducer(
                                        app.state.clone(),
                                        Action::SetFeedback(Some("Rebase aborted".into())),
                                    );
                                }
                                Err(e) => {
                                    app.state = reducer(
                                        app.state.clone(),
                                        Action::SetStatusError(Some(format!("rebase --abort error: {e}"))),
                                    );
                                }
                            }
                        }
                        app.state = reducer(app.state.clone(), Action::RebaseCancel);
                        continue;
                    }

                    if matches!(action, Action::RebaseSaveAndRun) {
                        // If any entry is reword and we don't have a reword message yet, open the modal and prompt.
                        let needs_reword = app
                            .state
                            .rebase_entries
                            .iter()
                            .find(|e| matches!(e.action, crate::app::state::RebaseAction::Reword))
                            .map(|e| e.message.clone());
                        if let Some(msg) = needs_reword {
                            let have_msg = app
                                .state
                                .reword_message
                                .as_ref()
                                .map(|m| !m.trim().is_empty())
                                .unwrap_or(false);
                            if !have_msg || app.state.reword_modal_open {
                                app.state = reducer(app.state.clone(), Action::OpenRewordModal(msg));
                                continue;
                            }
                        }

                        if let Err(msg) = preflight_rebase_builder(&app.state) {
                            app.state = reducer(
                                app.state.clone(),
                                Action::SetStatusError(Some(msg)),
                            );
                            continue;
                        }

                        // If any edit steps exist, set an op_status hint up front.
                        let has_edit = app
                            .state
                            .rebase_entries
                            .iter()
                            .any(|e| matches!(e.action, crate::app::state::RebaseAction::Edit));
                        if has_edit {
                            app.state = reducer(
                                app.state.clone(),
                                Action::SetOpStatus(Some(
                                    "Rebase with edit: will pause at edit steps; use continue/skip/abort (A abort)".into(),
                                )),
                            );
                        }

                        // Handle builder abort (if triggered)
                        if matches!(action, Action::RebaseAbortFromBuilder) {
                            continue;
                        }
                        // Build todo content
                        let todo_lines: Vec<String> = app
                            .state
                            .rebase_entries
                            .iter()
                            .map(|e| {
                                let verb = match e.action {
                                    crate::app::state::RebaseAction::Pick => "pick",
                                    crate::app::state::RebaseAction::Reword => "reword",
                                    crate::app::state::RebaseAction::Edit => "edit",
                                    crate::app::state::RebaseAction::Squash => "squash",
                                    crate::app::state::RebaseAction::Fixup => "fixup",
                                    crate::app::state::RebaseAction::Drop => "drop",
                                };
                                format!("{verb} {} {}", e.hash, e.message)
                            })
                            .collect();
                        if todo_lines.is_empty() {
                            app.state = reducer(
                                app.state.clone(),
                                Action::SetStatusError(Some("No commits selected for interactive rebase".into())),
                            );
                            continue;
                        }

                        let in_rebase = matches!(
                            app.state
                                .workflow_context
                                .as_ref()
                                .map(|ctx| ctx.state.clone()),
                            Some(WorkflowState::RebaseInProgress)
                        );

                        if in_rebase {
                            // Rebase already in progress: write into existing todo
                            match app.git_service.read_rebase_todo(&app.state.repo_path) {
                                Ok((_lines, path)) => {
                                    let mut content = todo_lines.join("\n");
                                    if !content.ends_with('\n') {
                                        content.push('\n');
                                    }
                                    match fs::write(&path, content) {
                                        Ok(_) => {
                                            app.state = reducer(
                                                app.state.clone(),
                                                Action::AppendOpLog("Rebase todo updated (in-progress)".into()),
                                            );
                                            app.state = reducer(
                                                app.state.clone(),
                                                Action::SetFeedback(Some(
                                                    "Rebase todo written. Run rebase --continue.".into(),
                                                )),
                                            );
                                            app.state.rebase_editor_open = false;
                                            app.state.rebase_dirty = false;
                                        }
                                        Err(e) => {
                                            app.state = reducer(
                                                app.state.clone(),
                                                Action::SetStatusError(Some(format!(
                                                    "Failed to write rebase todo: {e}"
                                                ))),
                                            );
                                        }
                                    }
                                }
                                Err(e) => {
                                    app.state = reducer(
                                        app.state.clone(),
                                        Action::SetStatusError(Some(format!(
                                            "No rebase todo found: {e}"
                                        ))),
                                    );
                                }
                            }
                        } else {
                            // Fresh rebase: start rebase -i and then overwrite todo
                        let base_hash = app
                            .state
                            .rebase_base
                            .clone()
                            .or_else(|| app.state.rebase_entries.last().map(|e| e.hash.clone()));
                        if base_hash.is_none() {
                            app.state = reducer(
                                app.state.clone(),
                                Action::SetStatusError(Some("No base found for interactive rebase".into())),
                            );
                            continue;
                        }
                        let base_hash = base_hash.unwrap();

                        // Determine if we need --root (base is root).
                        let root_hash = app.git_service.root_commit(&app.state.repo_path).unwrap_or_default();
                        let use_root = !root_hash.is_empty() && root_hash == base_hash;

                        // If only one commit is being rebased and base == commit, this would be a no-op
                        // and could fail; reject early with a helpful message (unless using root).
                        if !use_root && app.state.rebase_target_only {
                            if let Some(first) = app.state.rebase_entries.first() {
                                if first.hash == base_hash {
                                    app.state = reducer(
                                        app.state.clone(),
                                        Action::SetStatusError(Some(
                                            "Cannot rebase a single commit onto itself; pick a different base".into(),
                                        )),
                                    );
                                    continue;
                                }
                            }
                        }

                        let base_parent = if use_root {
                            None
                        } else {
                            Some(format!("{base_hash}^"))
                        };

                            let repo = app.state.repo_path.clone();
                            let result_todo = if use_root {
                                app.git_service.rebase_start_root(&repo)
                            } else if let Some(ref bp) = base_parent {
                                app.git_service.start_rebase_interactive(&repo, bp)
                            } else {
                                app.git_service.start_rebase_interactive(&repo, &base_hash)
                            };
                            match result_todo {
                                Ok(todo_path) => {
                                    let mut content = todo_lines.join("\n");
                                    if !content.ends_with('\n') {
                                        content.push('\n');
                                    }
                                    match fs::write(&todo_path, content) {
                                        Ok(_) => {
                                            let log_base = base_parent.clone().unwrap_or_else(|| base_hash.clone());
                                            app.state = reducer(
                                                app.state.clone(),
                                                Action::AppendOpLog(format!(
                                                    "rebase -i {} (todo written)",
                                                    log_base
                                                )),
                                            );
                                            app.state = reducer(
                                                app.state.clone(),
                                                Action::SetFeedback(Some(
                                                    "Rebase todo written. Run rebase --continue."
                                                        .into(),
                                                )),
                                            );
                                            app.state.rebase_editor_open = false;
                                            app.state.rebase_dirty = false;
                                        }
                                        Err(e) => {
                                            app.state = reducer(
                                                app.state.clone(),
                                                Action::SetStatusError(Some(format!(
                                                    "Failed to write rebase todo: {e}"
                                                ))),
                                            );
                                        }
                                    }
                                }
                                Err(e) => {
                                    app.state = reducer(
                                        app.state.clone(),
                                        Action::SetStatusError(Some(format!(
                                            "rebase -i failed to start: {e}"
                                        ))),
                                    );
                                }
                            }
                        }
                        continue;
                    }

                    if matches!(action, Action::CommitSubmit) && app.state.amend_mode {
                        if let Err(msg) = validate_amend_input(&app.state) {
                            app.state = reducer(
                                app.state.clone(),
                                Action::SetStatusError(Some(msg)),
                            );
                            continue;
                        }
                    }

                    if let Some(cmd) = app.action_to_command(&action) {
                        match cmd.execute(&app.git_service, &app.state) {
                            Ok(result) => {
                                app.apply_command_result(result).await?;
                                // Close palette after command execution if it was open
                                if was_palette_open {
                                    app.state = reducer(app.state.clone(), Action::HidePalette);
                                }
                            }
                            Err(e) => {
                                tracing::error!(error = %e, "Command execution failed");
                                app.state = reducer(
                                    app.state.clone(),
                                    Action::SetStatusError(Some(e.to_string()))
                                );
                                // Close palette even on error if it was open
                                if was_palette_open {
                                    app.state = reducer(app.state.clone(), Action::HidePalette);
                                }
                            }
                        }
                    } else if was_palette_open {
                        // Only show error for actions that should have commands but don't
                        // UI navigation actions (PaletteUp, PaletteDown, etc.) are handled by reducers, not commands
                        let is_ui_action = matches!(
                            &action,
                            Action::PaletteUp
                            | Action::PaletteDown
                            | Action::SetPaletteInput(_)
                            | Action::ShowPalette
                            | Action::HidePalette
                            | Action::CommitUp
                            | Action::CommitDown
                            | Action::StatusUp
                            | Action::StatusDown
                            | Action::BranchUp
                            | Action::BranchDown
                            | Action::StashUp
                            | Action::StashDown
                            | Action::TagUp
                            | Action::TagDown
                            | Action::ReflogUp
                            | Action::ReflogDown
                            | Action::LogActionMenuUp
                            | Action::LogActionMenuDown
                            | Action::ShowLogActionMenu(_)
                            | Action::HideLogActionMenu
                            | Action::FilterConflictsOnly
                            | Action::ShowConflictsPopup
                            | Action::HideConflictsPopup
                            | Action::ConflictsPopupUp
                            | Action::ConflictsPopupDown
                            | Action::StatusNextConflict
                            | Action::StatusPrevConflict
                        );
                        
                        if !is_ui_action {
                            // This action should have a command but doesn't - show error
                            tracing::warn!(action = ?action, "No command found for action");
                            app.state = reducer(
                                app.state.clone(),
                                Action::SetStatusError(Some(format!("No command found for action: {:?}", action)))
                            );
                            app.state = reducer(app.state.clone(), Action::HidePalette);
                        }
                        // For UI actions, just let them fall through to reducer/component update
                    }

                    // 5. Handle ShowLogActionMenu - state will be updated by reducer at the end
                    // No special handling needed - just let it fall through to the reducer
                    // The reducer will set log_action_menu = true, and the next render cycle will show the menu

                    // 6. Handle ShowCommitDetail asynchronously to avoid blocking
                    if let Action::ShowCommitDetail(ref hash) = action {
                        // Show loading state immediately
                        app.state = reducer(app.state.clone(), Action::SetCommitDetail(Some("Loading commit details...".to_string())));
                        
                        // Spawn blocking task to fetch commit details without blocking the event loop
                        let path = app.state.repo_path.clone();
                        let git_service = app.git_service.clone();
                        let hash_clone = hash.clone();
                        let async_tx = app.async_tx.clone();
                        
                        tokio::task::spawn_blocking(move || {
                            match git_service.show_commit(&path, &hash_clone) {
                                Ok(detail) => {
                                    // Send result via async channel
                                    let _ = async_tx.send(AsyncResult::CommitDetailLoaded(detail));
                                }
                                Err(e) => {
                                    let _ = async_tx.send(AsyncResult::CommitDetailError(format!("show commit error: {e}")));
                                }
                            }
                        });
                        
                        // Skip component update for this action - it's handled asynchronously
                        continue;
                    }

                    // 7. Handle RequestRefreshStatus - convert to RefreshStatus with immediate feedback
                    let action = if matches!(action, Action::RequestRefreshStatus) {
                        // Show immediate visual feedback
                        app.state = reducer(app.state.clone(), Action::RequestRefreshStatus);
                        // Convert to actual refresh action
                        Action::RefreshStatus
                    } else {
                        action
                    };

                    // 7. Apply reducer FIRST for immediate state updates
                    // This ensures UI state changes (like ShowLogActionMenu) are applied before render
                    // Note: Some actions are handled by components, some by reducer
                    // We apply reducer for all actions to ensure state consistency
                    app.state = reducer(app.state.clone(), action.clone());

                    // 7a. Short-circuit for ShowLogActionMenu to re-render immediately
                    if matches!(action, Action::ShowLogActionMenu(_)) {
                        tracing::debug!(
                            "ShowLogActionMenu applied, log_action_menu={}, commit_hash={:?}",
                            app.state.log_action_menu,
                            app.state.log_action_commit_hash
                        );
                        // Force an immediate render so the menu becomes visible before waiting for next input
                        if let Err(e) = terminal.draw(|frame| {
                            if let Err(re) = app.component_manager.render(frame, &app.state) {
                                tracing::error!(error = %re, "Render failed (log action menu)");
                            }
                        }) {
                            tracing::error!(error = %e, "Immediate render failed after ShowLogActionMenu");
                        }
                        continue;
                    }

                    // 8. Update component AFTER reducer (components can modify state directly)
                    // Skip component update for pure UI navigation actions - they're handled by reducer only
                    let is_ui_nav = matches!(
                        &action,
                        Action::PaletteUp
                            | Action::PaletteDown
                            | Action::SetPaletteInput(_)
                            | Action::ShowLogActionMenu(_)
                            | Action::HideLogActionMenu
                            | Action::LogActionMenuUp
                            | Action::LogActionMenuDown
                    );
                    if !is_ui_nav {
                        if let Err(e) = app.component_manager.update(action.clone(), &mut app.state) {
                            tracing::error!(error = %e, "Component update failed");
                        }
                    }

                    // 8. Handle selection changes (trigger diff refresh)
                    if matches!(
                        action,
                        Action::StatusUp
                            | Action::StatusDown
                            | Action::StatusPageUp
                            | Action::StatusPageDown
                            | Action::StatusTop
                            | Action::StatusBottom
                            | Action::SetStatusEntries(_)
                            | Action::SetSelectedPath(_)
                            | Action::RefreshStatus
                            | Action::ToggleDiffMode
                            | Action::SelectConflict(_)
                            | Action::OpenConflictInEditor(_)
                    ) {
                        app.component_manager.sync_status_selection(&mut app.state);
                    }
                    
                    // 9. Update workflow context when state changes
                    if matches!(
                        action,
                        Action::SetStatusEntries(_)
                            | Action::RefreshStatus
                            | Action::RebaseContinue
                            | Action::RebaseAbort
                            | Action::RebaseSkip
                            | Action::StartCommit
                            | Action::CancelCommit
                            | Action::ShowRebaseTodo
                            | Action::ShowConflictsGuided
                            | Action::CherryPickCommit(_)
                            | Action::CherryPickSelected
                            | Action::RevertCommit(_)
                            | Action::RevertSelected
                    ) {
                        app.state = reducer(app.state.clone(), Action::UpdateWorkflowContext);
                    }
                }
                Ok(None) => {}
                Err(e) => {
                    tracing::error!(error = %e, "Event handling failed");
                }
            }
        }
    }

    Ok(())
}

/// Initialize all data sources during application startup.
/// 
/// These operations are non-critical and failures are logged but don't block startup.
/// This allows the application to start even if some Git operations fail (e.g., 
/// in a corrupted repository or network issues).
fn initialize_data_sources(app: &mut Application) {
    let data_sources = [
        ("branches", Action::RefreshBranches),
        ("commits", Action::RefreshCommits),
        ("stashes", Action::RefreshStashes),
        ("tags", Action::RefreshTags),
        ("reflog", Action::RefreshReflog),
    ];
    
    for (source_name, action) in data_sources {
        if let Err(e) = app.component_manager.update(action, &mut app.state) {
            // Log with context but don't fail - these are non-critical startup operations
            tracing::warn!(
                source = source_name,
                error = %e,
                "Failed to initialize {} during startup (non-critical, continuing)",
                source_name
            );
        }
    }
}

fn preflight_amend(state: &crate::app::AppState) -> Result<(), String> {
    if let Some(ctx) = &state.workflow_context {
        match ctx.state {
            WorkflowState::RebaseInProgress
            | WorkflowState::CherryPickInProgress
            | WorkflowState::MergeInProgress
            | WorkflowState::Conflicts => {
                return Err("Cannot amend while rebase/cherry-pick/merge/conflicts are in progress".to_string());
            }
            _ => {}
        }
    }

    let has_unstaged_or_conflict = state
        .status_entries
        .iter()
        .any(|e| e.unstaged || e.conflict);
    if has_unstaged_or_conflict {
        return Err("Amend requires a clean working tree (no unstaged or conflict files)".to_string());
    }

    let has_untracked = state
        .status_entries
        .iter()
        .any(|e| !e.staged && !e.unstaged && !e.conflict);
    if has_untracked {
        return Err("Amend requires no untracked files; clean or add them first".to_string());
    }

    Ok(())
}

fn preflight_pick_revert(state: &crate::app::AppState, op: &str) -> Result<(), String> {
    if let Some(ctx) = &state.workflow_context {
        match ctx.state {
            WorkflowState::RebaseInProgress
            | WorkflowState::CherryPickInProgress
            | WorkflowState::MergeInProgress
            | WorkflowState::Conflicts => {
                return Err(format!("{op} blocked: rebase/cherry-pick/merge/conflicts in progress"));
            }
            _ => {}
        }
    }

    let has_dirty = state
        .status_entries
        .iter()
        .any(|e| e.staged || e.unstaged || e.conflict || (!e.staged && !e.unstaged && !e.conflict));
    if has_dirty {
        return Err(format!("{op} requires a clean working tree (no staged/unstaged/untracked/conflict files)"));
    }

    Ok(())
}

fn preflight_rebase_builder(state: &crate::app::AppState) -> Result<(), String> {
    let in_rebase = matches!(
        state
            .workflow_context
            .as_ref()
            .map(|ctx| ctx.state.clone()),
        Some(WorkflowState::RebaseInProgress)
    );
    if let Some(ctx) = &state.workflow_context {
        match ctx.state {
            WorkflowState::CherryPickInProgress
            | WorkflowState::MergeInProgress
            | WorkflowState::Conflicts => {
                return Err("Rebase builder blocked: cherry-pick/merge/conflicts in progress".to_string());
            }
            _ => {}
        }
    }

    if !in_rebase {
        let dirty = state
            .status_entries
            .iter()
            .any(|e| e.staged || e.unstaged || e.conflict || (!e.staged && !e.unstaged && !e.conflict));
        if dirty {
            return Err("Rebase builder requires a clean working tree (no staged/unstaged/untracked/conflicts)".to_string());
        }
    }
    Ok(())
}

fn validate_amend_input(state: &crate::app::AppState) -> Result<(), String> {
    if state.commit_input.trim().is_empty() {
        return Err("Amend requires a non-empty commit message".to_string());
    }
    if state.amend_author_name.trim().is_empty() {
        return Err("Amend requires author name".to_string());
    }
    if state.amend_author_email.trim().is_empty() {
        return Err("Amend requires author email".to_string());
    }
    Ok(())
}