/* push_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::prelude::*;

use crate::git;

pub struct PushDialogInit {
    pub defaults: git::PushDefaults,
    pub parent_window: Option<gtk::Window>,
}

#[derive(Debug)]
pub enum PushDialogMsg {
    ForceToggled(bool),
    RemoteChanged,
    BranchChanged,
}

#[derive(Debug)]
pub enum PushDialogOutput {
    Confirmed(git::PushSettings),
    Cancelled,
}

pub struct PushDialog {
    dialog: adw::AlertDialog,
    remote_model: gtk::StringList,
    remote_row: adw::ComboRow,
    branch_row: adw::EntryRow,
    set_upstream_row: adw::SwitchRow,
    selected_idx: u32,
    defaults: git::PushDefaults,
}

#[relm4::component(pub)]
impl Component for PushDialog {
    type Init = PushDialogInit;
    type Input = PushDialogMsg;
    type Output = PushDialogOutput;
    type CommandOutput = ();

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

            adw::PreferencesGroup {
                #[name = "remote_row"]
                adw::ComboRow {
                    set_title: &gettext("Remote"),
                    set_model: Some(&model.remote_model),
                    set_selected: model.selected_idx,
                },

                #[name = "branch_row"]
                adw::EntryRow {
                    set_title: &gettext("Branch"),
                    set_text: &model.defaults.suggested_remote_branch,
                },

                #[name = "set_upstream_row"]
                adw::SwitchRow {
                    set_title: &gettext("Set upstream"),
                    set_active: !model.defaults.has_upstream,
                },

                #[name = "force_row"]
                adw::SwitchRow {
                    set_title: &gettext("Force push"),
                    set_active: false,
                },

                #[name = "push_tags_row"]
                adw::SwitchRow {
                    set_title: &gettext("Push tags"),
                    set_active: false,
                },
            },
        }
    }

    fn init(init: Self::Init, root: Self::Root, sender: ComponentSender<Self>) -> ComponentParts<Self> {
        let defaults = init.defaults;

        let remote_model = gtk::StringList::new(&[]);
        let mut selected_idx = 0u32;
        for (i, remote) in defaults.remotes.iter().enumerate() {
            remote_model.append(remote);
            if *remote == defaults.suggested_remote {
                selected_idx = i as u32;
            }
        }

        let dialog = adw::AlertDialog::builder().heading(gettext("Push")).build();

        let mut model = PushDialog {
            dialog,
            remote_model,
            remote_row: adw::ComboRow::new(),
            branch_row: adw::EntryRow::new(),
            set_upstream_row: adw::SwitchRow::new(),
            selected_idx,
            defaults,
        };

        let widgets = view_output!();

        model.remote_row = widgets.remote_row.clone();
        model.branch_row = widgets.branch_row.clone();
        model.set_upstream_row = widgets.set_upstream_row.clone();

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

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

        let s = sender.clone();
        widgets.force_row.connect_active_notify(move |row| {
            s.input(PushDialogMsg::ForceToggled(row.is_active()));
        });

        let s = sender.clone();
        widgets.remote_row.connect_selected_notify(move |_| {
            s.input(PushDialogMsg::RemoteChanged);
        });

        let s = sender.clone();
        widgets.branch_row.connect_changed(move |_| {
            s.input(PushDialogMsg::BranchChanged);
        });

        let remote_model_ref = model.remote_model.clone();
        let remote_row = widgets.remote_row.clone();
        let branch_row = widgets.branch_row.clone();
        let force_row = widgets.force_row.clone();
        let set_upstream_row = widgets.set_upstream_row.clone();
        let push_tags_row = widgets.push_tags_row.clone();
        let local_branch = model.defaults.local_branch.clone();

        model.dialog.connect_response(None, move |_dialog, response| {
            if response == "confirm" {
                let remote = remote_model_ref
                    .string(remote_row.selected())
                    .map(|s| s.to_string())
                    .unwrap_or_default();

                let branch = branch_row.text().trim().to_string();

                if !branch.is_empty() {
                    sender
                        .output(PushDialogOutput::Confirmed(git::PushSettings {
                            remote,
                            local_branch: local_branch.clone(),
                            branch,
                            force: force_row.is_active(),
                            set_upstream: set_upstream_row.is_active(),
                            push_tags: push_tags_row.is_active(),
                        }))
                        .ok();
                }
            } else {
                sender.output(PushDialogOutput::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 {
            PushDialogMsg::ForceToggled(active) => {
                if active {
                    self.dialog
                        .set_response_appearance("confirm", adw::ResponseAppearance::Destructive);
                } else {
                    self.dialog
                        .set_response_appearance("confirm", adw::ResponseAppearance::Suggested);
                }
            }

            PushDialogMsg::RemoteChanged | PushDialogMsg::BranchChanged => {
                let is_default = self.remote_row.selected() == self.selected_idx
                    && self.branch_row.text().trim() == self.defaults.suggested_remote_branch;

                self.set_upstream_row
                    .set_active(if is_default { !self.defaults.has_upstream } else { true });
            }
        }
    }
}
