use crate::app::{actions::Action, state::AppState};
use crate::app::commit_template;
use crate::app::state::{RebaseAction, RebaseEntry};

pub fn reduce(mut state: AppState, action: &Action) -> Option<AppState> {
    match action {
        Action::SetCommits(commits) => {
            state.commits = commits.clone();
            if state.commit_selected >= state.commits.len() && !state.commits.is_empty() {
                state.commit_selected = state.commits.len() - 1;
            }
        }
        Action::CommitUp => {
            if state.commit_selected > 0 {
                state.commit_selected -= 1;
            }
        }
        Action::CommitDown => {
            if !state.commits.is_empty() && state.commit_selected < state.commits.len() - 1 {
                state.commit_selected += 1;
            }
        }
        Action::SetCommitDetail(detail) => {
            state.commit_detail = detail.clone();
        }
        Action::ShowLogActionMenu(hash) => {
            state.log_action_menu = true;
            state.log_action_selected = 0;
            state.log_action_commit_hash = Some(hash.clone());
        }
        Action::HideLogActionMenu => {
            state.log_action_menu = false;
            state.log_action_selected = 0;
            state.log_action_commit_hash = None;
        }
        Action::LogActionMenuUp => {
            if state.log_action_selected > 0 {
                state.log_action_selected -= 1;
            }
        }
        Action::LogActionMenuDown => {
            // Get available actions to determine max index
            // Use the same logic as the event handler to calculate available actions
            use crate::app::actions::LogAction;
            let mut actions = vec![
                LogAction::ShowDetail,
                LogAction::CherryPick,
                LogAction::Revert,
                LogAction::ResetSoft,
                LogAction::ResetMixed,
                LogAction::ResetHard,
                LogAction::CreateBranch,
                LogAction::CreateTag,
            ];
            
            // Check if this is HEAD (first commit in list)
            if let Some(hash) = &state.log_action_commit_hash {
                if let Some(first_commit) = state.commits.first() {
                    if first_commit.hash == *hash {
                        // Insert Amend at position 3 (after ShowDetail, CherryPick, Revert)
                        actions.insert(3, LogAction::Amend);
                    }
                }
            }
            
            let max_idx = actions.len().saturating_sub(1);
            if state.log_action_selected < max_idx {
                state.log_action_selected += 1;
            }
        }
        Action::ExecuteLogAction(_) => {
            // Handled by component/command layer
            state.log_action_menu = false;
            state.log_action_selected = 0;
            state.log_action_commit_hash = None;
        }
        Action::ShowConfirm(action, message) => {
            state.confirm_action = Some((**action).clone());
            state.confirm_message = Some(message.clone());
        }
        Action::ConfirmCancel => {
            state.confirm_action = None;
            state.confirm_message = None;
        }
        Action::ToggleLogGraph => {
            state.log_graph = !state.log_graph;
        }
        Action::SetLogGraphText(text) => {
            state.log_graph_text = text.clone();
        }
        Action::SetFileHistoryPath(path) => {
            state.file_history_path = path.clone();
        }
        Action::StartCommit => {
            state.commit_mode = true;
            state.amend_mode = false;
            // Apply template if available
            if let Some(ref template) = state.commit_template {
                state.commit_input = commit_template::get_template_preview(template, &state);
            } else {
                state.commit_input.clear();
            }
            state.command_palette = false;
        }
        Action::StartAmend => {
            state.commit_mode = true;
            state.amend_mode = true;
            state.amend_editing_field = Some(crate::app::state::AmendField::Message); // Start editing message by default
            // Load last commit info will be triggered by component
            state.command_palette = false;
        }
        Action::LoadLastCommitInfo => {
            // This will be handled by component to fetch from Git
            // Component will dispatch SetLastCommitInfo when done
        }
        Action::SetLastCommitInfo(info) => {
            state.last_commit_info = info.clone();
            if let Some(ref last_info) = info {
                // Pre-fill commit message and author info
                state.commit_input = last_info.message.clone();
                state.amend_author_name = last_info.author_name.clone();
                state.amend_author_email = last_info.author_email.clone();
            }
        }
        Action::SetAmendAuthorName(name) => {
            state.amend_author_name = name.clone();
        }
        Action::SetAmendAuthorEmail(email) => {
            state.amend_author_email = email.clone();
        }
        Action::SetAmendEditingField(field) => {
            state.amend_editing_field = *field;
        }
        Action::CancelCommit => {
            state.commit_mode = false;
            state.amend_mode = false;
            state.commit_input.clear();
            state.amend_author_name.clear();
            state.amend_author_email.clear();
            state.last_commit_info = None;
            state.amend_editing_field = None;
        }
        Action::SetCommitInput(msg) => {
            state.commit_input = msg.clone();
        }
        Action::CommitInputChar(c) => {
            state.commit_input.push(*c);
        }
        Action::CommitInputBackspace => {
            state.commit_input.pop();
        }
        Action::SetFeedback(msg) => {
            state.feedback_message = msg.clone();
        }
        Action::LogToggleSelect => {
            if let Some(commit) = state.commits.get(state.commit_selected) {
                if state.log_selected_hashes.contains(&commit.hash) {
                    state.log_selected_hashes.remove(&commit.hash);
                } else {
                    state.log_selected_hashes.insert(commit.hash.clone());
                }
            }
        }
        Action::RebaseSetBase(hash) => {
            state.rebase_base = Some(hash.clone());
        }
        Action::StartInteractiveRebase(commits) => {
            state.rebase_editor_open = true;
            state.rebase_dirty = true;
            state.rebase_target_only = commits.len() == 1;
            state.rebase_base = commits.last().cloned();
            state.rebase_entries = commits
                .iter()
                .map(|h| {
                    if let Some(c) = state.commits.iter().find(|c| c.hash == *h) {
                        RebaseEntry {
                            hash: c.hash.clone(),
                            short_hash: c.short_hash.clone(),
                            message: c.message.clone(),
                            action: RebaseAction::Pick,
                        }
                    } else {
                        RebaseEntry {
                            hash: h.clone(),
                            short_hash: h.chars().take(7).collect(),
                            message: "(not loaded)".to_string(),
                            action: RebaseAction::Pick,
                        }
                    }
                })
                .collect();
            state.rebase_cursor = 0;
        }
        Action::RebaseCancel => {
            state.rebase_editor_open = false;
            state.rebase_entries.clear();
            state.rebase_cursor = 0;
            state.rebase_dirty = false;
            state.reword_message = None;
            state.reword_modal_open = false;
        }
        Action::RebaseNext => {
            if !state.rebase_entries.is_empty() {
                state.rebase_cursor = (state.rebase_cursor + 1).min(state.rebase_entries.len().saturating_sub(1));
            }
        }
        Action::RebasePrev => {
            if !state.rebase_entries.is_empty() && state.rebase_cursor > 0 {
                state.rebase_cursor -= 1;
            }
        }
        Action::RebaseMoveUp => {
            if state.rebase_cursor > 0 {
                let idx = state.rebase_cursor;
                state.rebase_entries.swap(idx, idx - 1);
                state.rebase_cursor -= 1;
                state.rebase_dirty = true;
            }
        }
        Action::RebaseMoveDown => {
            if state.rebase_cursor + 1 < state.rebase_entries.len() {
                let idx = state.rebase_cursor;
                state.rebase_entries.swap(idx, idx + 1);
                state.rebase_cursor += 1;
                state.rebase_dirty = true;
            }
        }
        Action::RebaseCycleAction => {
            if let Some(entry) = state.rebase_entries.get_mut(state.rebase_cursor) {
                entry.action = match entry.action {
                    RebaseAction::Pick => RebaseAction::Reword,
                    RebaseAction::Reword => RebaseAction::Edit,
                    RebaseAction::Edit => RebaseAction::Squash,
                    RebaseAction::Squash => RebaseAction::Fixup,
                    RebaseAction::Fixup => RebaseAction::Drop,
                    RebaseAction::Drop => RebaseAction::Pick,
                };
                state.rebase_dirty = true;
            }
        }
        Action::RebaseSetAction(action) => {
            if let Some(entry) = state.rebase_entries.get_mut(state.rebase_cursor) {
                entry.action = *action;
                state.rebase_dirty = true;
            }
        }
        Action::RebaseLoadTodo(lines) => {
            state.rebase_entries = lines
                .iter()
                .filter_map(|line| {
                    if line.trim_start().starts_with('#') {
                        return None;
                    }
                    let parts: Vec<&str> = line.split_whitespace().collect();
                    if parts.len() >= 2 {
                        let verb = parts[0];
                        let hash = parts[1].to_string();
                        let message = line
                            .split_once(parts[1])
                            .map(|(_, msg)| msg.trim().to_string())
                            .unwrap_or_default();
                        let action = match verb {
                            "pick" => RebaseAction::Pick,
                            "reword" => RebaseAction::Reword,
                            "edit" => RebaseAction::Edit,
                            "squash" => RebaseAction::Squash,
                            "fixup" => RebaseAction::Fixup,
                            "drop" => RebaseAction::Drop,
                            _ => RebaseAction::Pick,
                        };
                        Some(RebaseEntry {
                            hash: hash.clone(),
                            short_hash: hash.chars().take(7).collect(),
                            message,
                            action,
                        })
                    } else {
                        None
                    }
                })
                .collect();
            state.rebase_cursor = 0;
            state.rebase_dirty = false;
            state.rebase_editor_open = true;
            state.rebase_base = state.rebase_entries.last().map(|e| e.hash.clone());
        }
        Action::RebaseSaveAndRun => {
            state.rebase_editor_open = false;
            state.rebase_dirty = false;
        }
        Action::SetRewordMessage(msg) => {
            state.reword_message = Some(msg.clone());
        }
        Action::SaveAndCloseReword(msg) => {
            state.reword_message = Some(msg.clone());
            state.reword_modal_open = false;
        }
        Action::SetRewordAuthorName(name) => {
            state.reword_author_name = Some(name.clone());
        }
        Action::SetRewordAuthorEmail(email) => {
            state.reword_author_email = Some(email.clone());
        }
        Action::FocusAndClearRewordAuthorName => {
            state.reword_focus_field = crate::app::state::RewordField::AuthorName;
            state.reword_author_name = Some(String::new());
        }
        Action::FocusAndClearRewordAuthorEmail => {
            state.reword_focus_field = crate::app::state::RewordField::AuthorEmail;
            state.reword_author_email = Some(String::new());
        }
        Action::OpenRewordModal(msg) => {
            state.reword_message = Some(msg.clone());
            state.reword_modal_open = true;
            state.reword_focus_field = crate::app::state::RewordField::Message;
        }
        Action::SetRewordFocus(field) => {
            state.reword_focus_field = *field;
        }
        Action::CloseRewordModal => {
            state.reword_modal_open = false;
            state.reword_focus_field = crate::app::state::RewordField::Message;
        }
        Action::SetEditMessage(msg) => {
            state.edit_message = Some(msg.clone());
        }
        Action::SaveAndCloseEdit(msg) => {
            state.edit_message = Some(msg.clone());
            state.edit_modal_open = false;
        }
        Action::SetEditAuthorName(name) => {
            state.edit_author_name = Some(name.clone());
        }
        Action::SetEditAuthorEmail(email) => {
            state.edit_author_email = Some(email.clone());
        }
        Action::OpenEditModal(msg) => {
            state.edit_message = Some(msg.clone());
            state.edit_modal_open = true;
            state.edit_focus_field = crate::app::state::RewordField::Message;
        }
        Action::FocusEditMessage => {
            state.edit_focus_field = crate::app::state::RewordField::Message;
        }
        Action::FocusEditAuthorName => {
            state.edit_focus_field = crate::app::state::RewordField::AuthorName;
        }
        Action::FocusEditAuthorEmail => {
            state.edit_focus_field = crate::app::state::RewordField::AuthorEmail;
        }
        Action::FocusAndClearEditAuthorName => {
            state.edit_focus_field = crate::app::state::RewordField::AuthorName;
            state.edit_author_name = Some(String::new());
        }
        Action::FocusAndClearEditAuthorEmail => {
            state.edit_focus_field = crate::app::state::RewordField::AuthorEmail;
            state.edit_author_email = Some(String::new());
        }
        Action::CloseEditModal => {
            state.edit_modal_open = false;
            state.edit_focus_field = crate::app::state::RewordField::Message;
        }
        Action::RebaseAbortFromBuilder => {
            // no-op here; handled in event loop
        }
        _ => return None,
    }
    Some(state)
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::app::actions::LogAction;
    use crate::app::state::CommitEntry;
    use crate::app::state::RebaseAction;

    fn state_with_commits(head_hash: &str, other_hash: &str) -> AppState {
        let mut state = AppState::new();
        state.commits = vec![
            CommitEntry {
                hash: head_hash.to_string(),
                short_hash: head_hash[..7.min(head_hash.len())].to_string(),
                message: "head".to_string(),
                author: "a".to_string(),
            },
            CommitEntry {
                hash: other_hash.to_string(),
                short_hash: other_hash[..7.min(other_hash.len())].to_string(),
                message: "other".to_string(),
                author: "b".to_string(),
            },
        ];
        state
    }

    #[test]
    fn show_and_hide_log_action_menu() {
        let state = AppState::new();
        let state = reduce(state, &Action::ShowLogActionMenu("abcd".into())).unwrap();
        assert!(state.log_action_menu);
        assert_eq!(state.log_action_selected, 0);
        assert_eq!(state.log_action_commit_hash.as_deref(), Some("abcd"));

        let state = reduce(state, &Action::HideLogActionMenu).unwrap();
        assert!(!state.log_action_menu);
        assert_eq!(state.log_action_selected, 0);
        assert!(state.log_action_commit_hash.is_none());
    }

    #[test]
    fn log_action_menu_down_bounds_when_head() {
        let state = state_with_commits("headhash", "otherhash");
        let mut state = reduce(state, &Action::ShowLogActionMenu("headhash".into())).unwrap();
        for _ in 0..20 {
            state = reduce(state, &Action::LogActionMenuDown).unwrap();
        }
        // Amend included => 9 actions => max index 8
        assert_eq!(state.log_action_selected, 8);
    }

    #[test]
    fn log_action_menu_down_bounds_when_not_head() {
        let state = state_with_commits("headhash", "otherhash");
        let mut state = reduce(state, &Action::ShowLogActionMenu("otherhash".into())).unwrap();
        for _ in 0..20 {
            state = reduce(state, &Action::LogActionMenuDown).unwrap();
        }
        // No amend => 8 actions => max index 7
        assert_eq!(state.log_action_selected, 7);
    }

    #[test]
    fn execute_log_action_resets_menu_state() {
        let state = AppState::new();
        let state = reduce(state, &Action::ShowLogActionMenu("abcd".into())).unwrap();
        let state = reduce(
            state,
            &Action::ExecuteLogAction(LogAction::ShowDetail),
        )
        .unwrap();
        assert!(!state.log_action_menu);
        assert_eq!(state.log_action_selected, 0);
        assert!(state.log_action_commit_hash.is_none());
    }

    #[test]
    fn rebase_builder_starts_and_moves() {
        let mut state = AppState::new();
        state.commits = vec![
            CommitEntry {
                hash: "h1".into(),
                short_hash: "h1".into(),
                message: "c1".into(),
                author: "a".into(),
            },
            CommitEntry {
                hash: "h2".into(),
                short_hash: "h2".into(),
                message: "c2".into(),
                author: "a".into(),
            },
        ];
        let state = reduce(state, &Action::StartInteractiveRebase(vec!["h1".into(), "h2".into()])).unwrap();
        assert!(state.rebase_editor_open);
        assert_eq!(state.rebase_entries.len(), 2);
        let state = reduce(state, &Action::RebaseNext).unwrap();
        assert_eq!(state.rebase_cursor, 1);
        let state = reduce(state, &Action::RebasePrev).unwrap();
        assert_eq!(state.rebase_cursor, 0);
    }

    #[test]
    fn rebase_cycle_and_set_action() {
        let mut state = AppState::new();
        state = reduce(state, &Action::StartInteractiveRebase(vec!["h1".into()])).unwrap();
        let state = reduce(state, &Action::RebaseCycleAction).unwrap();
        assert_eq!(state.rebase_entries[0].action, RebaseAction::Reword);
        let state = reduce(state, &Action::RebaseSetAction(RebaseAction::Fixup)).unwrap();
        assert_eq!(state.rebase_entries[0].action, RebaseAction::Fixup);
    }
}
