/* working_copy_view/selection.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 adw::prelude::*;
use relm4::prelude::*;

use super::{ChangedFileRow, RowSection, ViewLayout, WorkingCopyView};
use crate::diff_view::DiffViewMsg;

#[derive(Debug)]
pub enum SelectionAction {
    FileSelected { section: RowSection, path: Option<String> },
}

impl WorkingCopyView {
    pub(super) fn handle_selection_action(&mut self, action: SelectionAction, _sender: &ComponentSender<Self>) {
        match action {
            SelectionAction::FileSelected { section, path } => match path {
                Some(p) => {
                    let file = self.last_files.iter().find(|f| f.path == p);

                    let (index_status, wt_status) = match section {
                        RowSection::Unified => (file.and_then(|f| f.index_status), file.and_then(|f| f.wt_status)),
                        RowSection::Staged => (file.and_then(|f| f.index_status), None),
                        RowSection::Unstaged => (None, file.and_then(|f| f.wt_status)),
                    };

                    self.diff_view.emit(DiffViewMsg::ShowFile(p, index_status, wt_status));
                }

                None => self.diff_view.emit(DiffViewMsg::Clear),
            },
        }
    }

    pub(super) fn expand_to_selection(&self, section: RowSection, idx: usize) -> Vec<usize> {
        let list = self.factory_for(section).widget();
        let mut selected: Vec<usize> = list.selected_rows().iter().map(|r| r.index() as usize).collect();

        if selected.contains(&idx) && selected.len() > 1 {
            selected.sort_unstable();
            selected
        } else {
            vec![idx]
        }
    }

    pub(super) fn selected_paths_all(&self) -> Vec<String> {
        match self.view_layout {
            ViewLayout::Unified => self.selected_paths_in(RowSection::Unified),
            ViewLayout::Split => {
                let mut out = self.selected_paths_in(RowSection::Unstaged);
                out.extend(self.selected_paths_in(RowSection::Staged));
                out
            }
        }
    }

    pub(super) fn selected_paths_in(&self, section: RowSection) -> Vec<String> {
        self.factory_for(section)
            .widget()
            .selected_rows()
            .iter()
            .filter_map(|r| r.downcast_ref::<adw::ActionRow>().map(|ar| ar.title().to_string()))
            .collect()
    }

    pub(super) fn factory_for(&self, section: RowSection) -> &FactoryVecDeque<ChangedFileRow> {
        match section {
            RowSection::Unified => &self.files_factory,
            RowSection::Staged => &self.staged_factory,
            RowSection::Unstaged => &self.unstaged_factory,
        }
    }

    pub(super) fn factory_for_mut(&mut self, section: RowSection) -> &mut FactoryVecDeque<ChangedFileRow> {
        match section {
            RowSection::Unified => &mut self.files_factory,
            RowSection::Staged => &mut self.staged_factory,
            RowSection::Unstaged => &mut self.unstaged_factory,
        }
    }

    pub(super) fn reselect_files(&mut self, paths: &[String]) {
        if paths.is_empty() {
            return;
        }

        match self.view_layout {
            ViewLayout::Unified => self.reselect_in_list(RowSection::Unified, paths),
            ViewLayout::Split => {
                self.reselect_in_list(RowSection::Staged, paths);
                self.reselect_in_list(RowSection::Unstaged, paths);
            }
        }
    }

    // select_row replaces the current seletion, so we have to do a workaround: select all rows, then unselect
    // the unwanted rows
    pub(super) fn reselect_in_list(&self, section: RowSection, target_paths: &[String]) {
        let factory = self.factory_for(section);
        let list = factory.widget();

        list.select_all();

        for (idx, row_state) in factory.iter().enumerate() {
            if !target_paths.iter().any(|p| p == &row_state.file().path)
                && let Some(row) = list.row_at_index(idx as i32)
            {
                list.unselect_row(&row);
            }
        }
    }
}
