/* menus.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 relm4::gtk;

pub(crate) fn common_menu_section() -> gtk::gio::Menu {
    let section = gtk::gio::Menu::new();
    section.append(Some(&gettext("_Preferences")), Some("app.preferences"));
    section.append(Some(&gettext("_Keyboard Shortcuts")), Some("app.shortcuts"));
    section.append(Some(&gettext("_What's New")), Some("app.whats-new"));
    section.append(Some(&gettext("_About Gitte")), Some("app.about"));
    section
}

pub(crate) fn sort_menu() -> gtk::gio::Menu {
    let menu = gtk::gio::Menu::new();
    let section = gtk::gio::Menu::new();

    section.append(Some(&gettext("Manual")), Some("start.start-screen-sort::manual"));
    section.append(Some(&gettext("Name")), Some("start.start-screen-sort::name"));
    section.append(Some(&gettext("Last Opened")), Some("start.start-screen-sort::last-opened"));
    menu.append_section(None, &section);

    menu
}

pub(crate) fn start_menu() -> gtk::gio::Menu {
    let menu = gtk::gio::Menu::new();

    let window_section = gtk::gio::Menu::new();
    window_section.append(Some(&gettext("_New Window")), Some("app.new-window"));
    menu.append_section(None, &window_section);

    let repo_section = gtk::gio::Menu::new();
    repo_section.append(Some(&gettext("_Add Repository…")), Some("win.add-repository"));
    repo_section.append(Some(&gettext("_Clone Repository…")), Some("win.clone-repository"));
    menu.append_section(None, &repo_section);

    menu.append_section(None, &common_menu_section());
    menu
}

fn branch_context_menu(switch_label: &str, include_local_actions: bool) -> gtk::gio::Menu {
    let menu = gtk::gio::Menu::new();

    let switch_section = gtk::gio::Menu::new();
    switch_section.append(Some(switch_label), Some("tree-item.switch"));
    menu.append_section(None, &switch_section);

    let edit_section = gtk::gio::Menu::new();

    if include_local_actions {
        edit_section.append(Some(&gettext("_Rename…")), Some("tree-item.rename"));
    }

    edit_section.append(Some(&gettext("Create _Branch From Here…")), Some("tree-item.create-from"));
    edit_section.append(Some(&gettext("_Copy Branch Name")), Some("tree-item.copy-name"));

    menu.append_section(None, &edit_section);

    let merge_section = gtk::gio::Menu::new();
    merge_section.append(Some(&gettext("_Merge into Current…")), Some("tree-item.merge"));
    menu.append_section(None, &merge_section);

    let archive_section = gtk::gio::Menu::new();
    archive_section.append(Some(&gettext("Create _Archive…")), Some("tree-item.archive"));
    menu.append_section(None, &archive_section);

    let remote_section = gtk::gio::Menu::new();
    remote_section.append(Some(&gettext("_Fetch")), Some("tree-item.fetch"));
    if include_local_actions {
        remote_section.append(Some(&gettext("Pu_ll")), Some("repo.pull"));
        remote_section.append(Some(&gettext("_Push")), Some("tree-item.push"));
    }
    menu.append_section(None, &remote_section);

    // Delete is only offered for local branches: deleting a remote-tracking ref locally
    // would just be re-fetched on the next `git fetch` and is almost never what the user wants.
    if include_local_actions {
        let destructive_section = gtk::gio::Menu::new();
        destructive_section.append(Some(&gettext("_Delete")), Some("tree-item.delete"));
        menu.append_section(None, &destructive_section);
    }

    menu
}

pub(crate) fn local_branch_context_menu() -> gtk::gio::Menu {
    branch_context_menu(&gettext("S_witch"), true)
}

pub(crate) fn remote_branch_context_menu() -> gtk::gio::Menu {
    branch_context_menu(&gettext("Check _Out"), false)
}

pub(crate) fn pull_menu() -> gtk::gio::Menu {
    let menu = gtk::gio::Menu::new();

    let section = gtk::gio::Menu::new();
    section.append(Some(&gettext("Pu_ll…")), Some("repo.pull"));
    section.append(Some(&gettext("_Fetch")), Some("repo.fetch"));
    menu.append_section(None, &section);

    menu
}

pub(crate) fn stash_menu() -> gtk::gio::Menu {
    let menu = gtk::gio::Menu::new();

    let push_section = gtk::gio::Menu::new();
    push_section.append(Some(&gettext("_Stash")), Some("repo.stash"));
    push_section.append(Some(&gettext("Stash Including _Untracked")), Some("repo.stash-untracked"));
    menu.append_section(None, &push_section);

    let pop_section = gtk::gio::Menu::new();
    pop_section.append(Some(&gettext("Pop _Latest Stash")), Some("repo.stash-pop"));
    menu.append_section(None, &pop_section);

    menu
}

pub(crate) fn stash_item_context_menu() -> gtk::gio::Menu {
    let menu = gtk::gio::Menu::new();

    let apply_section = gtk::gio::Menu::new();
    apply_section.append(Some(&gettext("_Apply")), Some("stash-item.apply"));
    apply_section.append(Some(&gettext("_Pop")), Some("stash-item.pop"));
    menu.append_section(None, &apply_section);

    let destructive_section = gtk::gio::Menu::new();
    destructive_section.append(Some(&gettext("_Drop")), Some("stash-item.drop"));
    menu.append_section(None, &destructive_section);

    menu
}

pub(crate) fn tag_context_menu() -> gtk::gio::Menu {
    let menu = gtk::gio::Menu::new();

    let checkout_section = gtk::gio::Menu::new();
    checkout_section.append(Some(&gettext("Check _Out")), Some("tag-item.checkout"));
    checkout_section.append(Some(&gettext("Create _Branch From Here…")), Some("tag-item.create-branch"));
    menu.append_section(None, &checkout_section);

    let push_section = gtk::gio::Menu::new();
    push_section.append(Some(&gettext("_Push to Remote…")), Some("tag-item.push"));
    menu.append_section(None, &push_section);

    let archive_section = gtk::gio::Menu::new();
    archive_section.append(Some(&gettext("Create _Archive…")), Some("tag-item.archive"));
    menu.append_section(None, &archive_section);

    let destructive_section = gtk::gio::Menu::new();
    destructive_section.append(Some(&gettext("_Delete")), Some("tag-item.delete"));
    menu.append_section(None, &destructive_section);

    menu
}

pub(crate) fn commit_context_menu() -> gtk::gio::Menu {
    let menu = gtk::gio::Menu::new();

    let checkout_section = gtk::gio::Menu::new();
    checkout_section.append(Some(&gettext("Check _Out")), Some("commit-item.checkout"));
    menu.append_section(None, &checkout_section);

    let copy_section = gtk::gio::Menu::new();
    copy_section.append(Some(&gettext("Copy Commit _Hash")), Some("commit-item.copy-hash"));
    copy_section.append(Some(&gettext("Copy _Short Hash")), Some("commit-item.copy-short-hash"));
    menu.append_section(None, &copy_section);

    let tag_section = gtk::gio::Menu::new();
    tag_section.append(Some(&gettext("Create _Tag…")), Some("commit-item.create-tag"));
    menu.append_section(None, &tag_section);

    let archive_section = gtk::gio::Menu::new();
    archive_section.append(Some(&gettext("Create _Archive…")), Some("commit-item.archive"));
    menu.append_section(None, &archive_section);

    let history_section = gtk::gio::Menu::new();
    history_section.append(Some(&gettext("Re_word…")), Some("commit-item.reword"));
    history_section.append(Some(&gettext("_Edit Commit")), Some("commit-item.edit"));
    history_section.append(Some(&gettext("_Squash…")), Some("commit-item.squash"));
    history_section.append(Some(&gettext("_Fixup")), Some("commit-item.fixup"));
    history_section.append(Some(&gettext("_Drop…")), Some("commit-item.drop"));
    menu.append_section(None, &history_section);

    menu
}

pub(crate) fn diff_line_context_menu(fallback_pos: Option<u32>) -> gtk::gio::Menu {
    use gtk::glib;

    let menu = gtk::gio::Menu::new();
    let section = gtk::gio::Menu::new();

    let target = glib::Variant::from(fallback_pos.unwrap_or(0));

    let location_item = gtk::gio::MenuItem::new(Some(&gettext("Copy file _location")), Some("diff-line.copy-location"));
    location_item.set_attribute_value("target", Some(&target));
    section.append_item(&location_item);

    let code_item = gtk::gio::MenuItem::new(Some(&gettext("Copy _code")), Some("diff-line.copy-code"));
    code_item.set_attribute_value("target", Some(&target));
    section.append_item(&code_item);

    menu.append_section(None, &section);
    menu
}

pub(crate) fn diff_line_extra_menu() -> gtk::gio::Menu {
    let menu = gtk::gio::Menu::new();
    let section = gtk::gio::Menu::new();

    section.append(Some(&gettext("Copy file _location")), Some("diff-line.copy-location"));

    menu.append_section(None, &section);
    menu
}

pub(crate) fn changed_file_context_menu(is_fully_staged: bool) -> gtk::gio::Menu {
    let menu = gtk::gio::Menu::new();

    let stage_section = gtk::gio::Menu::new();

    let stage_label = if is_fully_staged {
        gettext("Unstage")
    } else {
        gettext("Stage")
    };

    stage_section.append(Some(&stage_label), Some("changed-file.toggle-stage"));
    menu.append_section(None, &stage_section);

    let path_section = gtk::gio::Menu::new();
    path_section.append(Some(&gettext("Copy Path")), Some("changed-file.copy-path"));
    path_section.append(Some(&gettext("Copy Relative Path")), Some("changed-file.copy-relative-path"));
    path_section.append(Some(&gettext("Open in File Manager")), Some("changed-file.open-in-file-manager"));
    menu.append_section(None, &path_section);

    let ignore_section = gtk::gio::Menu::new();
    ignore_section.append(Some(&gettext("Add to .gitignore…")), Some("changed-file.ignore"));
    menu.append_section(None, &ignore_section);

    let destructive_section = gtk::gio::Menu::new();
    destructive_section.append(Some(&gettext("Discard Changes")), Some("changed-file.discard"));
    destructive_section.append(Some(&gettext("Delete File")), Some("changed-file.delete"));
    menu.append_section(None, &destructive_section);

    menu
}

pub(crate) fn repo_menu() -> gtk::gio::Menu {
    let menu = gtk::gio::Menu::new();

    let remote_section = gtk::gio::Menu::new();
    remote_section.append(Some(&gettext("_Fetch")), Some("repo.fetch"));
    remote_section.append(Some(&gettext("Pu_ll")), Some("repo.pull"));
    remote_section.append(Some(&gettext("_Push")), Some("repo.push"));
    menu.append_section(None, &remote_section);

    let tag_section = gtk::gio::Menu::new();
    tag_section.append(Some(&gettext("Create _Tag…")), Some("repo.create-tag"));
    menu.append_section(None, &tag_section);

    let archive_section = gtk::gio::Menu::new();
    archive_section.append(Some(&gettext("Create _Archive…")), Some("repo.archive"));
    menu.append_section(None, &archive_section);

    let window_section = gtk::gio::Menu::new();
    window_section.append(Some(&gettext("_New Window")), Some("app.new-window"));
    menu.append_section(None, &window_section);

    let settings_section = common_menu_section();
    settings_section.prepend(Some(&gettext("Repository _Settings…")), Some("repo.settings"));
    menu.append_section(None, &settings_section);
    menu
}
