use std::{borrow::Borrow, path::PathBuf, thread::JoinHandle};

use anyhow::Result;
use eframe::egui;
use egui_extras::{Column, TableBuilder};

use crate::helper::getent::{get_groups, get_users, Group, User};

use posix_acl::{ACLEntry, PermSet, PosixACL, Qualifier, ACL_RWX};

pub struct ACLEditor {
    pub path: PathBuf,
    pub is_changed: bool,
    pub available_users: Vec<User>,
    pub available_groups: Vec<Group>,
    pub acl: PosixACL,

    pub search_user: String,
    pub search_group: String,

    pub save_thread: Option<JoinHandle<Result<()>>>,
    pub save_thread_error: Option<String>,
}

impl ACLEditor {
    pub fn new() -> Self {
        Self {
            path: PathBuf::new(),
            is_changed: false,
            available_groups: get_groups().unwrap(),
            available_users: get_users().unwrap(),
            acl: PosixACL::new(ACL_RWX, ACL_RWX, ACL_RWX),
            search_user: String::new(),
            search_group: String::new(),
            save_thread: None,
            save_thread_error: None,
        }
    }
    pub fn show(&mut self, ui: &mut egui::Ui, selected_path: &PathBuf) {
        let mut show_save_or_dicard_message = false;
        ui.vertical(|ui| {
            if self.save_thread.is_some() {
                if self.save_thread.as_ref().unwrap().is_finished() {
                    let save_thread = std::mem::replace(&mut self.save_thread, None);
                    let result = save_thread.unwrap().join();
                    match result {
                        Ok(Ok(_)) => {
                            ui.label("Saved successfully!");
                            self.save_thread_error = None;
                            self.is_changed = false;
                        }
                        Ok(Err(e)) => {
                            self.save_thread_error = Some(format!("Failed to save {:?}", e));
                        }
                        Err(e) => {
                            self.save_thread_error = Some(format!("Failed to join {:?}", e));
                        }
                    }
                } else {
                    egui::Frame::default()
                        .fill(egui::Color32::GREEN)
                        .inner_margin(4.0)
                        .show(ui, |ui| {
                            ui.label("Apply changes...");
                        });
                }
            } else if let Some(err) = &self.save_thread_error.clone() {
                egui::Frame::default()
                    .fill(egui::Color32::DARK_RED)
                    .inner_margin(4.0)
                    .show(ui, |ui| {
                        ui.label(err);

                        if ui.button("OK").clicked() {
                            self.save_thread_error = None;
                        }
                    });
            } else {
                ui.heading(format!("Editing: {:?}", self.path));
                ui.separator();

                if self.path != *selected_path {
                    if !self.is_changed {
                        // Load ACLs
                        self.path = selected_path.clone();
                        self.acl = PosixACL::new_from_file(&self.path, false).unwrap();
                    } else {
                        show_save_or_dicard_message = true;
                    }
                }

                self.acl_table(ui);

                ui.separator();
                ui.label("Add new entry:");

                self.new_user(ui);
                self.new_group(ui);

                ui.separator();

                if show_save_or_dicard_message {
                    egui::Frame::default()
                        .fill(egui::Color32::YELLOW)
                        .inner_margin(4.0)
                        .show(ui, |ui| {
                            ui.label("You have unsaved changes! Save or discard to continue.");
                        });
                }

                ui.horizontal(|ui| {
                    if ui.button("Save").clicked() {
                        // TODO: Save ACLs
                        let path = self.path.clone();
                        let acl = self.acl.clone();
                        self.save_thread_error = None;
                        self.save_thread = Some(std::thread::spawn(move || {
                            crate::helper::acl_writer::write_acl_recursive(path, acl)
                        }));
                    }

                    if ui.button("Discard").clicked() {
                        self.is_changed = false;
                        self.path.clear();
                    }
                });
            }
        });
    }

    fn new_group(&mut self, ui: &mut egui::Ui) {
        ui.label("Group:");
        let selected = filter_dropbox(
            ui,
            "group_popup",
            &mut self.search_group,
            self.available_groups.iter(),
            |group, filter| group.name.contains(filter),
            |group| group.name.as_str(),
        );

        if let Some(selected) = selected {
            self.acl
                .entries
                .push(ACLEntry(Qualifier::Group(selected.gid), PermSet::ACL_RWX));
            self.is_changed = true;
        }
    }

    fn new_user(&mut self, ui: &mut egui::Ui) {
        ui.label("User:");
        let selected = filter_dropbox(
            ui,
            "user_popup",
            &mut self.search_user,
            self.available_users.iter(),
            |user, filter| user.name.contains(filter),
            |user| user.name.as_str(),
        );

        if let Some(selected) = selected {
            self.acl
                .entries
                .push(ACLEntry(Qualifier::User(selected.uid), PermSet::ACL_RWX));
            self.is_changed = true;
        }
    }

    fn acl_table(&mut self, ui: &mut egui::Ui) {
        TableBuilder::new(ui)
            .auto_shrink([false, true])
            .striped(true)
            .column(Column::auto().resizable(false))
            .column(Column::remainder().resizable(false))
            .column(Column::auto().resizable(false))
            .column(Column::auto().resizable(false))
            .column(Column::auto().resizable(false))
            .column(Column::auto().resizable(false))
            .header(20.0, |mut header| {
                header.col(|ui| {
                    ui.heading("Type");
                });
                header.col(|ui| {
                    ui.heading("Name");
                });
                header.col(|ui| {
                    ui.heading("R");
                });
                header.col(|ui| {
                    ui.heading("W");
                });
                header.col(|ui| {
                    ui.heading("X");
                });
                header.col(|ui| {
                    ui.heading("D");
                });
            })
            .body(|mut body| {
                let mut to_delete: Vec<Qualifier> = Vec::new();

                self.acl.entries.sort();
                for entry in &mut self.acl.entries {
                    body.row(30.0, |mut row| {
                        row.col(|ui| {
                            let label = match entry.0 {
                                Qualifier::User(_) => "U",
                                Qualifier::Group(_) => "G",
                                Qualifier::Other => "",
                                Qualifier::GroupObj => "G",
                                Qualifier::UserObj => "U",
                                Qualifier::Mask => "",
                            };
                            ui.label(label);
                        });
                        row.col(|ui| {
                            let label = match entry.0 {
                                Qualifier::User(uid) => self
                                    .available_users
                                    .iter()
                                    .find(|user| user.uid == uid)
                                    .map(|user| user.name.clone())
                                    .unwrap_or_else(|| format!("Unknown user {}", uid)),
                                Qualifier::Group(gid) => self
                                    .available_groups
                                    .iter()
                                    .find(|group| group.gid == gid)
                                    .map(|group| group.name.clone())
                                    .unwrap_or_else(|| format!("Unknown group {}", gid)),
                                Qualifier::Other => "Other".to_string(),
                                Qualifier::GroupObj => "GroupObj".to_string(),
                                Qualifier::UserObj => "UserObj".to_string(),
                                Qualifier::Mask => "Mask".to_string(),
                            };

                            ui.label(label);
                        });
                        row.col(|ui| {
                            let mut checked = entry.1.contains(PermSet::ACL_READ);
                            if ui.checkbox(&mut checked, "").changed() {
                                if checked {
                                    entry.1.insert(PermSet::ACL_READ);
                                    self.is_changed = true;
                                } else {
                                    entry.1.remove(PermSet::ACL_READ);
                                    self.is_changed = true;
                                }
                            }
                        });
                        row.col(|ui| {
                            let mut checked = entry.1.contains(PermSet::ACL_WRITE);
                            if ui.checkbox(&mut checked, "").changed() {
                                if checked {
                                    entry.1.insert(PermSet::ACL_WRITE);
                                    self.is_changed = true;
                                } else {
                                    entry.1.remove(PermSet::ACL_WRITE);
                                    self.is_changed = true;
                                }
                            }
                        });
                        row.col(|ui| {
                            let mut checked = entry.1.contains(PermSet::ACL_EXECUTE);
                            if ui.checkbox(&mut checked, "").changed() {
                                if checked {
                                    entry.1.insert(PermSet::ACL_EXECUTE);
                                    self.is_changed = true;
                                } else {
                                    entry.1.remove(PermSet::ACL_EXECUTE);
                                    self.is_changed = true;
                                }
                            }
                        });

                        row.col(|ui| match entry.0 {
                            Qualifier::User(_) | Qualifier::Group(_) => {
                                if ui.button("🗑").clicked() {
                                    to_delete.push(entry.0.clone());
                                }
                            }
                            _ => {}
                        });
                    });
                }

                for entry in to_delete {
                    self.acl.entries.retain(|e| e.0 != entry);
                    self.is_changed = true;
                }
            });
    }
}

// fn searchable_dropdown()

fn filter_dropbox<
    'a,
    T: PartialEq + 'a,
    I: Iterator<Item = &'a T> + 'a,
    F: Fn(&'a T, &str) -> bool,
    L: Fn(&'a T) -> &str,
>(
    ui: &mut egui::Ui,
    popup_id: &str,
    search_buf: &mut String,
    items: I,
    filter_items: F,
    get_label: L,
) -> Option<&'a T> {
    let search_field = ui.text_edit_singleline(search_buf);
    let popup_id = ui.make_persistent_id(popup_id);

    if search_field.gained_focus() {
        ui.memory_mut(|mem| mem.open_popup(popup_id));
    }

    let mut selected = None;
    egui::popup_above_or_below_widget(
        &ui,
        popup_id,
        &search_field,
        egui::AboveOrBelow::Below,
        egui::PopupCloseBehavior::CloseOnClick,
        |ui| {
            ui.set_min_width(200.0);
            ui.set_max_height(300.0);
            egui::ScrollArea::vertical().show(ui, |ui| {
                let filtered = items.filter(|elm| filter_items(elm, &search_buf));

                for user in filtered {
                    let value = ui.selectable_value(&mut selected, Some(user), get_label(user));
                    if value.clicked() {
                        search_field.surrender_focus();
                    }
                }
            });
        },
    );

    if selected.is_some() {
        search_buf.clear();
    }

    selected
}
