/* repo_view/reword.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::prelude::*;

use super::rewrite_helpers::{RewriteToastLabels, rewrite_outcome_toast};
use super::{RepoView, RepoViewMsg, WorkspaceTab};
use crate::commit_message_dialog::{CommitMessageDialog, CommitMessageDialogInit, CommitMessageDialogOutput};
use crate::git;

#[derive(Debug)]
pub enum RewordAction {
    Requested {
        oid: String,
    },
    DirtyChecked {
        oid: String,
        summary: git::DirtySummary,
    },
    StashChoiceMade {
        oid: String,
        flags: git::StashPushFlags,
    },
    StashChoiceCancelled,
    StashPushed {
        oid: String,
        result: Result<(), String>,
    },
    Confirmed {
        oid: String,
        message: String,
        stashed: bool,
    },
    DialogDismissed,
    OperationComplete {
        result: Result<git::RebaseOutcome, String>,
        stashed: bool,
    },
}

impl RepoView {
    pub(super) fn handle_reword_action(
        &mut self,
        action: RewordAction,
        sender: &ComponentSender<Self>,
        root: &adw::ToastOverlay,
    ) {
        match action {
            RewordAction::Requested { oid } => {
                let repo_path = self.path.clone();

                sender.spawn_oneshot_command(move || match git::dirty_summary(&repo_path) {
                    Ok(summary) => RepoViewMsg::Reword(RewordAction::DirtyChecked { oid, summary }),
                    Err(msg) => RepoViewMsg::ShowToast(msg),
                });
            }

            RewordAction::DirtyChecked { oid, summary } => {
                if summary.is_dirty() {
                    let oid_for_stash = oid.clone();
                    self.open_rewrite_stash_choice_dialog(
                        gettext(
                            "You have uncommitted changes. They must be stashed before the commit message can be \
                             reworded.",
                        ),
                        summary,
                        sender,
                        root,
                        move |flags| {
                            RepoViewMsg::Reword(RewordAction::StashChoiceMade {
                                oid: oid_for_stash.clone(),
                                flags,
                            })
                        },
                        || RepoViewMsg::Reword(RewordAction::StashChoiceCancelled),
                    );
                } else {
                    self.open_reword_dialog(oid, false, sender, root);
                }
            }

            RewordAction::StashChoiceCancelled => {
                self.stash_switch_dialog = None;
            }

            RewordAction::StashChoiceMade { oid, flags } => {
                self.stash_switch_dialog = None;
                let repo_path = self.path.clone();

                sender.spawn_oneshot_command(move || {
                    let result = git::stash_push(&repo_path, None, flags);
                    RepoViewMsg::Reword(RewordAction::StashPushed { oid, result })
                });
            }

            RewordAction::StashPushed { oid, result } => {
                sender.input(RepoViewMsg::Refresh);

                match result {
                    Ok(()) => self.open_reword_dialog(oid, true, sender, root),

                    Err(msg) => {
                        root.add_toast(adw::Toast::new(&msg));
                    }
                }
            }

            RewordAction::DialogDismissed => {
                self.commit_message_dialog = None;
            }

            RewordAction::Confirmed { oid, message, stashed } => {
                self.commit_message_dialog = None;
                let path = self.path.clone();
                let git_command = crate::preferences::git_command();

                sender.spawn_oneshot_command(move || {
                    let result = git::reword_commit(&path, &git_command, &oid, &message);
                    RepoViewMsg::Reword(RewordAction::OperationComplete { result, stashed })
                });
            }

            RewordAction::OperationComplete { result, stashed } => {
                let toast = rewrite_outcome_toast(
                    &result,
                    stashed,
                    RewriteToastLabels {
                        completed: &gettext("Commit message updated"),
                        completed_with_stash: &gettext("Commit message updated, stashed changes preserved"),
                        paused_no_conflicts: &gettext("Rebase paused unexpectedly, click Continue Rebase to proceed"),
                        paused_no_conflicts_with_stash: &gettext(
                            "Rebase paused unexpectedly, click Continue Rebase to proceed",
                        ),
                    },
                );

                root.add_toast(adw::Toast::new(&toast));

                if matches!(result, Ok(git::RebaseOutcome::Paused { .. })) {
                    sender.input(RepoViewMsg::WorkspaceSelected(WorkspaceTab::WorkingCopy));
                }

                sender.input(RepoViewMsg::Refresh);
            }
        }
    }

    fn open_reword_dialog(
        &mut self,
        oid: String,
        stashed: bool,
        sender: &ComponentSender<Self>,
        root: &adw::ToastOverlay,
    ) {
        let (subject, body) = match git::load_commit_message(&self.path, &oid) {
            Ok(parts) => parts,

            Err(msg) => {
                root.add_toast(adw::Toast::new(&msg));
                return;
            }
        };

        let window = root.ancestor(gtk::Window::static_type()).and_downcast::<gtk::Window>();
        let oid_for_msg = oid;

        self.commit_message_dialog = Some(
            CommitMessageDialog::builder()
                .launch(CommitMessageDialogInit {
                    heading: gettext("Reword commit"),
                    body: gettext("Edit the commit message"),
                    confirm_label: gettext("Reword"),
                    initial_subject: subject,
                    initial_body: body,
                    repo_settings: self.settings.clone(),
                    parent_window: window,
                })
                .forward(sender.input_sender(), move |output| match output {
                    CommitMessageDialogOutput::Confirmed { subject, body } => {
                        let message = if body.is_empty() {
                            subject
                        } else {
                            format!("{subject}\n\n{body}")
                        };

                        RepoViewMsg::Reword(RewordAction::Confirmed {
                            oid: oid_for_msg.clone(),
                            message,
                            stashed,
                        })
                    }

                    CommitMessageDialogOutput::Cancelled => RepoViewMsg::Reword(RewordAction::DialogDismissed),
                }),
        );
    }
}
