//! All other status-related actions.
//!
//! This module handles all remaining status reducer actions that don't fit
//! into the other specialized modules.

use crate::app::{actions::Action, state::AppState};
use crate::app::state::DiffMode;
use crate::app::state::FocusPane;

/// Handle all other status-related actions.
pub fn handle_other(state: &mut AppState, action: &Action) -> Option<AppState> {
    match action {
        // Rebase todo actions
        Action::ShowRebaseTodo => {
            state.show_rebase_todo = true;
            state.rebase_todo_selected = 0;
            state.command_palette = false;
            state.rebase_todo_editing = false;
            state.rebase_todo_edit_buffer.clear();
        }
        Action::HideRebaseTodo => {
            state.show_rebase_todo = false;
            state.rebase_todo_editing = false;
            state.rebase_todo_edit_buffer.clear();
        }
        Action::RebaseTodoUp => {
            if state.rebase_todo_selected > 0 {
                state.rebase_todo_selected -= 1;
            }
        }
        Action::RebaseTodoDown => {
            let max_idx = state.rebase_todo.len().saturating_sub(1);
            if state.rebase_todo_selected < max_idx {
                state.rebase_todo_selected += 1;
            }
        }
        Action::SetRebaseTodo(lines) => {
            state.rebase_todo = lines.clone();
            state.rebase_todo_selected = 0;
            state.rebase_todo_original = lines.clone();
            state.rebase_todo_dirty = false;
        }
        Action::SetRebaseTodoWithPath(lines, path) => {
            state.rebase_todo = lines.clone();
            state.rebase_todo_selected = 0;
            state.rebase_todo_original = lines.clone();
            state.rebase_todo_path = Some(path.clone());
            state.rebase_todo_dirty = false;
        }
        Action::SetRebaseTodoPath(path) => {
            state.rebase_todo_path = Some(path.clone());
        }
        Action::StartRebaseTodoEdit(idx, text) => {
            state.rebase_todo_selected = (*idx).min(state.rebase_todo.len().saturating_sub(1));
            state.rebase_todo_editing = true;
            state.rebase_todo_edit_buffer = text.clone();
        }
        Action::RebaseTodoEditBuffer(text) => {
            state.rebase_todo_editing = true;
            state.rebase_todo_edit_buffer = text.clone();
        }
        Action::RebaseTodoToggleKind(idx, kind) => {
            if let Some(line) = state.rebase_todo.get_mut(*idx) {
                let mut parts = line.splitn(2, char::is_whitespace);
                let _ = parts.next();
                let tail = parts.next().unwrap_or("").trim_start();
                let new_line = if tail.is_empty() {
                    kind.clone()
                } else {
                    format!("{} {}", kind, tail)
                };
                *line = new_line;
                state.rebase_todo_dirty = true;
            }
        }
        Action::RebaseTodoRewriteLine(idx, text) => {
            if let Some(line) = state.rebase_todo.get_mut(*idx) {
                *line = text.clone();
                state.rebase_todo_dirty = true;
            }
            state.rebase_todo_editing = false;
            state.rebase_todo_edit_buffer.clear();
        }
        Action::SaveRebaseTodo => {
            // handled by command layer
            return None;
        }
        
        // Merge/remote actions
        Action::SetOpStatus(text) => {
            state.op_status = text.clone();
        }
        Action::AppendOpLog(line) => {
            const MAX_LOG: usize = 20;
            state.op_log.push_back(line.clone());
            while state.op_log.len() > MAX_LOG {
                state.op_log.pop_front();
            }
        }
        Action::ToggleOpLog => {
            state.show_op_log = !state.show_op_log;
        }
        Action::ShowOpLog => {
            state.show_op_log = true;
        }
        Action::ToggleMergeNotifier => {
            state.merge_notifier_enabled = !state.merge_notifier_enabled;
        }
        Action::SetMergeBaseBranch(name) => {
            state.merge_base_branch = name.clone();
        }
        Action::SetMergeAhead(ahead) => {
            state.merge_notifier_ahead = *ahead;
        }
        Action::SetMergeInterval(secs) => {
            state.merge_notifier_interval_secs = *secs;
        }
        Action::AppendMergeLog(line) => {
            const MAX_LOG: usize = 20;
            state.merge_notifier_log.push_back(line.clone());
            while state.merge_notifier_log.len() > MAX_LOG {
                state.merge_notifier_log.pop_front();
            }
        }
        Action::ToggleMergeLog => {
            state.show_merge_log = !state.show_merge_log;
        }
        Action::ShowMergeBasePicker => {
            state.merge_base_picker = true;
            state.merge_base_selected = 0;
        }
        Action::HideMergeBasePicker => {
            state.merge_base_picker = false;
        }
        Action::SetMergeBaseCandidates(list) => {
            state.merge_base_candidates = list.clone();
            state.merge_base_selected = 0;
        }
        Action::MergeBaseUp => {
            if state.merge_base_selected > 0 {
                state.merge_base_selected -= 1;
            }
        }
        Action::MergeBaseDown => {
            let max_idx = state.merge_base_candidates.len().saturating_sub(1);
            if state.merge_base_selected < max_idx {
                state.merge_base_selected += 1;
            }
        }
        Action::ApplyMergeBase(name) => {
            state.merge_base_branch = name.clone();
            state.merge_base_picker = false;
        }
        Action::TogglePushFFOnly => {
            state.push_ff_only_enforce = !state.push_ff_only_enforce;
            state.feedback_message = Some(if state.push_ff_only_enforce { "Push ff-only guard ON" } else { "Push ff-only guard OFF" }.into());
        }
        Action::ListMergedBranches => {
            // No state mutation; handled via command.
            return None;
        }
        Action::RequestPruneMergedBranches => {
            state.prune_merged_pending = true;
            state.feedback_message = Some("Confirm prune merged branches: press again to execute".into());
        }
        Action::PruneMergedBranches => {
            state.prune_merged_pending = false;
        }
        Action::RequestRemotePrune(remote) => {
            state.remote_prune_pending = true;
            state.feedback_message = Some(format!("Confirm remote prune {}: press again to execute", remote));
        }
        Action::RemotePrune(_) => {
            state.remote_prune_pending = false;
        }
        Action::ShowLogBrief | Action::ShowLogStats => {
            // handled by command layer; no state mutation here
            return None;
        }
        Action::ToggleFFOnly => {
            state.pull_ff_only = !state.pull_ff_only;
            state.feedback_message = Some(if state.pull_ff_only { "Pull ff-only ON" } else { "Pull ff-only OFF" }.into());
        }
        Action::ToggleAutostash => {
            state.pull_autostash = !state.pull_autostash;
            state.feedback_message = Some(if state.pull_autostash { "Autostash ON" } else { "Autostash OFF" }.into());
        }
        Action::ToggleAutoFetch => {
            state.auto_fetch_enabled = !state.auto_fetch_enabled;
            state.feedback_message = Some(if state.auto_fetch_enabled { "Auto-fetch ON" } else { "Auto-fetch OFF" }.into());
            if !state.auto_fetch_enabled {
                state.auto_fetch_last_check = None;
            }
        }
        Action::SetAutoFetchInterval(secs) => {
            state.auto_fetch_interval_secs = *secs;
            state.feedback_message = Some(format!("Auto-fetch interval {}s", secs));
        }
        Action::SetAutoFetchRemote(remote) => {
            state.auto_fetch_remote = remote.clone();
            state.feedback_message = Some(format!("Auto-fetch remote {}", remote));
        }
        Action::ShowPrHelper => {
            state.pr_helper = true;
            state.pr_selected = 0;
            state.command_palette = false;
        }
        Action::HidePrHelper => {
            state.pr_helper = false;
        }
        Action::PrHelperUp => {
            if state.pr_selected > 0 {
                state.pr_selected -= 1;
            }
        }
        Action::PrHelperDown => {
            let max_idx = state.pr_list.len().saturating_sub(1);
            if state.pr_selected < max_idx {
                state.pr_selected += 1;
            }
        }
        Action::SetPrList(list) => {
            state.pr_list = list.clone();
            state.pr_selected = 0;
        }
        Action::SetPullTimeout(secs) => {
            state.pull_timeout_secs = *secs;
            state.feedback_message = Some(format!("Pull timeout set to {}s", secs));
        }
        Action::SetRebaseTimeout(secs) => {
            state.rebase_timeout_secs = *secs;
            state.feedback_message = Some(format!("Rebase timeout set to {}s", secs));
        }
        Action::Pull | Action::PullRebase => {
            // passthrough, handled by command layer
            return None;
        }
        
        // Filtering actions
        Action::FilterConflictsOnly => {
            state.conflicts_only = true;
        }
        Action::ClearConflictFilter => {
            state.conflicts_only = false;
        }
        Action::StartFilter => {
            state.filter_mode = true;
            state.status_filter.clear();
        }
        Action::CancelFilter => {
            state.filter_mode = false;
            state.status_filter.clear();
        }
        Action::SetStatusFilter(s) => {
            state.status_filter = s.clone();
        }
        Action::SetFilterMode(on) => {
            state.filter_mode = *on;
        }
        Action::FilterInputChar(c) => {
            state.status_filter.push(*c);
        }
        Action::FilterInputBackspace => {
            state.status_filter.pop();
        }
        
        // Conflict actions
        Action::ShowConflictsPopup => {
            state.conflicts_popup = true;
            state.conflicts_popup_selected = 0;
            state.conflicts_only = true;
            state.command_palette = false;
        }
        Action::HideConflictsPopup => {
            state.conflicts_popup = false;
        }
        Action::ConflictsPopupUp => {
            if state.conflicts_popup_selected > 0 {
                state.conflicts_popup_selected -= 1;
            }
        }
        Action::ConflictsPopupDown => {
            let total_conflicts = state
                .status_entries
                .iter()
                .filter(|e| e.conflict)
                .count()
                .saturating_sub(1);
            if state.conflicts_popup_selected < total_conflicts {
                state.conflicts_popup_selected += 1;
            }
        }
        Action::SelectConflict(path) => {
            if let Some((idx, _)) = state
                .status_entries
                .iter()
                .enumerate()
                .find(|(_, e)| &e.path == path)
            {
                state.status_selected = idx;
                state.status_selected_path = path.clone();
            }
            state.conflicts_only = true;
            state.conflicts_popup = false;
        }
        Action::OpenConflictInEditor(path) => {
            if let Some((idx, _)) = state
                .status_entries
                .iter()
                .enumerate()
                .find(|(_, e)| &e.path == path)
            {
                state.status_selected = idx;
                state.status_selected_path = path.clone();
            }
            state.conflicts_only = true;
            state.conflicts_popup = false;
        }
        Action::ShowConflictsGuided => {
            state.conflicts_guided = true;
            state.conflicts_guided_selected = 0;
            state.conflicts_only = true;
            state.command_palette = false;
        }
        Action::HideConflictsGuided => {
            state.conflicts_guided = false;
        }
        Action::ConflictsGuidedUp => {
            if state.conflicts_guided_selected > 0 {
                state.conflicts_guided_selected -= 1;
            }
        }
        Action::ConflictsGuidedDown => {
            let total_conflicts = state
                .status_entries
                .iter()
                .filter(|e| e.conflict)
                .count()
                .saturating_sub(1);
            if state.conflicts_guided_selected < total_conflicts {
                state.conflicts_guided_selected += 1;
            }
        }
        Action::OpenGuidedConflictInEditor => {
            let conflicts: Vec<&crate::git::parsers::status::StatusEntry> = state
                .status_entries
                .iter()
                .filter(|e| e.conflict)
                .collect();
            let idx = state.conflicts_guided_selected.min(conflicts.len().saturating_sub(1));
            if let Some(entry) = conflicts.get(idx) {
                if let Some((sel_idx, _)) = state
                    .status_entries
                    .iter()
                    .enumerate()
                    .find(|(_, e)| e.path == entry.path)
                {
                    state.status_selected = sel_idx;
                    state.status_selected_path = entry.path.clone();
                }
            }
            state.conflicts_guided = false;
        }
        
        // Other status actions
        Action::SetSelectedPath(path) => {
            if state.status_selected_path != *path {
                state.last_diff.clear();
                state.last_diff_path.clear();
                state.last_diff_truncated = false;
                state.last_diff_total = 0;
            }
            state.status_selected_path = path.clone();
        }
        Action::SetCurrentBranch(branch) => {
            state.current_branch = branch.clone();
        }
        Action::ToggleDiffMode => {
            state.diff_mode = match state.diff_mode {
                DiffMode::Working => DiffMode::Staged,
                DiffMode::Staged => DiffMode::Working,
            };
        }
        Action::SetLastRefreshed(ts) => {
            state.last_refreshed = ts.clone();
        }
        Action::SetRefreshing(on) => {
            state.refreshing = *on;
            if *on {
                // Track when refresh starts (for potential future analytics, not rate limiting)
                state.last_refresh_time = Some(std::time::Instant::now());
            }
        }
        Action::RequestRefreshStatus => {
            // Show immediate visual feedback - this prevents abuse naturally
            // No artificial constraints - users can refresh whenever, but visual feedback
            // makes it clear when a refresh is in progress
            state.refreshing = true;
            state.last_refresh_time = Some(std::time::Instant::now());
            // This action will be converted to RefreshStatus in event loop
        }
        Action::SetDetailScrollX(x) => {
            state.detail_scroll_x = *x;
        }
        Action::ToggleFileSelect(path) => {
            if state.selected_files.contains(path) {
                state.selected_files.remove(path);
            } else {
                state.selected_files.insert(path.clone());
            }
        }
        Action::ClearFileSelects => {
            state.selected_files.clear();
        }
        Action::SetFileScroll(path, offs) => {
            state.file_scrolls.insert(path.clone(), *offs);
        }
        Action::ResetFileScroll(path) => {
            state.file_scrolls.remove(path);
        }
        Action::SetBranchAheadBehind(name, ahead, behind) => {
            for b in state.branches.iter_mut() {
                if b.name == *name {
                    b.name = if *ahead != 0 || *behind != 0 {
                        format!("{} (+{} / -{})", name, ahead, behind)
                    } else {
                        name.clone()
                    };
                    b.ahead = Some(*ahead);
                    b.behind = Some(*behind);
                }
            }
        }
        Action::FocusNext => {
            state.focus = match state.focus {
                FocusPane::Status => FocusPane::Branches,
                FocusPane::Branches => FocusPane::Log,
                FocusPane::Log => FocusPane::Stash,
                FocusPane::Stash => FocusPane::Tags,
                FocusPane::Tags => FocusPane::Reflog,
                FocusPane::Reflog => FocusPane::Status,
            };
        }
        Action::FocusPrev => {
            state.focus = match state.focus {
                FocusPane::Status => FocusPane::Reflog,
                FocusPane::Branches => FocusPane::Status,
                FocusPane::Log => FocusPane::Branches,
                FocusPane::Stash => FocusPane::Log,
                FocusPane::Tags => FocusPane::Stash,
                FocusPane::Reflog => FocusPane::Tags,
            };
        }
        _ => return None,
    }
    Some(state.clone())
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::app::actions::Action;
    use crate::git::parsers::status::StatusEntry;

    fn conflict_entry(path: &str) -> StatusEntry {
        StatusEntry {
            staged: false,
            unstaged: true,
            conflict: true,
            path: path.to_string(),
        }
    }

    #[test]
    fn show_conflicts_popup_enables_flags() {
        let mut state = crate::app::state::AppState::new();
        let result = handle_other(&mut state, &Action::ShowConflictsPopup);
        assert!(result.is_some());
        assert!(state.conflicts_popup);
        assert!(state.conflicts_only);
        assert_eq!(state.conflicts_popup_selected, 0);
    }

    #[test]
    fn conflicts_popup_navigation_clamps() {
        let mut state = crate::app::state::AppState::new();
        state.status_entries = vec![conflict_entry("a"), conflict_entry("b")];
        let _ = handle_other(&mut state, &Action::ConflictsPopupDown);
        assert_eq!(state.conflicts_popup_selected, 1);
        let _ = handle_other(&mut state, &Action::ConflictsPopupDown);
        assert_eq!(state.conflicts_popup_selected, 1);
    }

    #[test]
    fn select_conflict_sets_selection_and_closes() {
        let mut state = crate::app::state::AppState::new();
        state.status_entries = vec![conflict_entry("a"), conflict_entry("b")];
        let _ = handle_other(&mut state, &Action::SelectConflict("b".into()));
        assert!(!state.conflicts_popup);
        assert_eq!(state.status_selected_path, "b");
        assert_eq!(state.status_selected, 1);
    }

    #[test]
    fn append_merge_log_caps_size() {
        let mut state = crate::app::state::AppState::new();
        for i in 0..25 {
            let _ = handle_other(&mut state, &Action::AppendMergeLog(format!("line {i}")));
        }
        assert_eq!(state.merge_notifier_log.len(), 20);
        assert_eq!(state.merge_notifier_log.front().unwrap(), "line 5");
    }

    #[test]
    fn remote_prune_request_and_reset() {
        let mut state = crate::app::state::AppState::new();
        let _ = handle_other(&mut state, &Action::RequestRemotePrune("origin".into()));
        assert!(state.remote_prune_pending);
        let _ = handle_other(&mut state, &Action::RemotePrune("origin".into()));
        assert!(!state.remote_prune_pending);
    }

    #[test]
    fn set_pull_and_rebase_timeouts() {
        let mut state = crate::app::state::AppState::new();
        let _ = handle_other(&mut state, &Action::SetPullTimeout(45));
        assert_eq!(state.pull_timeout_secs, 45);
        let _ = handle_other(&mut state, &Action::SetRebaseTimeout(50));
        assert_eq!(state.rebase_timeout_secs, 50);
    }

    #[test]
    fn toggle_push_ff_guard() {
        let mut state = crate::app::state::AppState::new();
        let _ = handle_other(&mut state, &Action::TogglePushFFOnly);
        assert!(!state.push_ff_only_enforce);
        let _ = handle_other(&mut state, &Action::TogglePushFFOnly);
        assert!(state.push_ff_only_enforce);
    }

    #[test]
    fn rebase_todo_selection_bounds() {
        let mut state = crate::app::state::AppState::new();
        let _ = handle_other(&mut state, &Action::SetRebaseTodo(vec!["pick a".into(), "edit b".into()]));
        let _ = handle_other(&mut state, &Action::RebaseTodoDown);
        assert_eq!(state.rebase_todo_selected, 1);
        let _ = handle_other(&mut state, &Action::RebaseTodoDown);
        assert_eq!(state.rebase_todo_selected, 1);
        let _ = handle_other(&mut state, &Action::RebaseTodoUp);
        assert_eq!(state.rebase_todo_selected, 0);
        let _ = handle_other(&mut state, &Action::RebaseTodoUp);
        assert_eq!(state.rebase_todo_selected, 0);
    }

    #[test]
    fn conflicts_guided_bounds() {
        let mut state = crate::app::state::AppState::new();
        state.status_entries = vec![
            conflict_entry("a"),
            conflict_entry("b"),
            conflict_entry("c"),
        ];
        let _ = handle_other(&mut state, &Action::ShowConflictsGuided);
        assert!(state.conflicts_guided);
        let _ = handle_other(&mut state, &Action::ConflictsGuidedDown);
        let _ = handle_other(&mut state, &Action::ConflictsGuidedDown);
        let _ = handle_other(&mut state, &Action::ConflictsGuidedDown);
        assert_eq!(state.conflicts_guided_selected, 2);
        let _ = handle_other(&mut state, &Action::ConflictsGuidedUp);
        assert_eq!(state.conflicts_guided_selected, 1);
    }

    #[test]
    fn rebase_todo_toggle_sets_dirty() {
        let mut state = crate::app::state::AppState::new();
        let _ = handle_other(&mut state, &Action::SetRebaseTodo(vec!["pick a".into()]));
        let _ = handle_other(&mut state, &Action::RebaseTodoToggleKind(0, "reword".into()));
        assert_eq!(state.rebase_todo[0], "reword a");
        assert!(state.rebase_todo_dirty);
    }

    #[test]
    fn rebase_todo_rewrite_sets_dirty_and_clears_edit() {
        let mut state = crate::app::state::AppState::new();
        state.rebase_todo_editing = true;
        state.rebase_todo_edit_buffer = "pick a".into();
        let _ = handle_other(&mut state, &Action::SetRebaseTodo(vec!["pick a".into()]));
        let _ = handle_other(&mut state, &Action::RebaseTodoRewriteLine(0, "edit b".into()));
        assert_eq!(state.rebase_todo[0], "edit b");
        assert!(state.rebase_todo_dirty);
        assert!(!state.rebase_todo_editing);
    }
}
