//! Stage and unstage file commands.
//!
//! These commands handle staging and unstaging files with optimistic UI updates
//! for immediate visual feedback.

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

/// Command to stage one or more files
pub struct StageFilesCommand {
    pub paths: Vec<String>,
}

impl Command for StageFilesCommand {
    #[instrument(skip(self, git, state), fields(paths = ?self.paths))]
    fn execute(
        &self,
        git: &GitService,
        state: &AppState,
    ) -> Result<CommandResult, CommandError> {
        let mut staged_count = 0;
        let mut errors = Vec::new();
        
        for path in &self.paths {
            match git.stage_file(&state.repo_path, path) {
                Ok(_) => staged_count += 1,
                Err(e) => errors.push(format!("stage error: {e}")),
            }
        }
        
        let mut new_state = state.clone();
        
        if !errors.is_empty() {
            new_state = reducer(new_state, Action::SetStatusError(Some(errors.join(", "))));
        }
        
        if staged_count > 0 {
            // Apply optimistic update: mark staged files as staged in status_entries
            // This provides immediate visual feedback before the async refresh completes
            for entry in new_state.status_entries.iter_mut() {
                if self.paths.contains(&entry.path) {
                    entry.staged = true;
                    // Preserve unstaged state - file might have both staged and unstaged changes
                }
            }
            
            new_state = reducer(new_state, Action::SetFeedback(Some(
                format!("Staged {} file(s)", staged_count)
            )));
            new_state = reducer(new_state, Action::ClearFileSelects);
            new_state = reducer(new_state, Action::SetRefreshing(true));
        }
        
        Ok(CommandResult::StateUpdate(new_state))
    }
}

/// Command to unstage one or more files
pub struct UnstageFilesCommand {
    pub paths: Vec<String>,
}

impl Command for UnstageFilesCommand {
    #[instrument(skip(self, git, state), fields(paths = ?self.paths))]
    fn execute(
        &self,
        git: &GitService,
        state: &AppState,
    ) -> Result<CommandResult, CommandError> {
        let mut unstaged_count = 0;
        let mut errors = Vec::new();
        
        for path in &self.paths {
            match git.unstage_file(&state.repo_path, path) {
                Ok(_) => unstaged_count += 1,
                Err(e) => errors.push(format!("unstage error: {e}")),
            }
        }
        
        let mut new_state = state.clone();
        
        if !errors.is_empty() {
            new_state = reducer(new_state, Action::SetStatusError(Some(errors.join(", "))));
        }
        
        if unstaged_count > 0 {
            // Apply optimistic update: mark unstaged files as unstaged in status_entries
            // This provides immediate visual feedback before the async refresh completes
            for entry in new_state.status_entries.iter_mut() {
                if self.paths.contains(&entry.path) {
                    entry.staged = false;
                    // Preserve unstaged state - status refresh will set correct state
                }
            }
            
            new_state = reducer(new_state, Action::SetFeedback(Some(
                format!("Unstaged {} file(s)", unstaged_count)
            )));
            new_state = reducer(new_state, Action::ClearFileSelects);
            new_state = reducer(new_state, Action::SetRefreshing(true));
        }
        
        Ok(CommandResult::StateUpdate(new_state))
    }
}

