//! Rebase commands.
//!
//! Handles interactive rebase operations including continue, abort, skip, and todo management.

use crate::commands::{Command, CommandResult};
use crate::services::GitService;
use crate::app::{AppState, Action, reducer};
use crate::errors::CommandError;
use tracing::instrument;

/// Continue rebase
pub struct RebaseContinueCommand;

impl Command for RebaseContinueCommand {
    #[instrument(skip(self, git, state))]
    fn execute(
        &self,
        git: &GitService,
        state: &AppState,
    ) -> Result<CommandResult, CommandError> {
        let mut new_state = state.clone();
        new_state = reducer(new_state, Action::SetOpStatus(Some("rebase --continue…".into())));
        // Edit is stronger than reword - use edit fields if available, otherwise fall back to reword
        let message = new_state.edit_message.as_ref().or(new_state.reword_message.as_ref());
        let author_name = new_state.edit_author_name.as_ref().or(new_state.reword_author_name.as_ref());
        let author_email = new_state.edit_author_email.as_ref().or(new_state.reword_author_email.as_ref());

        match git.rebase_continue(
            &state.repo_path,
            message.map(|s| s.as_str()),
            author_name.map(|s| s.as_str()),
            author_email.map(|s| s.as_str()),
        ) {
            Ok(_) => {
                new_state = reducer(new_state, Action::SetFeedback(Some("Rebase continued".into())));
                new_state = reducer(new_state, Action::SetRefreshing(true));
                new_state = reducer(new_state, Action::AppendOpLog("rebase --continue".into()));
                new_state.reword_message = None;
                new_state.edit_message = None;
            }
            Err(e) => {
                new_state = reducer(new_state, Action::AppendOpLog(format!("rebase --continue error: {e}")));
                new_state = reducer(new_state, Action::SetStatusError(Some(format!("rebase --continue error: {e}"))));
            }
        }
        new_state = reducer(new_state, Action::SetOpStatus(None));
        Ok(CommandResult::StateUpdate(new_state))
    }
}

/// Abort rebase
pub struct RebaseAbortCommand;

impl Command for RebaseAbortCommand {
    #[instrument(skip(self, git, state))]
    fn execute(
        &self,
        git: &GitService,
        state: &AppState,
    ) -> Result<CommandResult, CommandError> {
        let mut new_state = state.clone();
        new_state = reducer(new_state, Action::SetOpStatus(Some("rebase --abort…".into())));
        match git.rebase_abort(&state.repo_path) {
            Ok(_) => {
                new_state = reducer(new_state, Action::SetFeedback(Some("Rebase aborted".into())));
                new_state = reducer(new_state, Action::SetRefreshing(true));
                new_state = reducer(new_state, Action::AppendOpLog("rebase --abort".into()));
            }
            Err(e) => {
                new_state = reducer(new_state, Action::AppendOpLog(format!("rebase --abort error: {e}")));
                new_state = reducer(new_state, Action::SetStatusError(Some(format!("rebase --abort error: {e}"))));
            }
        }
        new_state = reducer(new_state, Action::SetOpStatus(None));
        Ok(CommandResult::StateUpdate(new_state))
    }
}

/// Skip current rebase step
pub struct RebaseSkipCommand;

impl Command for RebaseSkipCommand {
    #[instrument(skip(self, git, state))]
    fn execute(
        &self,
        git: &GitService,
        state: &AppState,
    ) -> Result<CommandResult, CommandError> {
        let mut new_state = state.clone();
        new_state = reducer(new_state, Action::SetOpStatus(Some("rebase --skip…".into())));
        match git.rebase_skip(&state.repo_path) {
            Ok(_) => {
                new_state = reducer(new_state, Action::SetFeedback(Some("Rebase skip ok".into())));
                new_state = reducer(new_state, Action::AppendOpLog("rebase skip ok".into()));
                new_state = reducer(new_state, Action::SetRefreshing(true));
            }
            Err(e) => {
                new_state = reducer(new_state, Action::AppendOpLog(format!("rebase skip error: {e}")));
                new_state = reducer(new_state, Action::SetStatusError(Some(format!("rebase skip error: {e}"))));
            }
        }
        new_state = reducer(new_state, Action::SetOpStatus(None));
        Ok(CommandResult::StateUpdate(new_state))
    }
}

/// Save interactive rebase todo edits to disk with validation
pub struct RebaseTodoSaveCommand;

impl Command for RebaseTodoSaveCommand {
    #[instrument(skip(self, _git, state))]
    fn execute(
        &self,
        _git: &GitService,
        state: &AppState,
    ) -> Result<CommandResult, CommandError> {
        let mut new_state = state.clone();
        new_state = reducer(new_state, Action::SetOpStatus(Some("saving rebase todo…".into())));

        let path = match &state.rebase_todo_path {
            Some(p) => p.clone(),
            None => {
                new_state = reducer(new_state, Action::SetStatusError(Some("no rebase todo path found".into())));
                return Ok(CommandResult::StateUpdate(new_state));
            }
        };

        let lines = state.rebase_todo.clone();
        let has_action = lines.iter().any(|l| {
            let trimmed = l.trim();
            if trimmed.is_empty() || trimmed.starts_with('#') {
                return false;
            }
            let cmd = trimmed.split_whitespace().next().unwrap_or("");
            matches!(cmd, "pick" | "reword" | "edit" | "squash" | "fixup")
        });
        if lines.is_empty() {
            new_state = reducer(new_state, Action::SetStatusError(Some("rebase todo is empty".into())));
            new_state = reducer(new_state, Action::SetOpStatus(None));
            return Ok(CommandResult::StateUpdate(new_state));
        }
        if !has_action {
            new_state = reducer(new_state, Action::SetStatusError(Some("todo must contain at least one pick/edit/reword/squash/fixup".into())));
            new_state = reducer(new_state, Action::SetOpStatus(None));
            return Ok(CommandResult::StateUpdate(new_state));
        }

        let mut content = lines.join("\n");
        if !content.ends_with('\n') {
            content.push('\n');
        }

        match std::fs::write(&path, content) {
            Ok(_) => {
                new_state = reducer(new_state, Action::AppendOpLog("rebase todo saved".into()));
                new_state.rebase_todo_original = state.rebase_todo.clone();
                new_state.rebase_todo_dirty = false;
                new_state = reducer(new_state, Action::SetFeedback(Some("Rebase todo saved".into())));
            }
            Err(e) => {
                new_state = reducer(new_state, Action::SetStatusError(Some(format!("save error: {e}"))));
            }
        }
        new_state = reducer(new_state, Action::SetOpStatus(None));
        Ok(CommandResult::StateUpdate(new_state))
    }
}

/// Load interactive rebase todo and show popup
pub struct RebaseTodoLoadCommand;

impl Command for RebaseTodoLoadCommand {
    #[instrument(skip(self, git, state))]
    fn execute(
        &self,
        git: &GitService,
        state: &AppState,
    ) -> Result<CommandResult, CommandError> {
        let mut new_state = state.clone();
        new_state = reducer(new_state, Action::SetOpStatus(Some("loading rebase todo…".into())));
        match git.read_rebase_todo(&state.repo_path) {
            Ok((lines, path)) => {
                new_state = reducer(new_state, Action::SetRebaseTodoWithPath(lines, path.clone()));
                new_state = reducer(new_state, Action::SetRebaseTodoPath(path));
                new_state = reducer(new_state, Action::ShowRebaseTodo);
                new_state = reducer(new_state, Action::AppendOpLog("loaded rebase todo".into()));
            }
            Err(e) => {
                new_state = reducer(new_state, Action::AppendOpLog(format!("rebase todo error: {e}")));
                new_state = reducer(new_state, Action::SetStatusError(Some(format!("rebase todo error: {e}"))));
            }
        }
        new_state = reducer(new_state, Action::SetOpStatus(None));
        Ok(CommandResult::StateUpdate(new_state))
    }
}

/// Open interactive rebase todo in editor
pub struct RebaseTodoOpenCommand;

impl Command for RebaseTodoOpenCommand {
    #[instrument(skip(self, git, state))]
    fn execute(
        &self,
        git: &GitService,
        state: &AppState,
    ) -> Result<CommandResult, CommandError> {
        let mut new_state = state.clone();
        let path = match &state.rebase_todo_path {
            Some(p) => p.clone(),
            None => {
                match git.read_rebase_todo(&state.repo_path) {
                    Ok((_, p)) => p,
                    Err(e) => {
                        new_state = reducer(new_state, Action::SetStatusError(Some(format!("rebase todo error: {e}"))));
                        return Ok(CommandResult::StateUpdate(new_state));
                    }
                }
            }
        };
        
        let editor = std::env::var("EDITOR").unwrap_or_else(|_| "vi".to_string());
        match std::process::Command::new(&editor).arg(&path).status() {
            Ok(_) => {
                new_state = reducer(new_state, Action::AppendOpLog(format!("opened {} in {}", path, editor)));
            }
            Err(e) => {
                new_state = reducer(new_state, Action::SetStatusError(Some(format!("editor error: {e}"))));
            }
        }
        Ok(CommandResult::StateUpdate(new_state))
    }
}

