/* working_copy_view/staging.rs
 *
 * Copyright 2026 Christian Kruse
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 *
 * SPDX-License-Identifier: AGPL-3.0-or-later
 */

use relm4::prelude::*;

use super::{RowSection, WorkingCopyView, WorkingCopyViewMsg, WorkingCopyViewOutput};
use crate::git::{self, FileStatus};

#[derive(Debug)]
pub enum StagingAction {
    ToggleStaged { section: RowSection, idx: usize },
    ToggleStageAll,
    StagingComplete(Result<(), String>, Vec<String>),
    StageAllComplete(Result<(), String>),
}

impl WorkingCopyView {
    pub(super) fn handle_staging_action(&mut self, action: StagingAction, sender: &ComponentSender<Self>) {
        match action {
            StagingAction::ToggleStaged { section, idx } => {
                let indices = self.expand_to_selection(section, idx);
                self.toggle_staged_by_indices(section, indices, sender);
            }

            StagingAction::ToggleStageAll => {
                let repo_path = self.path.clone();
                let all_staged = self.all_staged;

                sender.spawn_oneshot_command(move || {
                    let result = if all_staged {
                        git::unstage_all(&repo_path)
                    } else {
                        git::stage_all(&repo_path)
                    };

                    WorkingCopyViewMsg::Staging(StagingAction::StageAllComplete(result))
                });
            }

            StagingAction::StagingComplete(result, paths) => {
                self.handle_operation_complete(&result, sender, paths);
            }

            StagingAction::StageAllComplete(result) => {
                if let Err(msg) = &result {
                    sender.output(WorkingCopyViewOutput::ShowToast(msg.clone())).ok();
                }

                self.spawn_refresh(sender, Vec::new());
            }
        }
    }

    pub(super) fn toggle_staged_by_indices(
        &mut self,
        section: RowSection,
        indices: Vec<usize>,
        sender: &ComponentSender<Self>,
    ) {
        if indices.is_empty() {
            return;
        }

        let repo_path = self.path.clone();
        let reselect_paths = self.selected_paths_all();
        let factory = self.factory_for_mut(section);
        let guard = factory.guard();

        let unstage_all = match section {
            RowSection::Staged => true,
            RowSection::Unstaged => false,
            RowSection::Unified => indices
                .iter()
                .all(|i| guard.get(*i).map(|r| r.file().is_fully_staged()).unwrap_or(false)),
        };

        let mut to_stage: Vec<(String, Option<FileStatus>)> = Vec::new();
        let mut to_unstage: Vec<String> = Vec::new();

        for idx in &indices {
            if let Some(row) = guard.get(*idx) {
                let path = row.file().path.clone();

                if unstage_all {
                    to_unstage.push(path);
                } else {
                    to_stage.push((path, row.file().wt_status));
                }
            }
        }

        drop(guard);

        sender.spawn_oneshot_command(move || {
            let result = if unstage_all {
                let refs: Vec<&str> = to_unstage.iter().map(String::as_str).collect();
                git::unstage_files(&repo_path, &refs)
            } else {
                git::stage_files(&repo_path, &to_stage)
            };

            WorkingCopyViewMsg::Staging(StagingAction::StagingComplete(result, reselect_paths))
        });
    }
}
