//! Push and pull commands.
//!
//! Handles pushing changes and pulling updates from remote repositories.

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

/// Push current branch
pub struct PushCommand {
    pub force_with_lease: bool,
}

impl Command for PushCommand {
    #[instrument(skip(self, git, state), fields(force = self.force_with_lease))]
    fn execute(
        &self,
        git: &GitService,
        state: &AppState,
    ) -> Result<CommandResult, CommandError> {
        let mut new_state = state.clone();
        new_state = reducer(new_state, Action::SetOpStatus(Some("pushing…".into())));
        if state.push_ff_only_enforce {
            if let Ok((_ahead, behind)) = git.ahead_behind_remote(&state.repo_path, &state.merge_base_branch) {
                if behind > 0 {
                    new_state = reducer(new_state, Action::AppendOpLog(format!("push blocked: behind {} on {}", behind, state.merge_base_branch)));
                    new_state = reducer(new_state, Action::SetStatusError(Some(format!("push blocked: behind {} on {}", behind, state.merge_base_branch))));
                    new_state = reducer(new_state, Action::SetOpStatus(None));
                    return Ok(CommandResult::StateUpdate(new_state));
                }
            }
        }
        match git.push(&state.repo_path, self.force_with_lease) {
            Ok(_) => {
                new_state = reducer(new_state, Action::SetFeedback(Some("Pushed".into())));
                new_state = reducer(new_state, Action::SetRefreshing(true));
                new_state = reducer(new_state, Action::AppendOpLog("push ok".into()));
            }
            Err(e) => {
                new_state = reducer(new_state, Action::AppendOpLog(format!("push error: {e}")));
                new_state = reducer(new_state, Action::SetStatusError(Some(format!("push error: {e}"))));
            }
        }
        new_state = reducer(new_state, Action::SetOpStatus(None));
        Ok(CommandResult::StateUpdate(new_state))
    }
}

/// Pull (ff-only optional)
pub struct PullCommand {
    pub ff_only: bool,
    pub timeout_secs: u64,
}

impl Command for PullCommand {
    #[instrument(skip(self, git, state), fields(ff_only = self.ff_only))]
    fn execute(
        &self,
        git: &GitService,
        state: &AppState,
    ) -> Result<CommandResult, CommandError> {
        let mut new_state = state.clone();
        new_state = reducer(new_state, Action::SetOpStatus(Some(if self.ff_only { "pull ff-only…" } else { "pull…" }.into())));
        match git.pull_ff_only(&state.repo_path, self.ff_only, self.timeout_secs) {
            Ok(_) => {
                new_state = reducer(new_state, Action::SetFeedback(Some("Pulled".into())));
                new_state = reducer(new_state, Action::AppendOpLog("pull ok".into()));
                new_state = reducer(new_state, Action::SetRefreshing(true));
            }
            Err(e) => {
                new_state = reducer(new_state, Action::AppendOpLog(format!("pull error: {e}")));
                new_state = reducer(new_state, Action::SetStatusError(Some(format!("pull error: {e}"))));
            }
        }
        new_state = reducer(new_state, Action::SetOpStatus(None));
        Ok(CommandResult::StateUpdate(new_state))
    }
}

/// Pull with rebase (autostash optional)
pub struct PullRebaseCommand {
    pub autostash: bool,
    pub timeout_secs: u64,
}

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

