/* working_copy_view/commit.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 gettextrs::gettext;
use gtk::prelude::*;
use relm4::gtk;
use relm4::prelude::*;

use super::{WorkingCopyView, WorkingCopyViewMsg, WorkingCopyViewOutput, WorkingCopyViewWidgets};
use crate::{git, wrapping};

#[derive(Debug)]
pub enum CommitAction {
    Commit,
    SubjectChanged(String),
    BodyShow,
    BodyHide,
    ToggleAmend(bool),
    AmendMessageLoaded(Result<(String, String), String>),
    Complete(Result<(), String>),
    WrapBody,
    MaxLineLengthChanged(u32),
    FocusCommit,
}

impl WorkingCopyView {
    pub(super) fn commit_button_active(&self) -> bool {
        if self.repo_state == git::RepoState::Rebase {
            self.has_staged_files
        } else if self.amend {
            !self.commit_subject_empty
        } else {
            !self.commit_subject_empty && self.has_staged_files
        }
    }

    pub(super) fn handle_commit_action(
        &mut self,
        action: CommitAction,
        widgets: &mut WorkingCopyViewWidgets,
        sender: &ComponentSender<Self>,
    ) {
        match action {
            CommitAction::FocusCommit => {
                self.commit_body_visible = true;
                widgets.commit_subject.grab_focus();
            }

            CommitAction::Commit => {
                if self.commit_subject_empty {
                    widgets.commit_subject.grab_focus();

                    sender
                        .output(WorkingCopyViewOutput::ShowToast(gettext("Specify a commit message subject to commit")))
                        .ok();

                    return;
                }

                if !self.has_staged_files && !self.amend {
                    sender
                        .output(WorkingCopyViewOutput::ShowToast(gettext("No staged files to commit")))
                        .ok();

                    return;
                }

                let subject = widgets.commit_subject.text().to_string();

                let body_buf = widgets.commit_body.buffer();
                let body = body_buf
                    .text(&body_buf.start_iter(), &body_buf.end_iter(), false)
                    .to_string();

                let message = if body.is_empty() {
                    subject
                } else {
                    format!("{subject}\n\n{body}")
                };

                let repo_path = self.path.clone();
                let amend = self.amend;
                sender.spawn_oneshot_command(move || {
                    let result = if amend {
                        git::amend_commit(&repo_path, &message)
                    } else {
                        git::create_commit(&repo_path, &message)
                    };

                    WorkingCopyViewMsg::Commit(CommitAction::Complete(result))
                });
            }

            CommitAction::SubjectChanged(text) => {
                self.commit_subject_empty = text.is_empty();
            }

            CommitAction::BodyShow => {
                self.commit_body_visible = true;
            }

            CommitAction::BodyHide => {
                if self.amend {
                    return;
                }

                let body_buf = widgets.commit_body.buffer();
                let body_text = body_buf.text(&body_buf.start_iter(), &body_buf.end_iter(), false);

                if body_text.is_empty() && self.commit_subject_empty {
                    self.commit_body_visible = false;
                }
            }

            CommitAction::Complete(result) => {
                let was_amend = self.amend;
                match result {
                    Ok(()) => {
                        widgets.commit_subject.set_text("");
                        widgets.commit_body.buffer().set_text("");
                        self.commit_body_visible = false;
                        self.amend = false;

                        let msg = if was_amend {
                            gettext("Commit amended")
                        } else {
                            gettext("Commit created")
                        };

                        sender.output(WorkingCopyViewOutput::ShowToast(msg)).ok();
                        sender.output(WorkingCopyViewOutput::DataChanged).ok();
                    }

                    Err(msg) => {
                        sender.output(WorkingCopyViewOutput::ShowToast(msg)).ok();
                    }
                }
            }

            CommitAction::ToggleAmend(checked) => {
                self.amend = checked;

                if checked {
                    let repo_path = self.path.clone();
                    sender.spawn_oneshot_command(move || {
                        WorkingCopyViewMsg::Commit(CommitAction::AmendMessageLoaded(git::get_head_commit_message(
                            &repo_path,
                        )))
                    });
                } else {
                    widgets.commit_subject.set_text("");
                    widgets.commit_body.buffer().set_text("");
                    self.commit_subject_empty = true;
                    self.commit_body_visible = false;
                }
            }

            CommitAction::AmendMessageLoaded(result) => match result {
                Ok((subject, body)) => {
                    widgets.commit_subject.set_text(&subject);
                    self.commit_subject_empty = subject.is_empty();

                    widgets.commit_body.buffer().set_text(&body);
                    if !body.is_empty() {
                        self.commit_body_visible = true;
                    }
                }

                Err(msg) => {
                    sender.output(WorkingCopyViewOutput::ShowToast(msg)).ok();
                    self.amend = false;
                }
            },

            CommitAction::MaxLineLengthChanged(len) => {
                self.max_line_length = len;
            }

            CommitAction::WrapBody => {
                if self.max_line_length > 0 && !self.wrapping_body {
                    self.wrapping_body = true;
                    wrapping::hard_wrap_buffer(&widgets.commit_body.buffer(), self.max_line_length as usize);
                    self.wrapping_body = false;
                }
            }
        }
    }
}
