/* create_tag_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 std::path::PathBuf;
use std::time::Duration;

use adw::prelude::*;
use gettextrs::gettext;
use gtk::glib;
use relm4::prelude::*;

use crate::{git, ui_helpers};

pub struct CreateTagDialogInit {
    pub repo_path: PathBuf,
    pub default_target: String,
    pub parent_window: Option<gtk::Window>,
}

#[derive(Debug)]
pub enum CreateTagDialogMsg {
    NameChanged,
    TargetChanged,
    TargetDebounceFired,
    AnnotatedToggled(bool),
}

#[derive(Debug)]
pub enum CreateTagDialogOutput {
    Confirmed {
        name: String,
        target: String,
        message: Option<String>,
    },
    Cancelled,
}

pub struct CreateTagDialog {
    dialog: adw::AlertDialog,
    name_row: adw::EntryRow,
    target_row: adw::EntryRow,
    message_row: adw::EntryRow,
    repo_path: PathBuf,
    default_target: String,
    debounce_source: Option<glib::SourceId>,
}

impl Drop for CreateTagDialog {
    fn drop(&mut self) {
        if let Some(source_id) = self.debounce_source.take() {
            source_id.remove();
        }
    }
}

fn update_target_row_title(row: &adw::EntryRow, repo_path: &std::path::Path, target: &str) {
    let base_title = gettext("Target");

    if target.is_empty() {
        row.set_title(&base_title);
        return;
    }

    match git::get_commit_summary(repo_path, target) {
        Ok((_short_oid, summary)) => {
            row.set_title(&format!("{base_title} \u{00B7} {summary}"));
        }

        Err(_) => {
            row.set_title(&base_title);
        }
    }
}

#[relm4::component(pub)]
impl Component for CreateTagDialog {
    type Init = CreateTagDialogInit;
    type Input = CreateTagDialogMsg;
    type Output = CreateTagDialogOutput;
    type CommandOutput = ();

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

            adw::PreferencesGroup {
                #[name = "name_row"]
                adw::EntryRow {
                    set_title: &gettext("Tag Name"),
                },

                #[name = "target_row"]
                adw::EntryRow {
                    set_title: &gettext("Target"),
                    set_text: &model.default_target,
                },

                #[name = "annotated_row"]
                adw::SwitchRow {
                    set_title: &gettext("Annotated"),
                    set_active: false,
                },

                #[name = "message_row"]
                adw::EntryRow {
                    set_title: &gettext("Message"),
                    set_sensitive: false,
                },
            },
        }
    }

    fn init(init: Self::Init, root: Self::Root, sender: ComponentSender<Self>) -> ComponentParts<Self> {
        let dialog = adw::AlertDialog::builder().heading(gettext("Create Tag")).build();

        let mut model = CreateTagDialog {
            dialog,
            name_row: adw::EntryRow::new(),
            target_row: adw::EntryRow::new(),
            message_row: adw::EntryRow::new(),
            repo_path: init.repo_path.clone(),
            default_target: init.default_target.clone(),
            debounce_source: None,
        };

        let widgets = view_output!();

        model.name_row = widgets.name_row.clone();
        model.target_row = widgets.target_row.clone();
        model.message_row = widgets.message_row.clone();

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

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

        update_target_row_title(&widgets.target_row, &init.repo_path, &init.default_target);

        let s = sender.clone();
        widgets.name_row.connect_changed(move |_| {
            s.input(CreateTagDialogMsg::NameChanged);
        });

        let s = sender.clone();
        widgets.target_row.connect_changed(move |_| {
            s.input(CreateTagDialogMsg::TargetChanged);
        });

        let s = sender.clone();
        widgets.annotated_row.connect_active_notify(move |row| {
            s.input(CreateTagDialogMsg::AnnotatedToggled(row.is_active()));
        });

        let name_row = widgets.name_row.clone();
        let target_row = widgets.target_row.clone();
        let annotated_row = widgets.annotated_row.clone();
        let message_row = widgets.message_row.clone();
        let s = sender.clone();
        model.dialog.connect_response(None, move |_dialog, response| {
            if response == "confirm" {
                let name = name_row.text().trim().to_string();
                let target = target_row.text().trim().to_string();

                if name.is_empty() || target.is_empty() {
                    return;
                }

                let message = if annotated_row.is_active() {
                    Some(message_row.text().to_string())
                } else {
                    None
                };

                s.output(CreateTagDialogOutput::Confirmed { name, target, message })
                    .ok();
            } else {
                s.output(CreateTagDialogOutput::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 {
            CreateTagDialogMsg::NameChanged | CreateTagDialogMsg::TargetChanged => {
                let name = self.name_row.text().trim().to_string();

                let valid =
                    !name.is_empty() && git::is_valid_tag_name(&name) && !self.target_row.text().trim().is_empty();

                self.dialog.set_response_enabled("confirm", valid);

                if matches!(msg, CreateTagDialogMsg::TargetChanged) {
                    if let Some(source_id) = self.debounce_source.take() {
                        source_id.remove();
                    }

                    let s = sender.input_sender().clone();
                    self.debounce_source = Some(glib::timeout_add_local_once(Duration::from_millis(200), move || {
                        s.emit(CreateTagDialogMsg::TargetDebounceFired);
                    }));
                }
            }

            CreateTagDialogMsg::TargetDebounceFired => {
                self.debounce_source = None;
                let trimmed = self.target_row.text().trim().to_string();

                update_target_row_title(&self.target_row, &self.repo_path, &trimmed);
            }

            CreateTagDialogMsg::AnnotatedToggled(active) => {
                if !active {
                    if ui_helpers::widget_has_focus(&self.message_row) {
                        self.name_row.grab_focus();
                    }

                    self.message_row.set_text("");
                }

                self.message_row.set_sensitive(active);
            }
        }
    }
}
