use crate::{MgmtApi, Result};

use async_trait::async_trait;

use std::collections::HashSet;

#[async_trait]
pub trait RbacApi: MgmtApi {
    async fn add_permission_for_user(
        &mut self,
        user: &str,
        permission: Vec<String>,
    ) -> Result<bool>;
    async fn add_permissions_for_user(
        &mut self,
        user: &str,
        permissions: Vec<Vec<String>>,
    ) -> Result<bool>;
    async fn add_role_for_user(
        &mut self,
        user: &str,
        role: &str,
        domain: Option<&str>,
    ) -> Result<bool>;
    async fn add_roles_for_user(
        &mut self,
        user: &str,
        roles: Vec<String>,
        domain: Option<&str>,
    ) -> Result<bool>;
    async fn delete_role_for_user(
        &mut self,
        user: &str,
        role: &str,
        domain: Option<&str>,
    ) -> Result<bool>;
    async fn delete_roles_for_user(
        &mut self,
        user: &str,
        domain: Option<&str>,
    ) -> Result<bool>;
    async fn delete_user(&mut self, name: &str) -> Result<bool>;
    async fn delete_role(&mut self, name: &str) -> Result<bool>;

    async fn delete_permission(
        &mut self,
        permission: Vec<String>,
    ) -> Result<bool> {
        self.remove_filtered_policy(1, permission).await
    }
    async fn delete_permission_for_user(
        &mut self,
        user: &str,
        permission: Vec<String>,
    ) -> Result<bool>;

    async fn delete_permissions_for_user(
        &mut self,
        user: &str,
    ) -> Result<bool> {
        self.remove_filtered_policy(
            0,
            vec![user].iter().map(|s| (*s).to_string()).collect(),
        )
        .await
    }

    fn get_roles_for_user(
        &mut self,
        name: &str,
        domain: Option<&str>,
    ) -> Vec<String>;
    fn get_users_for_role(
        &self,
        name: &str,
        domain: Option<&str>,
    ) -> Vec<String>;
    fn has_role_for_user(
        &mut self,
        name: &str,
        role: &str,
        domain: Option<&str>,
    ) -> bool;
    fn get_permissions_for_user(
        &self,
        user: &str,
        domain: Option<&str>,
    ) -> Vec<Vec<String>>;
    fn has_permission_for_user(
        &self,
        user: &str,
        permission: Vec<String>,
    ) -> bool;
    fn get_implicit_roles_for_user(
        &mut self,
        name: &str,
        domain: Option<&str>,
    ) -> Vec<String>;
    fn get_implicit_permissions_for_user(
        &mut self,
        name: &str,
        domain: Option<&str>,
    ) -> Vec<Vec<String>>;
    async fn get_implicit_users_for_permission(
        &self,
        permission: Vec<String>,
    ) -> Vec<String>;
}

#[async_trait]
impl<T> RbacApi for T
where
    T: MgmtApi,
{
    async fn add_permission_for_user(
        &mut self,
        user: &str,
        permission: Vec<String>,
    ) -> Result<bool> {
        let mut perm = permission;
        perm.insert(0, user.to_string());
        self.add_policy(perm).await
    }

    async fn add_permissions_for_user(
        &mut self,
        user: &str,
        permissions: Vec<Vec<String>>,
    ) -> Result<bool> {
        let perms = permissions
            .into_iter()
            .map(|mut p| {
                p.insert(0, user.to_string());
                p
            })
            .collect();
        self.add_policies(perms).await
    }

    async fn add_role_for_user(
        &mut self,
        user: &str,
        role: &str,
        domain: Option<&str>,
    ) -> Result<bool> {
        self.add_grouping_policy(if let Some(domain) = domain {
            vec![user, role, domain]
                .iter()
                .map(|s| (*s).to_string())
                .collect()
        } else {
            vec![user, role].iter().map(|s| (*s).to_string()).collect()
        })
        .await
    }

    async fn add_roles_for_user(
        &mut self,
        user: &str,
        roles: Vec<String>,
        domain: Option<&str>,
    ) -> Result<bool> {
        self.add_grouping_policies(
            roles
                .into_iter()
                .map(|role| {
                    if let Some(domain) = domain {
                        vec![user.to_string(), role, domain.to_string()]
                    } else {
                        vec![user.to_string(), role]
                    }
                })
                .collect(),
        )
        .await
    }

    async fn delete_role_for_user(
        &mut self,
        user: &str,
        role: &str,
        domain: Option<&str>,
    ) -> Result<bool> {
        self.remove_grouping_policy(if let Some(domain) = domain {
            vec![user, role, domain]
                .iter()
                .map(|s| (*s).to_string())
                .collect()
        } else {
            vec![user, role].iter().map(|s| (*s).to_string()).collect()
        })
        .await
    }

    async fn delete_roles_for_user(
        &mut self,
        user: &str,
        domain: Option<&str>,
    ) -> Result<bool> {
        self.remove_filtered_grouping_policy(
            0,
            if let Some(domain) = domain {
                vec![user, "", domain]
                    .iter()
                    .map(|s| (*s).to_string())
                    .collect()
            } else {
                vec![user].iter().map(|s| (*s).to_string()).collect()
            },
        )
        .await
    }

    fn get_roles_for_user(
        &mut self,
        name: &str,
        domain: Option<&str>,
    ) -> Vec<String> {
        let mut roles = vec![];
        if let Some(t1) = self.get_mut_model().get_mut_model().get_mut("g") {
            if let Some(t2) = t1.get_mut("g") {
                roles = t2.rm.write().unwrap().get_roles(name, domain);
            }
        }

        roles
    }

    fn get_users_for_role(
        &self,
        name: &str,
        domain: Option<&str>,
    ) -> Vec<String> {
        if let Some(t1) = self.get_model().get_model().get("g") {
            if let Some(t2) = t1.get("g") {
                return t2.rm.read().unwrap().get_users(name, domain);
            }
        }
        return vec![];
    }

    fn has_role_for_user(
        &mut self,
        name: &str,
        role: &str,
        domain: Option<&str>,
    ) -> bool {
        let roles = self.get_roles_for_user(name, domain);
        let mut has_role = false;
        for r in roles {
            if r == role {
                has_role = true;
                break;
            }
        }
        has_role
    }

    async fn delete_user(&mut self, name: &str) -> Result<bool> {
        self.remove_filtered_grouping_policy(0, vec![name.to_string()])
            .await
    }

    async fn delete_role(&mut self, name: &str) -> Result<bool> {
        let res1 = self
            .remove_filtered_grouping_policy(1, vec![name.to_string()])
            .await?;
        let res2 = self
            .remove_filtered_policy(0, vec![name.to_string()])
            .await?;
        Ok(res1 || res2)
    }

    async fn delete_permission_for_user(
        &mut self,
        user: &str,
        permission: Vec<String>,
    ) -> Result<bool> {
        let mut permission = permission;
        permission.insert(0, user.to_string());
        self.remove_policy(permission).await
    }

    fn get_permissions_for_user(
        &self,
        user: &str,
        domain: Option<&str>,
    ) -> Vec<Vec<String>> {
        self.get_filtered_policy(0, {
            if let Some(domain) = domain {
                vec![user, domain]
                    .iter()
                    .map(|s| (*s).to_string())
                    .collect()
            } else {
                vec![user].iter().map(|s| (*s).to_string()).collect()
            }
        })
    }

    fn has_permission_for_user(
        &self,
        user: &str,
        permission: Vec<String>,
    ) -> bool {
        let mut permission = permission;
        permission.insert(0, user.to_string());
        self.has_policy(permission)
    }

    fn get_implicit_roles_for_user(
        &mut self,
        name: &str,
        domain: Option<&str>,
    ) -> Vec<String> {
        let mut res: HashSet<String> = HashSet::new();
        let mut q: Vec<String> = vec![name.to_owned()];
        while !q.is_empty() {
            let name = q.swap_remove(0);
            let roles = self
                .get_role_manager()
                .write()
                .unwrap()
                .get_roles(&name, domain);
            for r in roles.into_iter() {
                if res.insert(r.to_owned()) {
                    q.push(r);
                }
            }
        }
        res.into_iter().collect()
    }

    fn get_implicit_permissions_for_user(
        &mut self,
        user: &str,
        domain: Option<&str>,
    ) -> Vec<Vec<String>> {
        let mut roles = self.get_implicit_roles_for_user(user, domain);
        roles.insert(0, user.to_owned());

        let mut res = vec![];

        for role in roles.iter() {
            let permissions = self.get_permissions_for_user(role, domain);
            res.extend(permissions);
        }
        res
    }

    async fn get_implicit_users_for_permission(
        &self,
        permission: Vec<String>,
    ) -> Vec<String> {
        let mut subjects = self.get_all_subjects();
        let roles = self.get_all_roles();

        subjects.extend(
            roles
                .iter()
                .map(|role| {
                    self.get_role_manager()
                        .read()
                        .unwrap()
                        .get_users(role, None)
                })
                .flatten(),
        );

        let users: Vec<String> = subjects
            .into_iter()
            .filter(|subject| !roles.contains(subject))
            .collect();

        let mut res: Vec<String> = vec![];
        for user in users.iter() {
            let mut req = permission.clone();
            req.insert(0, user.to_string());
            if let Ok(r) = self.enforce(req) {
                if r && !res.contains(user) {
                    res.push(user.to_owned());
                }
            }
        }
        res
    }
}

#[cfg(test)]
mod tests {
    use crate::prelude::*;

    fn sort_unstable<T: Ord>(mut v: Vec<T>) -> Vec<T> {
        v.sort_unstable();
        v
    }

    #[cfg(not(target_arch = "wasm32"))]
    #[cfg_attr(
        all(feature = "runtime-async-std", not(target_arch = "wasm32")),
        async_std::test
    )]
    #[cfg_attr(
        all(feature = "runtime-tokio", not(target_arch = "wasm32")),
        tokio::test
    )]
    async fn test_role_api() {
        let m = DefaultModel::from_file("examples/rbac_model.conf")
            .await
            .unwrap();

        let adapter = FileAdapter::new("examples/rbac_policy.csv");
        let mut e = Enforcer::new(m, adapter).await.unwrap();

        assert_eq!(vec!["data2_admin"], e.get_roles_for_user("alice", None));
        assert_eq!(vec![String::new(); 0], e.get_roles_for_user("bob", None));
        assert_eq!(
            vec![String::new(); 0],
            e.get_roles_for_user("data2_admin", None)
        );
        assert_eq!(
            vec![String::new(); 0],
            e.get_roles_for_user("non_exists", None)
        );

        assert_eq!(false, e.has_role_for_user("alice", "data1_admin", None));
        assert_eq!(true, e.has_role_for_user("alice", "data2_admin", None));

        e.add_role_for_user("alice", "data1_admin", None)
            .await
            .unwrap();
        assert_eq!(
            vec!["data1_admin", "data2_admin"],
            sort_unstable(e.get_roles_for_user("alice", None))
        );
        assert_eq!(vec![String::new(); 0], e.get_roles_for_user("bob", None));
        assert_eq!(
            vec![String::new(); 0],
            e.get_roles_for_user("data2_admin", None)
        );

        e.delete_role_for_user("alice", "data1_admin", None)
            .await
            .unwrap();
        assert_eq!(vec!["data2_admin"], e.get_roles_for_user("alice", None));
        assert_eq!(vec![String::new(); 0], e.get_roles_for_user("bob", None));
        assert_eq!(
            vec![String::new(); 0],
            e.get_roles_for_user("data2_admin", None)
        );

        e.delete_roles_for_user("alice", None).await.unwrap();
        assert_eq!(vec![String::new(); 0], e.get_roles_for_user("alice", None));
        assert_eq!(vec![String::new(); 0], e.get_roles_for_user("bob", None));
        assert_eq!(
            vec![String::new(); 0],
            e.get_roles_for_user("data2_admin", None)
        );

        e.add_roles_for_user(
            "bob",
            vec!["data1_admin", "data2_admin"]
                .iter()
                .map(|s| s.to_string())
                .collect(),
            None,
        )
        .await
        .unwrap();
        assert_eq!(vec![String::new(); 0], e.get_roles_for_user("alice", None));
        assert_eq!(
            vec!["data1_admin", "data2_admin"],
            sort_unstable(e.get_roles_for_user("bob", None))
        );

        e.delete_roles_for_user("bob", None).await.unwrap();
        assert_eq!(vec![String::new(); 0], e.get_roles_for_user("alice", None));
        assert_eq!(vec![String::new(); 0], e.get_roles_for_user("bob", None));

        e.add_role_for_user("alice", "data1_admin", None)
            .await
            .unwrap();
        e.delete_user("alice").await.unwrap();
        assert_eq!(vec![String::new(); 0], e.get_roles_for_user("alice", None));
        assert_eq!(vec![String::new(); 0], e.get_roles_for_user("bob", None));
        assert_eq!(
            vec![String::new(); 0],
            e.get_roles_for_user("data2_admin", None)
        );

        e.add_role_for_user("alice", "data2_admin", None)
            .await
            .unwrap();
        assert_eq!(true, e.enforce(("alice", "data1", "read")).unwrap());
        assert_eq!(false, e.enforce(("alice", "data1", "write")).unwrap());
        assert_eq!(true, e.enforce(("alice", "data2", "read")).unwrap());
        assert_eq!(true, e.enforce(("alice", "data2", "write")).unwrap());
        assert_eq!(false, e.enforce(("bob", "data1", "read")).unwrap());
        assert_eq!(false, e.enforce(("bob", "data1", "write")).unwrap());
        assert_eq!(false, e.enforce(("bob", "data2", "read")).unwrap());
        assert_eq!(true, e.enforce(("bob", "data2", "write")).unwrap());

        e.delete_role("data2_admin").await.unwrap();
        assert_eq!(true, e.enforce(("alice", "data1", "read")).unwrap());
        assert_eq!(false, e.enforce(("alice", "data1", "write")).unwrap());
        assert_eq!(false, e.enforce(("alice", "data2", "read")).unwrap());
        assert_eq!(false, e.enforce(("alice", "data2", "write")).unwrap());
        assert_eq!(false, e.enforce(("bob", "data1", "read")).unwrap());
        assert_eq!(false, e.enforce(("bob", "data1", "write")).unwrap());
        assert_eq!(false, e.enforce(("bob", "data2", "read")).unwrap());
        assert_eq!(true, e.enforce(("bob", "data2", "write")).unwrap());
    }

    #[cfg(not(target_arch = "wasm32"))]
    #[cfg_attr(
        all(feature = "runtime-async-std", not(target_arch = "wasm32")),
        async_std::test
    )]
    #[cfg_attr(
        all(feature = "runtime-tokio", not(target_arch = "wasm32")),
        tokio::test
    )]
    async fn test_role_api_threads() {
        use std::sync::{Arc, RwLock};
        use std::thread;

        #[cfg(feature = "runtime-async-std")]
        use async_std::task;

        let m = DefaultModel::from_file("examples/rbac_model.conf")
            .await
            .unwrap();

        let adapter = FileAdapter::new("examples/rbac_policy.csv");
        let e = Arc::new(RwLock::new(Enforcer::new(m, adapter).await.unwrap()));
        let ee = e.clone();

        assert_eq!(
            vec!["data2_admin"],
            e.write().unwrap().get_roles_for_user("alice", None)
        );
        assert_eq!(
            vec![String::new(); 0],
            e.write().unwrap().get_roles_for_user("bob", None)
        );
        assert_eq!(
            vec![String::new(); 0],
            e.write().unwrap().get_roles_for_user("data2_admin", None)
        );
        assert_eq!(
            vec![String::new(); 0],
            e.write().unwrap().get_roles_for_user("non_exists", None)
        );

        assert_eq!(
            false,
            e.write()
                .unwrap()
                .has_role_for_user("alice", "data1_admin", None)
        );
        assert_eq!(
            true,
            e.write()
                .unwrap()
                .has_role_for_user("alice", "data2_admin", None)
        );

        thread::spawn(move || {
            #[cfg(feature = "runtime-async-std")]
            {
                task::block_on(async move {
                    ee.write()
                        .unwrap()
                        .add_role_for_user("alice", "data1_admin", None)
                        .await
                        .unwrap();

                    assert_eq!(
                        vec!["data1_admin", "data2_admin"],
                        sort_unstable(
                            ee.write()
                                .unwrap()
                                .get_roles_for_user("alice", None)
                        )
                    );
                    assert_eq!(
                        vec![String::new(); 0],
                        ee.write().unwrap().get_roles_for_user("bob", None)
                    );
                    assert_eq!(
                        vec![String::new(); 0],
                        ee.write()
                            .unwrap()
                            .get_roles_for_user("data2_admin", None)
                    );

                    ee.write()
                        .unwrap()
                        .add_roles_for_user(
                            "bob",
                            vec!["data2_admin"]
                                .iter()
                                .map(|s| s.to_string())
                                .collect(),
                            None,
                        )
                        .await
                        .unwrap();

                    assert_eq!(
                        vec!["data1_admin", "data2_admin"],
                        sort_unstable(
                            ee.write()
                                .unwrap()
                                .get_roles_for_user("alice", None)
                        )
                    );
                    assert_eq!(
                        vec!["data2_admin"],
                        ee.write().unwrap().get_roles_for_user("bob", None)
                    );
                    assert_eq!(
                        vec![String::new(); 0],
                        ee.write()
                            .unwrap()
                            .get_roles_for_user("data2_admin", None)
                    );
                });
            }

            #[cfg(feature = "runtime-tokio")]
            {
                tokio::runtime::Builder::new()
                    .basic_scheduler()
                    .enable_all()
                    .build()
                    .unwrap()
                    .block_on(async move {
                        ee.write()
                            .unwrap()
                            .add_role_for_user("alice", "data1_admin", None)
                            .await
                            .unwrap();

                        assert_eq!(
                            vec!["data2_admin", "data1_admin"],
                            ee.write()
                                .unwrap()
                                .get_roles_for_user("alice", None)
                        );
                        assert_eq!(
                            vec![String::new(); 0],
                            ee.write().unwrap().get_roles_for_user("bob", None)
                        );
                        assert_eq!(
                            vec![String::new(); 0],
                            ee.write()
                                .unwrap()
                                .get_roles_for_user("data2_admin", None)
                        );

                        ee.write()
                            .unwrap()
                            .add_roles_for_user(
                                "bob",
                                vec!["data2_admin".to_owned()],
                                None,
                            )
                            .await
                            .unwrap();

                        assert_eq!(
                            vec!["data2_admin", "data1_admin"],
                            ee.write()
                                .unwrap()
                                .get_roles_for_user("alice", None)
                        );
                        assert_eq!(
                            vec!["data2_admin"],
                            ee.write().unwrap().get_roles_for_user("bob", None)
                        );
                        assert_eq!(
                            vec![String::new(); 0],
                            ee.write()
                                .unwrap()
                                .get_roles_for_user("data2_admin", None)
                        );
                    });
            }
        })
        .join()
        .unwrap();

        e.write()
            .unwrap()
            .delete_role_for_user("alice", "data1_admin", None)
            .await
            .unwrap();
        e.write()
            .unwrap()
            .delete_roles_for_user("bob", None)
            .await
            .unwrap();
        assert_eq!(
            vec!["data2_admin"],
            e.write().unwrap().get_roles_for_user("alice", None)
        );
        assert_eq!(
            vec![String::new(); 0],
            e.write().unwrap().get_roles_for_user("bob", None)
        );
        assert_eq!(
            vec![String::new(); 0],
            e.write().unwrap().get_roles_for_user("data2_admin", None)
        );

        e.write()
            .unwrap()
            .delete_roles_for_user("alice", None)
            .await
            .unwrap();
        assert_eq!(
            vec![String::new(); 0],
            e.write().unwrap().get_roles_for_user("alice", None)
        );
        assert_eq!(
            vec![String::new(); 0],
            e.write().unwrap().get_roles_for_user("bob", None)
        );
        assert_eq!(
            vec![String::new(); 0],
            e.write().unwrap().get_roles_for_user("data2_admin", None)
        );

        e.write()
            .unwrap()
            .add_role_for_user("alice", "data1_admin", None)
            .await
            .unwrap();
        e.write().unwrap().delete_user("alice").await.unwrap();
        assert_eq!(
            vec![String::new(); 0],
            e.write().unwrap().get_roles_for_user("alice", None)
        );
        assert_eq!(
            vec![String::new(); 0],
            e.write().unwrap().get_roles_for_user("bob", None)
        );
        assert_eq!(
            vec![String::new(); 0],
            e.write().unwrap().get_roles_for_user("data2_admin", None)
        );

        e.write()
            .unwrap()
            .add_role_for_user("alice", "data2_admin", None)
            .await
            .unwrap();
        assert_eq!(
            true,
            e.write()
                .unwrap()
                .enforce(("alice", "data1", "read"))
                .unwrap()
        );
        assert_eq!(
            false,
            e.write()
                .unwrap()
                .enforce(("alice", "data1", "write"))
                .unwrap()
        );
        assert_eq!(
            true,
            e.write()
                .unwrap()
                .enforce(("alice", "data2", "read"))
                .unwrap()
        );
        assert_eq!(
            true,
            e.write()
                .unwrap()
                .enforce(("alice", "data2", "write"))
                .unwrap()
        );
        assert_eq!(
            false,
            e.write()
                .unwrap()
                .enforce(("bob", "data1", "read"))
                .unwrap()
        );
        assert_eq!(
            false,
            e.write()
                .unwrap()
                .enforce(("bob", "data1", "write"))
                .unwrap()
        );
        assert_eq!(
            false,
            e.write()
                .unwrap()
                .enforce(("bob", "data2", "read"))
                .unwrap()
        );
        assert_eq!(
            true,
            e.write()
                .unwrap()
                .enforce(("bob", "data2", "write"))
                .unwrap()
        );

        e.write().unwrap().delete_role("data2_admin").await.unwrap();
        assert_eq!(
            true,
            e.write()
                .unwrap()
                .enforce(("alice", "data1", "read"))
                .unwrap()
        );
        assert_eq!(
            false,
            e.write()
                .unwrap()
                .enforce(("alice", "data1", "write"))
                .unwrap()
        );
        assert_eq!(
            false,
            e.write()
                .unwrap()
                .enforce(("alice", "data2", "read"))
                .unwrap()
        );
        assert_eq!(
            false,
            e.write()
                .unwrap()
                .enforce(("alice", "data2", "write"))
                .unwrap()
        );
        assert_eq!(
            false,
            e.write()
                .unwrap()
                .enforce(("bob", "data1", "read"))
                .unwrap()
        );
        assert_eq!(
            false,
            e.write()
                .unwrap()
                .enforce(("bob", "data1", "write"))
                .unwrap()
        );
        assert_eq!(
            false,
            e.write()
                .unwrap()
                .enforce(("bob", "data2", "read"))
                .unwrap()
        );
        assert_eq!(
            true,
            e.write()
                .unwrap()
                .enforce(("bob", "data2", "write"))
                .unwrap()
        );
    }

    #[cfg(not(target_arch = "wasm32"))]
    #[cfg_attr(
        all(feature = "runtime-async-std", not(target_arch = "wasm32")),
        async_std::test
    )]
    #[cfg_attr(
        all(feature = "runtime-tokio", not(target_arch = "wasm32")),
        tokio::test
    )]
    async fn test_permission_api() {
        let m = DefaultModel::from_file(
            "examples/basic_without_resources_model.conf",
        )
        .await
        .unwrap();

        let adapter =
            FileAdapter::new("examples/basic_without_resources_policy.csv");
        let mut e = Enforcer::new(m, adapter).await.unwrap();

        assert_eq!(true, e.enforce(("alice", "read")).unwrap());
        assert_eq!(false, e.enforce(("alice", "write")).unwrap());
        assert_eq!(false, e.enforce(("bob", "read")).unwrap());
        assert_eq!(true, e.enforce(("bob", "write")).unwrap());

        assert_eq!(
            vec![vec!["alice", "read"]],
            e.get_permissions_for_user("alice", None)
        );
        assert_eq!(
            vec![vec!["bob", "write"]],
            e.get_permissions_for_user("bob", None)
        );

        assert_eq!(
            true,
            e.has_permission_for_user(
                "alice",
                vec!["read"].iter().map(|s| s.to_string()).collect()
            )
        );
        assert_eq!(
            false,
            e.has_permission_for_user(
                "alice",
                vec!["write"].iter().map(|s| s.to_string()).collect()
            )
        );
        assert_eq!(
            false,
            e.has_permission_for_user(
                "bob",
                vec!["read"].iter().map(|s| s.to_string()).collect()
            )
        );
        assert_eq!(
            true,
            e.has_permission_for_user(
                "bob",
                vec!["write"].iter().map(|s| s.to_string()).collect()
            )
        );

        e.delete_permission(
            vec!["read"].iter().map(|s| s.to_string()).collect(),
        )
        .await
        .unwrap();

        assert_eq!(false, e.enforce(("alice", "read")).unwrap());
        assert_eq!(false, e.enforce(("alice", "write")).unwrap());
        assert_eq!(false, e.enforce(("bob", "read")).unwrap());
        assert_eq!(true, e.enforce(("bob", "write")).unwrap());

        e.add_permission_for_user(
            "bob",
            vec!["read"].iter().map(|s| s.to_string()).collect(),
        )
        .await
        .unwrap();
        e.add_permissions_for_user(
            "eve",
            vec![
                vec!["read"].iter().map(|s| s.to_string()).collect(),
                vec!["write"].iter().map(|s| s.to_string()).collect(),
            ],
        )
        .await
        .unwrap();

        assert_eq!(false, e.enforce(("alice", "read")).unwrap());
        assert_eq!(false, e.enforce(("alice", "write")).unwrap());
        assert_eq!(true, e.enforce(("bob", "read")).unwrap());
        assert_eq!(true, e.enforce(("bob", "write")).unwrap());
        assert_eq!(true, e.enforce(("eve", "read")).unwrap());
        assert_eq!(true, e.enforce(("eve", "write")).unwrap());

        e.delete_permission_for_user(
            "bob",
            vec!["read"].iter().map(|s| s.to_string()).collect(),
        )
        .await
        .unwrap();

        assert_eq!(false, e.enforce(("alice", "read")).unwrap());
        assert_eq!(false, e.enforce(("alice", "write")).unwrap());
        assert_eq!(false, e.enforce(("bob", "read")).unwrap());
        assert_eq!(true, e.enforce(("bob", "write")).unwrap());
        assert_eq!(true, e.enforce(("eve", "read")).unwrap());
        assert_eq!(true, e.enforce(("eve", "write")).unwrap());

        e.delete_permissions_for_user("bob").await.unwrap();
        e.delete_permissions_for_user("eve").await.unwrap();

        assert_eq!(false, e.enforce(("alice", "read")).unwrap());
        assert_eq!(false, e.enforce(("alice", "write")).unwrap());
        assert_eq!(false, e.enforce(("bob", "read")).unwrap());
        assert_eq!(false, e.enforce(("bob", "write")).unwrap());
        assert_eq!(false, e.enforce(("eve", "read")).unwrap());
        assert_eq!(false, e.enforce(("eve", "write")).unwrap());
    }

    #[cfg(not(target_arch = "wasm32"))]
    #[cfg_attr(
        all(feature = "runtime-async-std", not(target_arch = "wasm32")),
        async_std::test
    )]
    #[cfg_attr(
        all(feature = "runtime-tokio", not(target_arch = "wasm32")),
        tokio::test
    )]
    async fn test_implicit_role_api() {
        let m = DefaultModel::from_file("examples/rbac_model.conf")
            .await
            .unwrap();

        let adapter =
            FileAdapter::new("examples/rbac_with_hierarchy_policy.csv");
        let mut e = Enforcer::new(m, adapter).await.unwrap();

        assert_eq!(
            vec![vec!["alice", "data1", "read"]],
            e.get_permissions_for_user("alice", None)
        );
        assert_eq!(
            vec![vec!["bob", "data2", "write"]],
            e.get_permissions_for_user("bob", None)
        );

        assert_eq!(
            vec!["admin", "data1_admin", "data2_admin"],
            sort_unstable(e.get_implicit_roles_for_user("alice", None))
        );
        assert_eq!(
            vec![String::new(); 0],
            e.get_implicit_roles_for_user("bob", None)
        );
    }

    #[cfg(not(target_arch = "wasm32"))]
    #[cfg_attr(
        all(feature = "runtime-async-std", not(target_arch = "wasm32")),
        async_std::test
    )]
    #[cfg_attr(
        all(feature = "runtime-tokio", not(target_arch = "wasm32")),
        tokio::test
    )]
    async fn test_implicit_permission_api() {
        let m = DefaultModel::from_file("examples/rbac_model.conf")
            .await
            .unwrap();

        let adapter =
            FileAdapter::new("examples/rbac_with_hierarchy_policy.csv");
        let mut e = Enforcer::new(m, adapter).await.unwrap();

        assert_eq!(
            vec![vec!["alice", "data1", "read"]],
            e.get_permissions_for_user("alice", None)
        );
        assert_eq!(
            vec![vec!["bob", "data2", "write"]],
            e.get_permissions_for_user("bob", None)
        );

        assert_eq!(
            vec![
                vec!["alice", "data1", "read"],
                vec!["data1_admin", "data1", "read"],
                vec!["data1_admin", "data1", "write"],
                vec!["data2_admin", "data2", "read"],
                vec!["data2_admin", "data2", "write"],
            ],
            sort_unstable(e.get_implicit_permissions_for_user("alice", None))
        );
        assert_eq!(
            vec![vec!["bob", "data2", "write"]],
            e.get_implicit_permissions_for_user("bob", None)
        );
    }

    #[cfg(not(target_arch = "wasm32"))]
    #[cfg_attr(
        all(feature = "runtime-async-std", not(target_arch = "wasm32")),
        async_std::test
    )]
    #[cfg_attr(
        all(feature = "runtime-tokio", not(target_arch = "wasm32")),
        tokio::test
    )]
    async fn test_implicit_user_api() {
        let m = DefaultModel::from_file("examples/rbac_model.conf")
            .await
            .unwrap();

        let adapter =
            FileAdapter::new("examples/rbac_with_hierarchy_policy.csv");
        let e = Enforcer::new(m, adapter).await.unwrap();

        assert_eq!(
            vec!["alice"],
            e.get_implicit_users_for_permission(
                vec!["data1", "read"]
                    .iter()
                    .map(|s| s.to_string())
                    .collect()
            )
            .await
        );
        assert_eq!(
            vec!["alice"],
            e.get_implicit_users_for_permission(
                vec!["data1", "write"]
                    .iter()
                    .map(|s| s.to_string())
                    .collect()
            )
            .await
        );
        assert_eq!(
            vec!["alice"],
            e.get_implicit_users_for_permission(
                vec!["data2", "read"]
                    .iter()
                    .map(|s| s.to_string())
                    .collect()
            )
            .await
        );
        assert_eq!(
            vec!["alice", "bob"],
            sort_unstable(
                e.get_implicit_users_for_permission(
                    vec!["data2", "write"]
                        .iter()
                        .map(|s| s.to_string())
                        .collect()
                )
                .await
            )
        );
    }

    #[cfg(not(target_arch = "wasm32"))]
    #[cfg_attr(
        all(feature = "runtime-async-std", not(target_arch = "wasm32")),
        async_std::test
    )]
    #[cfg_attr(
        all(feature = "runtime-tokio", not(target_arch = "wasm32")),
        tokio::test
    )]
    async fn test_implicit_permission_api_with_domain() {
        let m =
            DefaultModel::from_file("examples/rbac_with_domains_model.conf")
                .await
                .unwrap();

        let adapter = FileAdapter::new(
            "examples/rbac_with_hierarchy_with_domains_policy.csv",
        );
        let mut e = Enforcer::new(m, adapter).await.unwrap();

        assert_eq!(
            vec![
                vec!["alice", "domain1", "data2", "read"],
                vec!["role:reader", "domain1", "data1", "read"],
                vec!["role:writer", "domain1", "data1", "write"],
            ],
            sort_unstable(
                e.get_implicit_permissions_for_user("alice", Some("domain1"))
            )
        );
    }

    #[cfg(not(target_arch = "wasm32"))]
    #[cfg_attr(
        all(feature = "runtime-async-std", not(target_arch = "wasm32")),
        async_std::test
    )]
    #[cfg_attr(
        all(feature = "runtime-tokio", not(target_arch = "wasm32")),
        tokio::test
    )]
    async fn test_pattern_matching_fn() {
        let mut e = Enforcer::new(
            "examples/rbac_with_pattern_model.conf",
            "examples/rbac_with_pattern_policy.csv",
        )
        .await
        .unwrap();

        use crate::model::key_match2;

        e.get_role_manager()
            .write()
            .unwrap()
            .matching_fn(Some(key_match2), None);

        assert!(e.enforce(("alice", "/pen/1", "GET")).unwrap());
        assert!(e.enforce(("alice", "/pen2/1", "GET")).unwrap());
        assert!(e.enforce(("alice", "/book/1", "GET")).unwrap());
        assert!(e.enforce(("alice", "/book/2", "GET")).unwrap());
        assert!(e.enforce(("alice", "/pen/1", "GET")).unwrap());
        assert!(!e.enforce(("alice", "/pen/2", "GET")).unwrap());
        assert!(!e.enforce(("bob", "/book/1", "GET")).unwrap());
        assert!(!e.enforce(("bob", "/book/2", "GET")).unwrap());
        assert!(e.enforce(("bob", "/pen/1", "GET")).unwrap());
        assert!(e.enforce(("bob", "/pen/2", "GET")).unwrap());

        assert_eq!(
            vec!["/book/:id", "book_group"],
            sort_unstable(e.get_implicit_roles_for_user("/book/1", None))
        );

        assert_eq!(
            vec!["/pen/:id", "pen_group"],
            sort_unstable(e.get_implicit_roles_for_user("/pen/1", None))
        );
    }

    #[cfg(not(target_arch = "wasm32"))]
    #[cfg_attr(
        all(feature = "runtime-async-std", not(target_arch = "wasm32")),
        async_std::test
    )]
    #[cfg_attr(
        all(feature = "runtime-tokio", not(target_arch = "wasm32")),
        tokio::test
    )]
    async fn test_pattern_matching_fn_with_domain() {
        let mut e = Enforcer::new(
            "examples/rbac_with_pattern_domain_model.conf",
            "examples/rbac_with_pattern_domain_policy.csv",
        )
        .await
        .unwrap();

        use crate::function_map::key_match;

        e.get_role_manager()
            .write()
            .unwrap()
            .matching_fn(None, Some(key_match));

        assert!(e.enforce(("alice", "domain1", "data1", "read")).unwrap());
        assert!(e.enforce(("alice", "domain1", "data1", "write")).unwrap());
        assert!(e.enforce(("alice", "domain2", "data2", "read")).unwrap());
        assert!(e.enforce(("alice", "domain2", "data2", "write")).unwrap());

        assert!(!e.enforce(("bob", "domain1", "data1", "read")).unwrap());
        assert!(!e.enforce(("bob", "domain1", "data1", "write")).unwrap());
        assert!(e.enforce(("bob", "domain2", "data2", "read")).unwrap());
        assert!(e.enforce(("bob", "domain2", "data2", "write")).unwrap());

        assert_eq!(
            vec!["admin".to_owned()],
            e.get_implicit_roles_for_user("alice", Some("domain3"))
        );

        assert_eq!(
            vec!["alice".to_owned()],
            e.get_users_for_role("admin", Some("domain3"))
        );
    }

    #[cfg(not(target_arch = "wasm32"))]
    #[cfg_attr(
        all(feature = "runtime-async-std", not(target_arch = "wasm32")),
        async_std::test
    )]
    #[cfg_attr(
        all(feature = "runtime-tokio", not(target_arch = "wasm32")),
        tokio::test
    )]
    async fn test_pattern_matching_basic_role() {
        let mut e = Enforcer::new(
            "examples/rbac_basic_role_model.conf",
            "examples/rbac_basic_role_policy.csv",
        )
        .await
        .unwrap();

        use crate::model::key_match;

        e.get_role_manager()
            .write()
            .unwrap()
            .matching_fn(Some(key_match), None);

        assert!(e.enforce(("alice", "/pen/1", "GET")).unwrap());
        assert!(e.enforce(("alice", "/book/1", "GET")).unwrap());
        assert!(e.enforce(("bob", "/pen/1", "GET")).unwrap());
        assert!(e.enforce(("bob", "/book/1", "GET")).unwrap());

        assert!(!e.enforce(("alice", "/pen/2", "GET")).unwrap());
        assert!(!e.enforce(("alice", "/book/2", "GET")).unwrap());
        assert!(!e.enforce(("bob", "/pen/2", "GET")).unwrap());
        assert!(!e.enforce(("bob", "/book/2", "GET")).unwrap());

        assert_eq!(
            vec!["*", "book_admin", "pen_admin"],
            sort_unstable(e.get_implicit_roles_for_user("alice", None))
        );
        assert_eq!(
            vec!["*", "book_admin", "pen_admin"],
            sort_unstable(e.get_implicit_roles_for_user("bob", None))
        );
    }

    #[cfg(not(target_arch = "wasm32"))]
    #[cfg_attr(
        all(feature = "runtime-async-std", not(target_arch = "wasm32")),
        async_std::test
    )]
    #[cfg_attr(
        all(feature = "runtime-tokio", not(target_arch = "wasm32")),
        tokio::test
    )]
    async fn test_implicit_users_for_permission() {
        let mut m = DefaultModel::default();
        m.add_def("r", "r", "sub, obj, act");
        m.add_def("p", "p", "sub, obj, act");
        m.add_def("g", "g", "_, _");
        m.add_def("e", "e", "some(where (p.eft == allow))");
        m.add_def(
            "m",
            "m",
            "g(r.sub, p.sub) && r.obj == p.obj && regexMatch(r.act, p.act)",
        );

        let a = MemoryAdapter::default();

        let mut e = Enforcer::new(m, a).await.unwrap();

        assert!(e
            .add_policy(vec![
                "role::r1".to_owned(),
                "data1".to_owned(),
                "(read)|(writer)".to_owned()
            ])
            .await
            .unwrap());

        assert!(e
            .add_policy(vec![
                "role::r2".to_owned(),
                "data2".to_owned(),
                "writer".to_owned()
            ])
            .await
            .unwrap());

        assert!(e
            .add_policy(vec![
                "user1".to_owned(),
                "data2".to_owned(),
                "writer".to_owned()
            ])
            .await
            .unwrap());

        assert!(e
            .add_grouping_policy(vec![
                "user2".to_owned(),
                "role::r2".to_owned(),
            ])
            .await
            .unwrap());

        assert!(e
            .add_grouping_policy(vec![
                "user3".to_owned(),
                "role::r2".to_owned(),
            ])
            .await
            .unwrap());

        assert_eq!(
            vec!["user1".to_owned(), "user2".to_owned(), "user3".to_owned()],
            sort_unstable(
                e.get_implicit_users_for_permission(vec![
                    "data2".to_owned(),
                    "writer".to_owned()
                ])
                .await
            )
        );
    }
}
