// SPDX-FileCopyrightText: 2024 Andrey Kutejko <andy128k@gmail.com>
// SPDX-FileCopyrightText: 2026 Wladimir Palant https://palant.info/
//
// SPDX-License-Identifier: GPL-3.0-or-later

use crate::{
    chmod_component::ChmodComponent,
    connection::Connection,
    dir::Directory,
    file::{File, FileOps},
    utils::{ErrorMessage, NO_BUTTONS, SenderExt, WindowExt, dialog_button_box},
};
use gettextrs::gettext;
use gtk::{glib, prelude::*};

#[repr(C)]
#[derive(Clone, Copy, PartialEq, Eq)]
pub enum ChmodRecursiveMode {
    AllFiles = 0,
    DirectoriesOnly,
}

#[async_recursion::async_recursion(?Send)]
async fn chmod_recursively(
    parent_window: &gtk::Window,
    connection: Option<&Connection>,
    file: &File,
    permissions: u32,
    recursive: Option<ChmodRecursiveMode>,
) {
    if let Err(error) = file.chmod(permissions) {
        ErrorMessage::with_error(
            gettext("Could not chmod {}").replace("{}", &file.name()),
            &error,
        )
        .show(parent_window)
        .await;
        return;
    }

    if let Some(mode) = recursive
        && file.is_directory()
        && let Some(connection) = connection
    {
        let dir = Directory::new_from_file(connection, &*file.file());
        if let Err(error) = dir.list_files(parent_window, false).await {
            error.show(parent_window).await;
            return;
        }

        // Create a copy of the list to guard against modifications while we process it
        let files = dir
            .files()
            .iter()
            .filter(|child| !child.is_dotdot() && child.name() != "." && !child.is_symlink())
            .filter(|child| match mode {
                ChmodRecursiveMode::AllFiles => true,
                ChmodRecursiveMode::DirectoriesOnly => child.is_directory(),
            })
            .cloned()
            .collect::<Vec<_>>();
        for child in files {
            chmod_recursively(
                parent_window,
                Some(connection),
                &child,
                permissions,
                recursive,
            )
            .await;
        }
    }
}

async fn chmod_files(
    parent_window: &gtk::Window,
    connection: Option<&Connection>,
    files: &[File],
    permissions: u32,
    recursive: Option<ChmodRecursiveMode>,
) {
    for file in files {
        chmod_recursively(parent_window, connection, file, permissions, recursive).await;
    }
}

pub async fn show_chmod_dialog(
    parent_window: &gtk::Window,
    connection: Option<&Connection>,
    files: &[File],
) -> bool {
    let Some(file) = files.first() else {
        return false;
    };

    let dialog = gtk::Window::builder()
        .transient_for(parent_window)
        .title(gettext("Access Permissions"))
        .resizable(false)
        .build();
    dialog.add_css_class("dialog");

    let content_area = gtk::Box::builder()
        .orientation(gtk::Orientation::Vertical)
        .build();
    dialog.set_child(Some(&content_area));

    let chmod_component = ChmodComponent::new(file.permissions());
    content_area.append(&chmod_component);

    content_area.append(&gtk::Separator::new(gtk::Orientation::Horizontal));

    let recurse_check = gtk::CheckButton::with_label(&gettext("Apply Recursively for"));
    content_area.append(&recurse_check);

    let recurse_model =
        gtk::StringList::new(&[&gettext("All files"), &gettext("Directories only")]);
    let recurse_combo = gtk::DropDown::builder()
        .sensitive(false)
        .model(&recurse_model)
        .build();
    recurse_combo.set_selected(0);
    content_area.append(&recurse_combo);

    recurse_check.connect_toggled(glib::clone!(
        #[weak]
        recurse_combo,
        move |toggle| {
            recurse_combo.set_sensitive(toggle.is_active());
        }
    ));

    let (sender, receiver) = async_channel::bounded::<bool>(1);

    let cancel_btn = gtk::Button::builder()
        .label(gettext("_Cancel"))
        .use_underline(true)
        .build();
    cancel_btn.connect_clicked(glib::clone!(
        #[strong]
        sender,
        move |_| sender.toss(false)
    ));

    let ok_btn = gtk::Button::builder()
        .label(gettext("C_hange Permissions"))
        .use_underline(true)
        .build();
    ok_btn.connect_clicked(glib::clone!(
        #[strong]
        sender,
        move |_| sender.toss(true)
    ));

    content_area.append(&dialog_button_box(NO_BUTTONS, &[&cancel_btn, &ok_btn]));

    dialog.set_default_widget(Some(&ok_btn));
    dialog.set_cancel_widget(&cancel_btn);
    dialog.present();

    let result = receiver.recv().await == Ok(true);

    if result {
        let permissions = chmod_component.permissions();
        let recursive = recurse_check
            .is_active()
            .then(|| match recurse_combo.selected() {
                0 => ChmodRecursiveMode::AllFiles,
                _ => ChmodRecursiveMode::DirectoriesOnly,
            });

        chmod_files(
            dialog.upcast_ref(),
            connection,
            files,
            permissions,
            recursive,
        )
        .await;
    }

    dialog.close();

    result
}
