/* repo_view/tag.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::{RepoView, RepoViewMsg, RepoViewOutput, TagAction};
use crate::confirm_dialog::{ConfirmDialog, ConfirmDialogInit, ConfirmDialogOutput};
use crate::create_tag_dialog::{CreateTagDialog, CreateTagDialogInit, CreateTagDialogOutput};
use crate::select_remote_dialog::{SelectRemoteDialog, SelectRemoteDialogInit, SelectRemoteDialogOutput};
use crate::{git, preferences};

impl RepoView {
    pub(super) fn handle_tag_action(
        &mut self,
        action: TagAction,
        sender: &ComponentSender<Self>,
        root: &adw::ToastOverlay,
    ) {
        match action {
            TagAction::CreateRequested => {
                let target = self.selected_commit_oid.clone().unwrap_or_else(|| "HEAD".to_string());
                let window = root.ancestor(gtk::Window::static_type()).and_downcast::<gtk::Window>();

                self.create_tag_dialog = Some(
                    CreateTagDialog::builder()
                        .launch(CreateTagDialogInit {
                            repo_path: self.path.clone(),
                            default_target: target,
                            parent_window: window,
                        })
                        .forward(sender.input_sender(), |output| match output {
                            CreateTagDialogOutput::Confirmed { name, target, message } => {
                                RepoViewMsg::Tag(TagAction::CreateConfirmed { name, target, message })
                            }

                            CreateTagDialogOutput::Cancelled => RepoViewMsg::Tag(TagAction::CreateDialogDismissed),
                        }),
                );
            }

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

                self.create_tag_dialog = Some(
                    CreateTagDialog::builder()
                        .launch(CreateTagDialogInit {
                            repo_path: self.path.clone(),
                            default_target: oid,
                            parent_window: window,
                        })
                        .forward(sender.input_sender(), |output| match output {
                            CreateTagDialogOutput::Confirmed { name, target, message } => {
                                RepoViewMsg::Tag(TagAction::CreateConfirmed { name, target, message })
                            }

                            CreateTagDialogOutput::Cancelled => RepoViewMsg::Tag(TagAction::CreateDialogDismissed),
                        }),
                );
            }

            TagAction::CreateDialogDismissed => {
                self.create_tag_dialog = None;
            }

            TagAction::CreateConfirmed { name, target, message } => {
                self.create_tag_dialog = None;
                let repo_path = self.path.clone();

                sender.spawn_oneshot_command(move || {
                    let result = match message {
                        Some(msg) => git::create_annotated_tag(&repo_path, &name, &target, &msg),
                        None => git::create_lightweight_tag(&repo_path, &name, &target),
                    };

                    RepoViewMsg::Tag(TagAction::OperationComplete(result, Some(gettext("Tag created"))))
                });
            }

            TagAction::ConfirmDialogDismissed => {
                self.confirm_dialog = None;
            }

            TagAction::DeleteRequested(name) => {
                let window = root.ancestor(gtk::Window::static_type()).and_downcast::<gtk::Window>();
                let n = name.clone();

                self.confirm_dialog = Some(
                    ConfirmDialog::builder()
                        .launch(ConfirmDialogInit {
                            heading: gettext("Delete Tag?"),
                            body: gettext("The tag “{}” will be deleted.").replace("{}", &name),
                            action_label: gettext("Delete"),
                            appearance: adw::ResponseAppearance::Destructive,
                            parent_window: window,
                        })
                        .forward(sender.input_sender(), move |output| match output {
                            ConfirmDialogOutput::Confirmed => RepoViewMsg::Tag(TagAction::DeleteConfirmed(n.clone())),

                            ConfirmDialogOutput::Cancelled => RepoViewMsg::Tag(TagAction::ConfirmDialogDismissed),
                        }),
                );
            }

            TagAction::DeleteConfirmed(name) => {
                self.confirm_dialog = None;
                let repo_path = self.path.clone();

                sender.spawn_oneshot_command(move || {
                    let result = git::delete_tag(&repo_path, &name);
                    RepoViewMsg::Tag(TagAction::OperationComplete(result, Some(gettext("Tag deleted"))))
                });
            }

            TagAction::PushRequested(tag_name) => {
                let repo_path = self.path.clone();

                sender.spawn_oneshot_command(move || match git::list_remote_names(&repo_path) {
                    Ok(remotes) => RepoViewMsg::Tag(TagAction::PushRemotesLoaded { tag_name, remotes }),
                    Err(msg) => RepoViewMsg::Tag(TagAction::OperationComplete(Err(msg), None)),
                });
            }

            TagAction::PushRemotesLoaded { tag_name, remotes } => {
                if remotes.is_empty() {
                    root.add_toast(adw::Toast::new(&gettext("No remotes configured")));
                    return;
                }

                if let [remote] = remotes.as_slice() {
                    sender.input(RepoViewMsg::Tag(TagAction::PushConfirmed {
                        tag_name,
                        remote: remote.clone(),
                    }));

                    return;
                }

                let window = root.ancestor(gtk::Window::static_type()).and_downcast::<gtk::Window>();
                let tag = tag_name.clone();

                self.select_remote_dialog = Some(
                    SelectRemoteDialog::builder()
                        .launch(SelectRemoteDialogInit {
                            tag_name,
                            remotes,
                            parent_window: window,
                        })
                        .forward(sender.input_sender(), move |output| match output {
                            SelectRemoteDialogOutput::Confirmed(remote) => RepoViewMsg::Tag(TagAction::PushConfirmed {
                                tag_name: tag.clone(),
                                remote,
                            }),
                            SelectRemoteDialogOutput::Cancelled => RepoViewMsg::Tag(TagAction::PushDialogDismissed),
                        }),
                );
            }

            TagAction::PushDialogDismissed => {
                self.select_remote_dialog = None;
            }

            TagAction::PushConfirmed { tag_name, remote } => {
                self.select_remote_dialog = None;

                if self.remote_op_in_progress {
                    return;
                }

                self.remote_op_in_progress = true;
                sender.output(RepoViewOutput::RemoteOpStarted).ok();

                let repo_path = self.path.clone();
                let git_command = preferences::git_command();

                sender.spawn_oneshot_command(move || {
                    let result = git::push_tag(&repo_path, &tag_name, &remote, &git_command);
                    RepoViewMsg::Tag(TagAction::OperationComplete(result, Some(gettext("Tag pushed"))))
                });
            }

            TagAction::OperationComplete(result, success_msg) => {
                if self.remote_op_in_progress {
                    self.remote_op_in_progress = false;
                    sender.output(RepoViewOutput::RemoteOpFinished).ok();
                }

                match &result {
                    Ok(()) => {
                        if let Some(msg) = &success_msg {
                            root.add_toast(adw::Toast::new(msg));
                        }
                    }

                    Err(msg) => root.add_toast(adw::Toast::new(msg)),
                }

                if result.is_ok() {
                    sender.input(RepoViewMsg::Refresh);
                }
            }
        }
    }
}
