/* working_copy_view/helpers.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 gettextrs::gettext;
use relm4::{ComponentSender, gtk};

use super::{
    ChangedFileRowOutput, CommitAction, DeleteAction, DiscardAction, GitignoreAction, RowSection, SelectionAction,
    StagingAction, WorkingCopyView, WorkingCopyViewMsg, WorkingCopyViewWidgets,
};
use crate::{git, ui_helpers};

pub(super) fn forward_row_output(output: ChangedFileRowOutput) -> WorkingCopyViewMsg {
    match output {
        ChangedFileRowOutput::ToggleStaged { section, idx } => {
            WorkingCopyViewMsg::Staging(StagingAction::ToggleStaged { section, idx })
        }

        ChangedFileRowOutput::DeleteRequested { section, idx } => {
            WorkingCopyViewMsg::Delete(DeleteAction::Requested { section, idx })
        }

        ChangedFileRowOutput::IgnoreRequested { section, idx } => {
            WorkingCopyViewMsg::Gitignore(GitignoreAction::Requested { section, idx })
        }

        ChangedFileRowOutput::DiscardRequested { section, idx } => {
            WorkingCopyViewMsg::Discard(DiscardAction::Requested { section, idx })
        }
    }
}

pub(super) fn attach_row_selection(
    list: &gtk::ListBox,
    section: RowSection,
    other: Option<gtk::ListBox>,
    sender: &relm4::Sender<WorkingCopyViewMsg>,
) {
    let s = sender.clone();
    let other_for_check = other.clone();
    list.connect_selected_rows_changed(move |list| {
        let rows = list.selected_rows();

        if !rows.is_empty() {
            if let Some(other) = &other {
                other.unselect_all();
            }

            let path = rows
                .last()
                .and_then(|r| r.downcast_ref::<adw::ActionRow>().map(|ar| ar.title().to_string()));

            s.emit(WorkingCopyViewMsg::Selection(SelectionAction::FileSelected { section, path }));
        } else {
            if other_for_check
                .as_ref()
                .map(|o| !o.selected_rows().is_empty())
                .unwrap_or(false)
            {
                return;
            }

            s.emit(WorkingCopyViewMsg::Selection(SelectionAction::FileSelected { section, path: None }));
        }
    });
}

pub(super) fn attach_list_keys(list: &gtk::ListBox, section: RowSection, sender: &relm4::Sender<WorkingCopyViewMsg>) {
    let s = sender.clone();
    let list_clone = list.clone();
    let key_ctrl = gtk::EventControllerKey::new();

    key_ctrl.connect_key_pressed(move |_, key, _, _| {
        let selected = list_clone.selected_rows();
        if selected.is_empty() {
            return gtk::glib::Propagation::Proceed;
        }

        let idx = selected[0].index() as usize;

        match key {
            gtk::gdk::Key::Return | gtk::gdk::Key::space => {
                s.emit(WorkingCopyViewMsg::Staging(StagingAction::ToggleStaged { section, idx }));
                gtk::glib::Propagation::Stop
            }

            gtk::gdk::Key::Delete | gtk::gdk::Key::KP_Delete => {
                s.emit(WorkingCopyViewMsg::Discard(DiscardAction::Requested { section, idx }));
                gtk::glib::Propagation::Stop
            }

            _ => gtk::glib::Propagation::Proceed,
        }
    });

    list.add_controller(key_ctrl);
}

pub(super) fn setup_commit_area_controllers(
    widgets: &WorkingCopyViewWidgets,
    sender: &ComponentSender<WorkingCopyView>,
) {
    // Show commit body on subject focus
    let s = sender.input_sender().clone();
    let subject_focus = gtk::EventControllerFocus::new();
    subject_focus.connect_enter(move |_| {
        s.emit(WorkingCopyViewMsg::Commit(CommitAction::BodyShow));
    });
    widgets.commit_subject.add_controller(subject_focus);

    // Hide commit body when all commit widgets lose focus
    for widget in [
        widgets.commit_subject.upcast_ref::<gtk::Widget>(),
        widgets.commit_body.upcast_ref::<gtk::Widget>(),
        widgets.amend_check.upcast_ref::<gtk::Widget>(),
    ] {
        attach_commit_focus_leave(
            widget,
            sender.input_sender(),
            &widgets.commit_subject,
            &widgets.commit_body,
            &widgets.amend_check,
        );
    }

    // Ctrl+Enter to commit
    attach_ctrl_enter_commit(&widgets.commit_subject, sender.input_sender());
    attach_ctrl_enter_commit(&widgets.commit_body, sender.input_sender());
}

fn attach_commit_focus_leave(
    widget: &impl IsA<gtk::Widget>,
    sender: &relm4::Sender<WorkingCopyViewMsg>,
    subject: &gtk::Entry,
    body: &gtk::TextView,
    amend: &gtk::CheckButton,
) {
    let s = sender.clone();
    let subject = subject.clone();
    let body = body.clone();
    let amend = amend.clone();

    let focus = gtk::EventControllerFocus::new();
    focus.connect_leave(move |_| {
        let s = s.clone();
        let subject = subject.clone();
        let body = body.clone();
        let amend = amend.clone();

        gtk::glib::idle_add_local_once(move || {
            if !ui_helpers::widget_has_focus(&subject)
                && !ui_helpers::widget_has_focus(&body)
                && !ui_helpers::widget_has_focus(&amend)
            {
                s.emit(WorkingCopyViewMsg::Commit(CommitAction::BodyHide));
            }
        });
    });

    widget.add_controller(focus);
}

fn attach_ctrl_enter_commit(widget: &impl IsA<gtk::Widget>, sender: &relm4::Sender<WorkingCopyViewMsg>) {
    let s = sender.clone();
    let key_ctrl = gtk::EventControllerKey::new();
    key_ctrl.connect_key_pressed(move |_, key, _, modifier| {
        if key == gtk::gdk::Key::Return && modifier.contains(gtk::gdk::ModifierType::CONTROL_MASK) {
            s.emit(WorkingCopyViewMsg::Commit(CommitAction::Commit));
            return gtk::glib::Propagation::Stop;
        }

        gtk::glib::Propagation::Proceed
    });

    widget.add_controller(key_ctrl);
}

pub(super) fn state_banner_title(state: git::RepoState, rebase_has_conflicts: bool) -> String {
    match state {
        git::RepoState::Merge => gettext("Merge in progress, resolve conflicts and commit"),
        git::RepoState::CherryPick => gettext("Cherry-pick in progress, resolve conflicts and commit"),
        git::RepoState::Revert => gettext("Revert in progress, resolve conflicts and commit"),
        git::RepoState::Clean => String::new(),

        git::RepoState::Rebase if rebase_has_conflicts => {
            gettext("Rebase in progress, resolve conflicts, stage files, then continue")
        }

        git::RepoState::Rebase => {
            gettext("Rebase paused, amend the commit and click Continue Rebase, or make additional commits first")
        }
    }
}
