use crate::{InternalApi, Result};

use async_trait::async_trait;

#[async_trait]
pub trait MgmtApi: InternalApi {
    async fn add_policy(&mut self, params: Vec<String>) -> Result<bool> {
        self.add_named_policy("p", params).await
    }

    async fn add_policies(
        &mut self,
        paramss: Vec<Vec<String>>,
    ) -> Result<bool> {
        self.add_named_policies("p", paramss).await
    }

    async fn remove_policy(&mut self, params: Vec<String>) -> Result<bool> {
        self.remove_named_policy("p", params).await
    }

    async fn remove_policies(
        &mut self,
        paramss: Vec<Vec<String>>,
    ) -> Result<bool> {
        self.remove_named_policies("p", paramss).await
    }

    async fn add_named_policy(
        &mut self,
        ptype: &str,
        params: Vec<String>,
    ) -> Result<bool>;
    async fn add_named_policies(
        &mut self,
        ptype: &str,
        paramss: Vec<Vec<String>>,
    ) -> Result<bool>;
    async fn remove_named_policy(
        &mut self,
        ptype: &str,
        params: Vec<String>,
    ) -> Result<bool>;
    async fn remove_named_policies(
        &mut self,
        ptype: &str,
        paramss: Vec<Vec<String>>,
    ) -> Result<bool>;

    async fn add_grouping_policy(
        &mut self,
        params: Vec<String>,
    ) -> Result<bool> {
        self.add_named_grouping_policy("g", params).await
    }

    async fn add_grouping_policies(
        &mut self,
        paramss: Vec<Vec<String>>,
    ) -> Result<bool> {
        self.add_named_grouping_policies("g", paramss).await
    }

    async fn remove_grouping_policy(
        &mut self,
        params: Vec<String>,
    ) -> Result<bool> {
        self.remove_named_grouping_policy("g", params).await
    }

    async fn remove_grouping_policies(
        &mut self,
        paramss: Vec<Vec<String>>,
    ) -> Result<bool> {
        self.remove_named_grouping_policies("g", paramss).await
    }

    async fn add_named_grouping_policy(
        &mut self,
        ptype: &str,
        params: Vec<String>,
    ) -> Result<bool>;
    async fn add_named_grouping_policies(
        &mut self,
        ptype: &str,
        paramss: Vec<Vec<String>>,
    ) -> Result<bool>;
    async fn remove_named_grouping_policy(
        &mut self,
        ptype: &str,
        params: Vec<String>,
    ) -> Result<bool>;
    async fn remove_named_grouping_policies(
        &mut self,
        ptype: &str,
        paramss: Vec<Vec<String>>,
    ) -> Result<bool>;

    async fn remove_filtered_policy(
        &mut self,
        field_index: usize,
        field_values: Vec<String>,
    ) -> Result<bool> {
        self.remove_filtered_named_policy("p", field_index, field_values)
            .await
    }

    async fn remove_filtered_grouping_policy(
        &mut self,
        field_index: usize,
        field_values: Vec<String>,
    ) -> Result<bool> {
        self.remove_filtered_named_grouping_policy(
            "g",
            field_index,
            field_values,
        )
        .await
    }

    async fn remove_filtered_named_policy(
        &mut self,
        ptype: &str,
        field_index: usize,
        field_values: Vec<String>,
    ) -> Result<bool>;
    async fn remove_filtered_named_grouping_policy(
        &mut self,
        ptype: &str,
        field_index: usize,
        field_values: Vec<String>,
    ) -> Result<bool>;

    fn get_policy(&self) -> Vec<Vec<String>> {
        self.get_named_policy("p")
    }

    fn get_all_policy(&self) -> Vec<Vec<String>>;
    fn get_named_policy(&self, ptype: &str) -> Vec<Vec<String>>;

    fn get_filtered_policy(
        &self,
        field_index: usize,
        field_values: Vec<String>,
    ) -> Vec<Vec<String>> {
        self.get_filtered_named_policy("p", field_index, field_values)
    }

    fn get_filtered_named_policy(
        &self,
        ptype: &str,
        field_index: usize,
        field_values: Vec<String>,
    ) -> Vec<Vec<String>>;

    fn has_policy(&self, params: Vec<String>) -> bool {
        self.has_named_policy("p", params)
    }

    fn has_named_policy(&self, ptype: &str, params: Vec<String>) -> bool;

    fn get_grouping_policy(&self) -> Vec<Vec<String>> {
        self.get_named_grouping_policy("g")
    }

    fn get_all_grouping_policy(&self) -> Vec<Vec<String>>;
    fn get_named_grouping_policy(&self, ptype: &str) -> Vec<Vec<String>>;

    fn get_filtered_grouping_policy(
        &self,
        field_index: usize,
        field_values: Vec<String>,
    ) -> Vec<Vec<String>> {
        self.get_filtered_named_grouping_policy("g", field_index, field_values)
    }

    fn get_filtered_named_grouping_policy(
        &self,
        ptype: &str,
        field_index: usize,
        field_values: Vec<String>,
    ) -> Vec<Vec<String>>;
    fn has_grouping_policy(&self, params: Vec<String>) -> bool {
        self.has_grouping_named_policy("g", params)
    }
    fn has_grouping_named_policy(
        &self,
        ptype: &str,
        params: Vec<String>,
    ) -> bool;

    fn get_all_subjects(&self) -> Vec<String> {
        self.get_all_named_subjects("p")
    }

    fn get_all_named_subjects(&self, ptype: &str) -> Vec<String>;

    fn get_all_objects(&self) -> Vec<String> {
        self.get_all_named_objects("p")
    }

    fn get_all_named_objects(&self, ptype: &str) -> Vec<String>;

    fn get_all_actions(&self) -> Vec<String> {
        self.get_all_named_actions("p")
    }

    fn get_all_roles(&self) -> Vec<String> {
        self.get_all_named_roles("g")
    }

    fn get_all_named_actions(&self, ptype: &str) -> Vec<String>;

    fn get_all_named_roles(&self, ptype: &str) -> Vec<String>;
}

#[async_trait]
impl<T> MgmtApi for T
where
    T: InternalApi,
{
    async fn add_named_policy(
        &mut self,
        ptype: &str,
        params: Vec<String>,
    ) -> Result<bool> {
        self.add_policy_internal("p", ptype, params).await
    }

    async fn add_named_policies(
        &mut self,
        ptype: &str,
        paramss: Vec<Vec<String>>,
    ) -> Result<bool> {
        self.add_policies_internal("p", ptype, paramss).await
    }

    async fn remove_named_policy(
        &mut self,
        ptype: &str,
        params: Vec<String>,
    ) -> Result<bool> {
        self.remove_policy_internal("p", ptype, params).await
    }

    async fn remove_named_policies(
        &mut self,
        ptype: &str,
        paramss: Vec<Vec<String>>,
    ) -> Result<bool> {
        self.remove_policies_internal("p", ptype, paramss).await
    }

    async fn add_named_grouping_policy(
        &mut self,
        ptype: &str,
        params: Vec<String>,
    ) -> Result<bool> {
        let rule_added = self.add_policy_internal("g", ptype, params).await?;
        Ok(rule_added)
    }

    async fn add_named_grouping_policies(
        &mut self,
        ptype: &str,
        paramss: Vec<Vec<String>>,
    ) -> Result<bool> {
        let all_added = self.add_policies_internal("g", ptype, paramss).await?;
        Ok(all_added)
    }

    async fn remove_named_grouping_policy(
        &mut self,
        ptype: &str,
        params: Vec<String>,
    ) -> Result<bool> {
        let rule_removed =
            self.remove_policy_internal("g", ptype, params).await?;
        Ok(rule_removed)
    }

    async fn remove_named_grouping_policies(
        &mut self,
        ptype: &str,
        paramss: Vec<Vec<String>>,
    ) -> Result<bool> {
        let all_removed =
            self.remove_policies_internal("g", ptype, paramss).await?;
        Ok(all_removed)
    }

    async fn remove_filtered_named_grouping_policy(
        &mut self,
        ptype: &str,
        field_index: usize,
        field_values: Vec<String>,
    ) -> Result<bool> {
        #[allow(unused_variables)]
        let (rule_removed, rules) = self
            .remove_filtered_policy_internal(
                "g",
                ptype,
                field_index,
                field_values,
            )
            .await?;
        Ok(rule_removed)
    }

    async fn remove_filtered_named_policy(
        &mut self,
        ptype: &str,
        field_index: usize,
        field_values: Vec<String>,
    ) -> Result<bool> {
        Ok(self
            .remove_filtered_policy_internal(
                "p",
                ptype,
                field_index,
                field_values,
            )
            .await?
            .0)
    }

    fn get_named_policy(&self, ptype: &str) -> Vec<Vec<String>> {
        self.get_model().get_policy("p", ptype)
    }

    fn get_all_policy(&self) -> Vec<Vec<String>> {
        let mut res: Vec<Vec<String>> = vec![];
        let sec = "p";
        if let Some(ast_map) = self.get_model().get_model().get(sec) {
            for (ptype, ast) in ast_map {
                res.extend(ast.get_policy().clone().into_iter().map(|mut x| {
                    x.insert(0, ptype.clone());
                    x.insert(0, sec.to_owned());
                    x
                }))
            }
        }

        res
    }

    fn get_filtered_named_policy(
        &self,
        ptype: &str,
        field_index: usize,
        field_values: Vec<String>,
    ) -> Vec<Vec<String>> {
        self.get_model().get_filtered_policy(
            "p",
            ptype,
            field_index,
            field_values,
        )
    }

    fn has_named_policy(&self, ptype: &str, params: Vec<String>) -> bool {
        self.get_model().has_policy("p", ptype, params)
    }

    fn get_named_grouping_policy(&self, ptype: &str) -> Vec<Vec<String>> {
        self.get_model().get_policy("g", ptype)
    }

    fn get_all_grouping_policy(&self) -> Vec<Vec<String>> {
        let mut res: Vec<Vec<String>> = vec![];
        let sec = "g";
        if let Some(ast_map) = self.get_model().get_model().get(sec) {
            for (ptype, ast) in ast_map {
                res.extend(ast.get_policy().clone().into_iter().map(|mut x| {
                    x.insert(0, ptype.clone());
                    x.insert(0, sec.to_owned());
                    x
                }))
            }
        }

        res
    }

    fn get_filtered_named_grouping_policy(
        &self,
        ptype: &str,
        field_index: usize,
        field_values: Vec<String>,
    ) -> Vec<Vec<String>> {
        self.get_model().get_filtered_policy(
            "g",
            ptype,
            field_index,
            field_values,
        )
    }

    fn has_grouping_named_policy(
        &self,
        ptype: &str,
        params: Vec<String>,
    ) -> bool {
        self.get_model().has_policy("g", ptype, params)
    }

    fn get_all_named_subjects(&self, ptype: &str) -> Vec<String> {
        self.get_model()
            .get_values_for_field_in_policy("p", ptype, 0)
    }

    fn get_all_named_objects(&self, ptype: &str) -> Vec<String> {
        self.get_model()
            .get_values_for_field_in_policy("p", ptype, 1)
    }

    fn get_all_named_actions(&self, ptype: &str) -> Vec<String> {
        self.get_model()
            .get_values_for_field_in_policy("p", ptype, 2)
    }

    fn get_all_named_roles(&self, ptype: &str) -> Vec<String> {
        self.get_model()
            .get_values_for_field_in_policy("g", ptype, 1)
    }
}

#[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_modify_grouping_policy_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("eve", None));
        assert_eq!(
            vec![String::new(); 0],
            e.get_roles_for_user("non_exist", None)
        );

        e.remove_grouping_policy(
            vec!["alice", "data2_admin"]
                .iter()
                .map(|s| s.to_string())
                .collect(),
        )
        .await
        .unwrap();
        e.add_grouping_policy(
            vec!["bob", "data1_admin"]
                .iter()
                .map(|s| s.to_string())
                .collect(),
        )
        .await
        .unwrap();
        e.add_grouping_policy(
            vec!["eve", "data3_admin"]
                .iter()
                .map(|s| s.to_string())
                .collect(),
        )
        .await
        .unwrap();

        let named_grouping_policy =
            vec!["alice".to_string(), "data2_admin".to_string()];
        assert_eq!(vec![String::new(); 0], e.get_roles_for_user("alice", None));
        e.add_named_grouping_policy("g", named_grouping_policy.clone())
            .await
            .unwrap();
        assert_eq!(vec!["data2_admin"], e.get_roles_for_user("alice", None));
        e.remove_named_grouping_policy("g", named_grouping_policy.clone())
            .await
            .unwrap();

        e.remove_grouping_policy(
            vec!["alice", "data2_admin"]
                .iter()
                .map(|s| s.to_string())
                .collect(),
        )
        .await
        .unwrap();
        e.add_grouping_policy(
            vec!["bob", "data1_admin"]
                .iter()
                .map(|s| s.to_string())
                .collect(),
        )
        .await
        .unwrap();
        e.add_grouping_policy(
            vec!["eve", "data3_admin"]
                .iter()
                .map(|s| s.to_string())
                .collect(),
        )
        .await
        .unwrap();

        assert_eq!(vec!["bob"], e.get_users_for_role("data1_admin", None));
        assert_eq!(
            vec![String::new(); 0],
            e.get_users_for_role("data2_admin", None)
        );
        assert_eq!(vec!["eve"], e.get_users_for_role("data3_admin", None));

        e.remove_filtered_grouping_policy(
            0,
            vec!["bob"].iter().map(|s| s.to_string()).collect(),
        )
        .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!["data3_admin"], e.get_roles_for_user("eve", None));
        assert_eq!(
            vec![String::new(); 0],
            e.get_roles_for_user("non_exist", None)
        );

        assert_eq!(
            vec![String::new(); 0],
            e.get_users_for_role("data1_admin", None)
        );
        assert_eq!(
            vec![String::new(); 0],
            e.get_users_for_role("data2_admin", None)
        );
        assert_eq!(vec!["eve"], e.get_users_for_role("data3_admin", 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_modify_policy_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![
                vec!["alice", "data1", "read"],
                vec!["bob", "data2", "write"],
                vec!["data2_admin", "data2", "read"],
                vec!["data2_admin", "data2", "write"],
            ],
            sort_unstable(e.get_policy())
        );

        e.remove_policy(
            vec!["alice", "data1", "read"]
                .iter()
                .map(|s| s.to_string())
                .collect(),
        )
        .await
        .unwrap();
        e.remove_policy(
            vec!["bob", "data2", "write"]
                .iter()
                .map(|s| s.to_string())
                .collect(),
        )
        .await
        .unwrap();
        e.remove_policy(
            vec!["alice", "data1", "read"]
                .iter()
                .map(|s| s.to_string())
                .collect(),
        )
        .await
        .unwrap();
        e.add_policy(
            vec!["eve", "data3", "read"]
                .iter()
                .map(|s| s.to_string())
                .collect(),
        )
        .await
        .unwrap();
        e.add_policy(
            vec!["eve", "data3", "read"]
                .iter()
                .map(|s| s.to_string())
                .collect(),
        )
        .await
        .unwrap();

        let named_policy =
            vec!["eve".to_string(), "data3".to_string(), "read".to_string()];
        e.remove_named_policy("p", named_policy.clone())
            .await
            .unwrap();
        e.add_named_policy("p", named_policy.clone()).await.unwrap();

        assert_eq!(
            vec![
                vec!["data2_admin", "data2", "read"],
                vec!["data2_admin", "data2", "write"],
                vec!["eve", "data3", "read"],
            ],
            sort_unstable(e.get_policy())
        );

        e.remove_filtered_policy(
            1,
            vec!["data2"].iter().map(|s| s.to_string()).collect(),
        )
        .await
        .unwrap();
        assert_eq!(vec![vec!["eve", "data3", "read"],], e.get_policy());
    }

    #[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_get_policy_api() {
        let m = DefaultModel::from_file("examples/rbac_model.conf")
            .await
            .unwrap();

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

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

        assert_eq!(
            vec![vec!["alice", "data1", "read"]],
            e.get_filtered_policy(
                0,
                vec!["alice"].iter().map(|s| s.to_string()).collect()
            )
        );
        assert_eq!(
            vec![vec!["bob", "data2", "write"]],
            e.get_filtered_policy(
                0,
                vec!["bob"].iter().map(|s| s.to_string()).collect()
            )
        );
        assert_eq!(
            vec![
                vec!["data2_admin", "data2", "read"],
                vec!["data2_admin", "data2", "write"],
            ],
            sort_unstable(e.get_filtered_policy(
                0,
                vec!["data2_admin"].iter().map(|s| s.to_string()).collect()
            ))
        );
        assert_eq!(
            vec![vec!["alice", "data1", "read"],],
            e.get_filtered_policy(
                1,
                vec!["data1"].iter().map(|s| s.to_string()).collect()
            )
        );
        assert_eq!(
            vec![
                vec!["bob", "data2", "write"],
                vec!["data2_admin", "data2", "read"],
                vec!["data2_admin", "data2", "write"],
            ],
            sort_unstable(e.get_filtered_policy(
                1,
                vec!["data2"].iter().map(|s| s.to_string()).collect()
            ))
        );
        assert_eq!(
            vec![
                vec!["alice", "data1", "read"],
                vec!["data2_admin", "data2", "read"],
            ],
            sort_unstable(e.get_filtered_policy(
                2,
                vec!["read"].iter().map(|s| s.to_string()).collect()
            ))
        );
        assert_eq!(
            vec![
                vec!["bob", "data2", "write"],
                vec!["data2_admin", "data2", "write"],
            ],
            sort_unstable(e.get_filtered_policy(
                2,
                vec!["write"].iter().map(|s| s.to_string()).collect()
            ))
        );
        assert_eq!(
            vec![
                vec!["data2_admin", "data2", "read"],
                vec!["data2_admin", "data2", "write"],
            ],
            sort_unstable(
                e.get_filtered_policy(
                    0,
                    vec!["data2_admin", "data2"]
                        .iter()
                        .map(|s| s.to_string())
                        .collect()
                )
            )
        );
        // Note: "" (empty string) in fieldValues means matching all values.
        assert_eq!(
            vec![vec!["data2_admin", "data2", "read"],],
            e.get_filtered_policy(
                0,
                vec!["data2_admin", "", "read"]
                    .iter()
                    .map(|s| s.to_string())
                    .collect()
            )
        );
        assert_eq!(
            vec![
                vec!["bob", "data2", "write"],
                vec!["data2_admin", "data2", "write"],
            ],
            sort_unstable(
                e.get_filtered_policy(
                    1,
                    vec!["data2", "write"]
                        .iter()
                        .map(|s| s.to_string())
                        .collect()
                )
            )
        );

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

        assert_eq!(
            vec![vec!["alice", "data2_admin"]],
            e.get_filtered_grouping_policy(
                0,
                vec!["alice"].iter().map(|s| s.to_string()).collect()
            )
        );
        let empty_policy: Vec<Vec<String>> = vec![];
        assert_eq!(
            empty_policy,
            e.get_filtered_grouping_policy(
                0,
                vec!["bob"].iter().map(|s| s.to_string()).collect()
            )
        );
        assert_eq!(
            empty_policy,
            e.get_filtered_grouping_policy(
                1,
                vec!["data1_admin"].iter().map(|s| s.to_string()).collect()
            )
        );
        assert_eq!(
            vec![vec!["alice", "data2_admin"],],
            e.get_filtered_grouping_policy(
                1,
                vec!["data2_admin"].iter().map(|s| s.to_string()).collect()
            )
        );
        // Note: "" (empty string) in fieldValues means matching all values.
        assert_eq!(
            empty_policy,
            e.get_filtered_grouping_policy(
                0,
                vec!["data2_admin"].iter().map(|s| s.to_string()).collect()
            )
        );

        assert_eq!(
            true,
            e.has_grouping_policy(
                vec!["alice", "data2_admin"]
                    .iter()
                    .map(|s| s.to_string())
                    .collect()
            )
        );
        assert_eq!(
            false,
            e.has_grouping_policy(
                vec!["bob", "data2_admin"]
                    .iter()
                    .map(|s| s.to_string())
                    .collect()
            )
        );
    }

    #[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_get_list() {
        let m = DefaultModel::from_file("examples/rbac_model.conf")
            .await
            .unwrap();

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

        assert_eq!(
            vec!["alice", "bob", "data2_admin"],
            sort_unstable(e.get_all_subjects())
        );
        assert_eq!(vec!["data1", "data2"], sort_unstable(e.get_all_objects()));
        assert_eq!(vec!["read", "write"], sort_unstable(e.get_all_actions()));
        assert_eq!(vec!["data2_admin"], e.get_all_roles());
    }

    #[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_modify_policies_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![
                vec!["alice", "data1", "read"],
                vec!["bob", "data2", "write"],
                vec!["data2_admin", "data2", "read"],
                vec!["data2_admin", "data2", "write"],
            ],
            sort_unstable(e.get_policy())
        );

        e.remove_policies(vec![
            vec!["alice", "data1", "read"]
                .iter()
                .map(|s| s.to_string())
                .collect(),
            vec!["bob", "data2", "write"]
                .iter()
                .map(|s| s.to_string())
                .collect(),
        ])
        .await
        .unwrap();
        e.remove_policies(vec![vec!["alice", "data1", "read"]
            .iter()
            .map(|s| s.to_string())
            .collect()])
            .await
            .unwrap();
        assert_eq!(
            false,
            e.has_policy(
                vec!["alice", "data1", "read"]
                    .iter()
                    .map(|s| s.to_string())
                    .collect()
            )
        );
        assert_eq!(
            false,
            e.has_policy(
                vec!["bob", "data2", "write"]
                    .iter()
                    .map(|s| s.to_string())
                    .collect()
            )
        );
        assert_eq!(
            true,
            e.has_policy(
                vec!["data2_admin", "data2", "read"]
                    .iter()
                    .map(|s| s.to_string())
                    .collect()
            )
        );
        assert_eq!(
            true,
            e.has_policy(
                vec!["data2_admin", "data2", "write"]
                    .iter()
                    .map(|s| s.to_string())
                    .collect()
            )
        );
        e.add_policies(vec![vec!["eve", "data3", "read"]
            .iter()
            .map(|s| s.to_string())
            .collect()])
            .await
            .unwrap();
        e.add_policies(vec![
            vec!["eve", "data3", "read"]
                .iter()
                .map(|s| s.to_string())
                .collect(),
            vec!["eve", "data3", "read"]
                .iter()
                .map(|s| s.to_string())
                .collect(),
        ])
        .await
        .unwrap();
        assert_eq!(
            false,
            e.has_policy(
                vec!["alice", "data1", "read"]
                    .iter()
                    .map(|s| s.to_string())
                    .collect()
            )
        );
        assert_eq!(
            false,
            e.has_policy(
                vec!["bob", "data2", "write"]
                    .iter()
                    .map(|s| s.to_string())
                    .collect()
            )
        );
        assert_eq!(
            true,
            e.has_policy(
                vec!["eve", "data3", "read"]
                    .iter()
                    .map(|s| s.to_string())
                    .collect()
            )
        );
        assert_eq!(
            true,
            e.has_policy(
                vec!["data2_admin", "data2", "read"]
                    .iter()
                    .map(|s| s.to_string())
                    .collect()
            )
        );
        assert_eq!(
            true,
            e.has_policy(
                vec!["data2_admin", "data2", "write"]
                    .iter()
                    .map(|s| s.to_string())
                    .collect()
            )
        );

        let named_policy =
            vec!["eve".to_string(), "data3".to_string(), "read".to_string()];
        e.remove_named_policies("p", vec![named_policy.clone()])
            .await
            .unwrap();
        e.add_named_policies("p", vec![named_policy.clone()])
            .await
            .unwrap();

        assert_eq!(
            vec![
                vec!["data2_admin", "data2", "read"],
                vec!["data2_admin", "data2", "write"],
                vec!["eve", "data3", "read"],
            ],
            sort_unstable(e.get_policy())
        );

        e.remove_filtered_policy(
            1,
            vec!["data2"].iter().map(|s| s.to_string()).collect(),
        )
        .await
        .unwrap();
        assert_eq!(vec![vec!["eve", "data3", "read"],], e.get_policy());
    }

    #[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_modify_grouping_policies_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("eve", None));
        assert_eq!(
            vec![String::new(); 0],
            e.get_roles_for_user("non_exist", None)
        );

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

        let named_grouping_policy =
            vec!["alice".to_string(), "data2_admin".to_string()];
        assert_eq!(vec![String::new(); 0], e.get_roles_for_user("alice", None));
        e.add_named_grouping_policies("g", vec![named_grouping_policy.clone()])
            .await
            .unwrap();
        assert_eq!(vec!["data2_admin"], e.get_roles_for_user("alice", None));
        e.remove_named_grouping_policies(
            "g",
            vec![named_grouping_policy.clone()],
        )
        .await
        .unwrap();

        e.remove_grouping_policies(vec![vec!["alice", "data2_admin"]
            .iter()
            .map(|s| s.to_string())
            .collect()])
            .await
            .unwrap();

        e.add_grouping_policies(vec![
            vec!["bob", "data1_admin"]
                .iter()
                .map(|s| s.to_string())
                .collect(),
            vec!["eve", "data3_admin"]
                .iter()
                .map(|s| s.to_string())
                .collect(),
        ])
        .await
        .unwrap();

        assert_eq!(vec!["bob"], e.get_users_for_role("data1_admin", None));
        assert_eq!(
            vec![String::new(); 0],
            e.get_users_for_role("data2_admin", None)
        );
        assert_eq!(vec!["eve"], e.get_users_for_role("data3_admin", None));

        e.remove_filtered_grouping_policy(
            0,
            vec!["bob"].iter().map(|s| s.to_string()).collect(),
        )
        .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!["data3_admin"], e.get_roles_for_user("eve", None));
        assert_eq!(
            vec![String::new(); 0],
            e.get_roles_for_user("non_exist", None)
        );

        assert_eq!(
            vec![String::new(); 0],
            e.get_users_for_role("data1_admin", None)
        );
        assert_eq!(
            vec![String::new(); 0],
            e.get_users_for_role("data2_admin", None)
        );
        assert_eq!(vec!["eve"], e.get_users_for_role("data3_admin", None));
    }
}
