/* commit_message_dialog.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::gtk::gio;
use relm4::prelude::*;

use crate::wrapping;

pub struct CommitMessageDialogInit {
    pub heading: String,
    pub body: String,
    pub confirm_label: String,
    pub initial_subject: String,
    pub initial_body: String,
    pub repo_settings: gio::Settings,
    pub parent_window: Option<gtk::Window>,
}

#[derive(Debug)]
pub enum CommitMessageDialogMsg {
    SubjectChanged,
    MaxLineLengthChanged(u32),
    WrapBody,
}

#[derive(Debug)]
pub enum CommitMessageDialogOutput {
    Confirmed { subject: String, body: String },
    Cancelled,
}

pub struct CommitMessageDialog {
    dialog: adw::AlertDialog,
    subject_entry: gtk::Entry,
    body_view: gtk::TextView,
    max_line_length: u32,
    wrapping_body: bool,
    repo_settings: gio::Settings,
}

#[relm4::component(pub)]
impl Component for CommitMessageDialog {
    type Init = CommitMessageDialogInit;
    type Input = CommitMessageDialogMsg;
    type Output = CommitMessageDialogOutput;
    type CommandOutput = ();

    view! {
        #[root]
        gtk::Box {
            set_orientation: gtk::Orientation::Vertical,
            add_css_class: "gap-s",
            set_width_request: 480,

            #[name = "subject_entry"]
            gtk::Entry {
                set_placeholder_text: Some(&gettext("Commit Subject")),
                update_property: &[gtk::accessible::Property::Label(&gettext("Commit Subject"))],
                #[watch]
                set_max_length: model.max_line_length as i32,
                set_activates_default: false,
            },

            gtk::ScrolledWindow {
                set_hscrollbar_policy: gtk::PolicyType::Never,
                set_min_content_height: 160,
                set_vexpand: true,

                #[name = "body_view"]
                gtk::TextView {
                    set_wrap_mode: gtk::WrapMode::Word,
                    set_top_margin: 6,
                    set_bottom_margin: 6,
                    set_left_margin: 6,
                    set_right_margin: 6,
                    add_css_class: "card",
                    update_property: &[gtk::accessible::Property::Label(&gettext("Commit Details"))],
                },
            },
        }
    }

    fn init(init: Self::Init, root: Self::Root, sender: ComponentSender<Self>) -> ComponentParts<Self> {
        let repo_settings = init.repo_settings.clone();
        let max_line_length = repo_settings.int("commit-line-length").max(0) as u32;

        let dialog = adw::AlertDialog::builder()
            .heading(&init.heading)
            .body(&init.body)
            .build();

        let mut model = CommitMessageDialog {
            dialog,
            subject_entry: gtk::Entry::new(),
            body_view: gtk::TextView::new(),
            max_line_length,
            wrapping_body: false,
            repo_settings,
        };

        let widgets = view_output!();

        model.subject_entry = widgets.subject_entry.clone();
        model.body_view = widgets.body_view.clone();

        widgets.subject_entry.set_text(&init.initial_subject);
        widgets.body_view.buffer().set_text(&init.initial_body);

        model.dialog.set_extra_child(Some(&root));

        model.dialog.add_response("cancel", &gettext("Cancel"));
        model.dialog.add_response("confirm", &init.confirm_label);
        model
            .dialog
            .set_response_appearance("confirm", adw::ResponseAppearance::Suggested);
        model.dialog.set_default_response(Some("confirm"));
        model.dialog.set_close_response("cancel");

        let initial_valid = !init.initial_subject.trim().is_empty();
        model.dialog.set_response_enabled("confirm", initial_valid);

        {
            let s = sender.clone();
            widgets.subject_entry.connect_changed(move |_| {
                s.input(CommitMessageDialogMsg::SubjectChanged);
            });
        }

        {
            let s = sender.clone();
            widgets.body_view.buffer().connect_changed(move |_| {
                s.input(CommitMessageDialogMsg::WrapBody);
            });
        }

        {
            let s = sender.clone();
            model
                .repo_settings
                .connect_changed(Some("commit-line-length"), move |settings: &gio::Settings, _| {
                    let len = settings.int("commit-line-length").max(0) as u32;
                    s.input(CommitMessageDialogMsg::MaxLineLengthChanged(len));
                });
        }

        let subject_entry = widgets.subject_entry.clone();
        let body_view = widgets.body_view.clone();
        model.dialog.connect_response(None, move |_dialog, response| {
            if response == "confirm" {
                let subject = subject_entry.text().trim().to_string();

                if subject.is_empty() {
                    sender.output(CommitMessageDialogOutput::Cancelled).ok();
                    return;
                }

                let buffer = body_view.buffer();
                let body = buffer
                    .text(&buffer.start_iter(), &buffer.end_iter(), false)
                    .to_string()
                    .trim()
                    .to_string();

                sender
                    .output(CommitMessageDialogOutput::Confirmed { subject, body })
                    .ok();
            } else {
                sender.output(CommitMessageDialogOutput::Cancelled).ok();
            }
        });

        model.dialog.present(init.parent_window.as_ref());

        ComponentParts { model, widgets }
    }

    fn update(&mut self, msg: Self::Input, _sender: ComponentSender<Self>, _root: &Self::Root) {
        match msg {
            CommitMessageDialogMsg::SubjectChanged => {
                let valid = !self.subject_entry.text().trim().is_empty();
                self.dialog.set_response_enabled("confirm", valid);
            }

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

            CommitMessageDialogMsg::WrapBody => {
                if self.max_line_length > 0 && !self.wrapping_body {
                    self.wrapping_body = true;

                    wrapping::hard_wrap_buffer(&self.body_view.buffer(), self.max_line_length as usize);

                    self.wrapping_body = false;
                }
            }
        }
    }
}
