use derive_more::Display;
use std::{
    any::type_name,
    fmt::{self, Debug},
    ops::{BitAnd, BitOr},
};

use super::Role;
use crate::claims::common::ScalarOrArray;
use serde::{Deserialize, Serialize};

/// A `Scope` is set of [`Permission`]s with an allocation-free representation and is cheaply copyable.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Display)]
#[display("{_0}")]
pub struct Scope(PermissionFlags);

impl Default for Scope {
    /// Very explicitly, a default [`Scope`] has no permissions.
    /// This is important to avoid accidentally granting permissions that were not intended.
    fn default() -> Self {
        Self::with_no_permissions()
    }
}

/// A compact representation of a set of [`Permission`] values.
///
/// `PermissionFlags` implements [`IntoIterator<Item = Permission>`].
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct PermissionFlags(u64);

/// A `Permission` represents authorisation to perform a specific operation.
///
/// Each enum variant represents a permission category and each category has its own specific permissions.
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Display)]
pub enum Permission {
    #[display("access_key:{_0}")]
    AccessKey(AccessKeyPermission),

    #[display("dataset:{_0}")]
    Keyset(KeysetPermission),

    #[display("client:{_0}")]
    Client(ClientPermission),

    #[display("data_key:{_0}")]
    DataKey(DataKeyPermission),

    #[display("cs_support:{_0}")]
    Support(SupportPermission),

    #[display("logging:{_0}")]
    Logging(LoggingPermission),

    #[display("actor:{_0}")]
    Actor(ActorPermission),
}

/// Represents a permission reserved for a CipherStash employee in a customer support role.
///
/// **NOTE** this is here for backwards compatibility and should be removed - it seems more like a role.
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Display)]
#[repr(u8)]
pub enum SupportPermission {
    #[display("admin")]
    Admin = 0b0000_0001,
}

/// Represents a permission reserved for a audit logging.
///
/// **NOTE** this is here for backwards compatibility and should be removed - it seems more like a role.
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Display)]
#[repr(u8)]
pub enum LoggingPermission {
    #[display("append")]
    Append = 0b0000_0001,
}

/// An `AccessKey` permission.
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Display)]
#[repr(u8)]
pub enum AccessKeyPermission {
    /// Permission to create Access Keys
    #[display("create")]
    Create = 0b0000_0001,

    /// Permission to list Access Keys
    #[display("list")]
    List = 0b0000_0010,

    /// Permission to delete Access Keys
    #[display("delete")]
    Delete = 0b0000_0100,

    /// Permission to update Access Keys
    #[display("update")]
    Update = 0b0000_1000,
}

/// A `Keyset` permission.
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Display)]
#[repr(u8)]
pub enum KeysetPermission {
    /// Permission to create Keysets
    #[display("create")]
    Create = 0b0000_0001,

    /// Permission to list Keysets
    #[display("list")]
    List = 0b0000_0010,

    /// Permission to disable Keysets
    #[display("disable")]
    Disable = 0b0000_0100,

    /// Permission to enable Keysets
    #[display("enable")]
    Enable = 0b0000_1000,

    /// Permission to grant Keysets
    #[display("grant")]
    Grant = 0b0001_0000,

    /// Permission to modify Keysets
    #[display("modify")]
    Modify = 0b0010_0000,

    /// Permission to revoke Keysets
    #[display("revoke")]
    Revoke = 0b0100_0000,
}

/// A `Keyset` permission.
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Display)]
#[repr(u8)]
pub enum ClientPermission {
    /// Permission to create Clients
    #[display("create")]
    Create = 0b0000_0001,

    /// Permission to list Clients
    #[display("list")]
    List = 0b0000_0010,

    /// Permission to delete Clients
    #[display("delete")]
    Delete = 0b0000_0100,
}

/// A `DataKey` permission.
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Display)]
#[repr(u8)]
pub enum DataKeyPermission {
    /// Permission to generate DataKeys for encrypting data
    #[display("generate")]
    Generate = 0b0000_0001,

    /// Permission to retrieve DataKeys for decrypting data
    #[display("retrieve")]
    Retrieve = 0b0000_0010,
}

/// An `Actor` permission.
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Display)]
#[repr(u8)]
pub enum ActorPermission {
    /// Permission to create Actors
    #[display("create")]
    Create = 0b0000_0001,

    /// Permission to list Actors
    #[display("list")]
    List = 0b0000_0010,

    /// Permission to delete Actors
    #[display("delete")]
    Delete = 0b0000_0100,
}

impl Permission {
    /// Parse a `Permission` from `input`.
    ///
    /// If the input does not match the known syntax for any permission the `None` is returned.
    fn parse(input: &str) -> Option<Self> {
        match input {
            s if s.starts_with("keyset:") => Some(Self::Keyset(KeysetPermission::parse(
                &s["keyset:".len()..],
            )?)),
            s if s.starts_with("access_key:") => Some(Self::AccessKey(AccessKeyPermission::parse(
                &s["access_key:".len()..],
            )?)),
            // Backwards compatibilty: deal with any "dataset:*" permissions in JWTs
            s if s.starts_with("dataset:") => Some(Self::Keyset(KeysetPermission::parse(
                &s["dataset:".len()..],
            )?)),
            s if s.starts_with("data_key:") => Some(Self::DataKey(DataKeyPermission::parse(
                &s["data_key:".len()..],
            )?)),
            s if s.starts_with("client:") => Some(Self::Client(ClientPermission::parse(
                &s["client:".len()..],
            )?)),
            s if s.starts_with("cs_support:") => Some(Self::Support(SupportPermission::parse(
                &s["cs_support:".len()..],
            )?)),
            s if s.starts_with("logging:") => Some(Self::Logging(LoggingPermission::parse(
                &s["logging:".len()..],
            )?)),
            s if s.starts_with("actor:") => {
                Some(Self::Actor(ActorPermission::parse(&s["actor:".len()..])?))
            }
            _ => None,
        }
    }

    /// Converts `self` to a `PermissionFlags` with a single bit set that represents `self`.
    const fn to_flags(self) -> PermissionFlags {
        // These static assertions will fail if a permission type is not #[repr(u8)], which is critical for knowing that
        // the flags will merge correctly into a u64.  If the static assertions fail then the bit shifts and/or the size
        // of PermissionFlags must change.
        const _: () = assert!(size_of::<KeysetPermission>() == 1);
        const _: () = assert!(size_of::<ClientPermission>() == 1);
        const _: () = assert!(size_of::<DataKeyPermission>() == 1);
        const _: () = assert!(size_of::<SupportPermission>() == 1);
        const _: () = assert!(size_of::<LoggingPermission>() == 1);
        const _: () = assert!(size_of::<AccessKeyPermission>() == 1);
        const _: () = assert!(size_of::<ActorPermission>() == 1);

        match self {
            Permission::Keyset(permission) => PermissionFlags(permission as u64),
            Permission::Client(permission) => PermissionFlags((permission as u64) << 8),
            Permission::DataKey(permission) => PermissionFlags((permission as u64) << 16),
            Permission::Support(permission) => PermissionFlags((permission as u64) << 24),
            Permission::Logging(permission) => PermissionFlags((permission as u64) << 32),
            Permission::AccessKey(permission) => PermissionFlags((permission as u64) << 40),
            Permission::Actor(permission) => PermissionFlags((permission as u64) << 48),
        }
    }

    /// All permissions.
    const ALL: &[Permission] = &[
        // Keyset permissions
        Self::Keyset(KeysetPermission::Create),
        Self::Keyset(KeysetPermission::List),
        Self::Keyset(KeysetPermission::Enable),
        Self::Keyset(KeysetPermission::Disable),
        Self::Keyset(KeysetPermission::Grant),
        Self::Keyset(KeysetPermission::Modify),
        Self::Keyset(KeysetPermission::Revoke),
        // DataKey permissions
        Self::DataKey(DataKeyPermission::Generate),
        Self::DataKey(DataKeyPermission::Retrieve),
        // Client permissions
        Self::Client(ClientPermission::Create),
        Self::Client(ClientPermission::List),
        Self::Client(ClientPermission::Delete),
        // AccessKey permissions
        Self::AccessKey(AccessKeyPermission::Create),
        Self::AccessKey(AccessKeyPermission::List),
        Self::AccessKey(AccessKeyPermission::Delete),
        Self::AccessKey(AccessKeyPermission::Update),
        // Actor permissions
        Self::Actor(ActorPermission::Create),
        Self::Actor(ActorPermission::List),
        Self::Actor(ActorPermission::Delete),
        Self::Support(SupportPermission::Admin),
        Self::Logging(LoggingPermission::Append),
    ];
}

macro_rules! impl_from {
    ($variant:ident, $ty:ty) => {
        impl From<$ty> for Permission {
            fn from(value: $ty) -> Self {
                Self::$variant(value)
            }
        }
    };
}

impl_from!(AccessKey, AccessKeyPermission);
impl_from!(Keyset, KeysetPermission);
impl_from!(DataKey, DataKeyPermission);
impl_from!(Client, ClientPermission);
impl_from!(Support, SupportPermission);
impl_from!(Logging, LoggingPermission);
impl_from!(Actor, ActorPermission);

impl PermissionFlags {
    /// Create a new empty set of permissions.
    pub const fn empty() -> Self {
        Self(0)
    }

    /// Return permissions with a single bit set for `flag`.
    pub const fn with_flag(flag: Permission) -> Self {
        Self::empty().add(flag)
    }

    /// Add `permission` to `self` and return a new `PermissionFlags`.
    pub const fn add(self, permission: Permission) -> Self {
        Self(self.0 | permission.to_flags().0)
    }

    /// Merge `self` with `other` returning a new `PermissionFlags`.
    pub const fn merge(self, other: Self) -> Self {
        Self(self.0 | other.0)
    }

    /// Count how many permission flags are set in `self`.
    fn count_permissions(&self) -> u32 {
        self.0.count_ones()
    }
}

impl IntoIterator for PermissionFlags {
    type IntoIter = PermissionsIter;
    type Item = Permission;

    fn into_iter(self) -> Self::IntoIter {
        PermissionsIter(self, Permission::ALL)
    }
}

impl FromIterator<Permission> for PermissionFlags {
    fn from_iter<T: IntoIterator<Item = Permission>>(iter: T) -> Self {
        let iter = iter.into_iter();
        let mut flags = Self(0);
        for permission in iter {
            flags = flags.add(permission);
        }
        flags
    }
}

/// Iterator that iterates over all flags in `self` producing [`Permission`] values.
pub struct PermissionsIter(PermissionFlags, &'static [Permission]);

impl Iterator for PermissionsIter {
    type Item = Permission;

    fn next(&mut self) -> Option<Self::Item> {
        // Iteration works by looping through all of the possible permissions and checking if a flag for each permission
        // exists in the PermissionFlags.
        loop {
            if let Some((first, rest)) = self.1.split_first() {
                // Effectively pops the front of the slice from self.1
                self.1 = rest;
                if self.0 & first.to_flags() == first.to_flags() {
                    return Some(*first);
                }
            } else {
                // There are no more permissions left to check.
                return None;
            }
        }
    }
}

impl BitAnd for PermissionFlags {
    type Output = Self;

    fn bitand(self, rhs: Self) -> Self::Output {
        Self(self.0 & rhs.0)
    }
}

impl BitOr for PermissionFlags {
    type Output = Self;

    fn bitor(self, rhs: Self) -> Self::Output {
        Self(self.0 | rhs.0)
    }
}

impl fmt::Display for PermissionFlags {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let count = self.count_permissions() as usize;

        // NOTE: Iterator::intersperse is unstable
        for (idx, scope) in self.into_iter().enumerate() {
            fmt::Display::fmt(&scope, f)?;
            if idx < count - 1 {
                fmt::Display::fmt(", ", f)?;
            }
        }

        Ok(())
    }
}

impl Debug for PermissionFlags {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let count = self.count_permissions() as usize;

        f.write_fmt(format_args!("{}(", type_name::<Self>()))?;

        for (idx, scope) in self.into_iter().enumerate() {
            fmt::Display::fmt(&scope, f)?;
            if idx < count - 1 {
                fmt::Display::fmt("|", f)?;
            }
        }

        f.write_str(")")?;

        Ok(())
    }
}

impl KeysetPermission {
    fn parse(input: &str) -> Option<Self> {
        match input {
            "create" => Some(Self::Create),
            "list" => Some(Self::List),
            "disable" => Some(Self::Disable),
            "enable" => Some(Self::Enable),
            "grant" => Some(Self::Grant),
            "modify" => Some(Self::Modify),
            "revoke" => Some(Self::Revoke),
            _ => None,
        }
    }
}

impl ClientPermission {
    fn parse(input: &str) -> Option<Self> {
        match input {
            "create" => Some(Self::Create),
            "list" => Some(Self::List),
            "delete" => Some(Self::Delete),
            _ => None,
        }
    }
}

impl DataKeyPermission {
    fn parse(input: &str) -> Option<Self> {
        match input {
            "generate" => Some(Self::Generate),
            "retrieve" => Some(Self::Retrieve),
            _ => None,
        }
    }
}

impl SupportPermission {
    fn parse(input: &str) -> Option<Self> {
        match input {
            "admin" => Some(Self::Admin),
            _ => None,
        }
    }
}

impl LoggingPermission {
    fn parse(input: &str) -> Option<Self> {
        match input {
            "append" => Some(Self::Append),
            _ => None,
        }
    }
}

impl AccessKeyPermission {
    fn parse(input: &str) -> Option<Self> {
        match input {
            "create" => Some(Self::Create),
            "list" => Some(Self::List),
            "delete" => Some(Self::Delete),
            "update" => Some(Self::Update),
            _ => None,
        }
    }
}

impl ActorPermission {
    fn parse(input: &str) -> Option<Self> {
        match input {
            "create" => Some(Self::Create),
            "list" => Some(Self::List),
            "delete" => Some(Self::Delete),
            _ => None,
        }
    }
}

impl Scope {
    /// Makes a `Scope` containing all [`Permission`]s in `permissions`.
    pub const fn with_permissions(permissions: PermissionFlags) -> Self {
        Self(permissions)
    }

    /// Makes a `Scope` containing `permission`.
    pub const fn with_permission(permission: Permission) -> Self {
        Self(PermissionFlags::empty().add(permission))
    }

    /// Makes a `Scope` without permissions.
    pub fn with_no_permissions() -> Self {
        Self(PermissionFlags::empty())
    }

    /// Merges `self` with `other` returning a new `Scope` containing the union of the permissions of both scopes.
    pub fn merge(self, other: Self) -> Self {
        Self(self.0 | other.0)
    }

    /// Parses a `Scope` from `input`. Always succeeds, even when the string does not contain a syntactically valid
    /// scope but an empty `Scope` is returned when this occurs.
    pub fn parse(input: &str) -> Self {
        let permissions: PermissionFlags =
            input
                .split_whitespace()
                .fold(PermissionFlags::empty(), |acc, s| {
                    if let Some(permission) = Permission::parse(s) {
                        acc.add(permission)
                    } else {
                        acc
                    }
                });

        Self(permissions)
    }

    /// Checks if `self` contains `permission`.
    pub fn has_permission(&self, permission: Permission) -> bool {
        self.0 & permission.to_flags() == permission.to_flags()
    }

    /// Checks if `self` has at least all of the permissions of `other`.
    pub fn has_all_permissions(&self, other: Self) -> bool {
        self.0 & other.0 == other.0
    }

    /// Counts the number of permissions in `self`.
    pub fn count_permissions(&self) -> u32 {
        self.0.count_permissions()
    }

    /// Returns an [`Iterator`] over the [`Permission`]s in `self`.
    pub fn permissions(&self) -> impl Iterator<Item = Permission> {
        self.0.into_iter()
    }
}

/// The set of [`Scope`]s assigned to an authenticated ZeroKMS admin role.
/// See also [`Role`].
pub const WORKSPACE_ADMIN_SCOPE: Scope = Scope(
    PermissionFlags::empty()
        .add(Permission::AccessKey(AccessKeyPermission::Create))
        .add(Permission::AccessKey(AccessKeyPermission::List))
        .add(Permission::AccessKey(AccessKeyPermission::Delete))
        .add(Permission::AccessKey(AccessKeyPermission::Update))
        .add(Permission::Keyset(KeysetPermission::Create))
        .add(Permission::Keyset(KeysetPermission::List))
        .add(Permission::Keyset(KeysetPermission::Disable))
        .add(Permission::Keyset(KeysetPermission::Enable))
        .add(Permission::Keyset(KeysetPermission::Grant))
        .add(Permission::Keyset(KeysetPermission::Modify))
        .add(Permission::Keyset(KeysetPermission::Revoke))
        .add(Permission::Client(ClientPermission::Create))
        .add(Permission::Client(ClientPermission::List))
        .add(Permission::Client(ClientPermission::Delete))
        .add(Permission::DataKey(DataKeyPermission::Generate))
        .add(Permission::DataKey(DataKeyPermission::Retrieve))
        .add(Permission::Actor(ActorPermission::Create))
        .add(Permission::Actor(ActorPermission::List))
        .add(Permission::Actor(ActorPermission::Delete)),
);

/// The set of [`Scope`]s assigned to an authenticated ZeroKMS control role.
/// See also [`Role`].
/// This role is used by services that need to manage keysets for multi-tenant setups.
pub const WORKSPACE_CONTROL_SCOPE: Scope = Scope(
    PermissionFlags::empty()
        .add(Permission::Keyset(KeysetPermission::Create))
        .add(Permission::Keyset(KeysetPermission::List))
        .add(Permission::Keyset(KeysetPermission::Disable))
        .add(Permission::Keyset(KeysetPermission::Enable))
        .add(Permission::Keyset(KeysetPermission::Grant))
        .add(Permission::Keyset(KeysetPermission::Modify))
        .add(Permission::Keyset(KeysetPermission::Revoke))
        .add(Permission::Client(ClientPermission::List)),
);

/// The set of [`Scope`]s assigned to an authenticated ZeroKMS member role.
/// See also [`Role`].
pub const WORKSPACE_MEMBER_SCOPE: Scope = Scope(
    PermissionFlags::empty()
        .add(Permission::Keyset(KeysetPermission::List))
        .add(Permission::Client(ClientPermission::Create))
        .add(Permission::DataKey(DataKeyPermission::Generate))
        .add(Permission::DataKey(DataKeyPermission::Retrieve)),
);

pub const LOGGING_SCOPE: Scope =
    Scope(PermissionFlags::empty().add(Permission::Logging(LoggingPermission::Append)));

impl From<Option<Role>> for Scope {
    fn from(role: Option<Role>) -> Self {
        match role {
            Some(role) => role.scope(),
            None => Default::default(),
        }
    }
}

impl From<Vec<String>> for Scope {
    fn from(scope_names: Vec<String>) -> Self {
        Scope(
            scope_names
                .into_iter()
                .fold(PermissionFlags::empty(), |acc, s| {
                    if let Some(permission) = Permission::parse(&s) {
                        acc.merge(permission.to_flags())
                    } else {
                        acc
                    }
                }),
        )
    }
}

impl Serialize for Permission {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: serde::Serializer,
    {
        self.to_string().serialize(serializer)
    }
}

impl Serialize for Scope {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: serde::Serializer,
    {
        let flags: Vec<Permission> = self.permissions().collect();

        // TODO: serialize directly from an Iterator to avoid allocating a Vec.
        match &flags[..] {
            &[] => serializer.serialize_none(),
            &[permission] => Some(ScalarOrArray::Scalar(permission)).serialize(serializer),
            flags => Some(ScalarOrArray::Array(flags.iter().collect())).serialize(serializer),
        }
    }
}

// Custom deserialization for `Scope` to handle both single string and array of strings.
impl<'de> Deserialize<'de> for Scope {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: serde::Deserializer<'de>,
    {
        let items: Option<ScalarOrArray<String>> = Deserialize::deserialize(deserializer)?;
        match items {
            Some(ScalarOrArray::Array(items)) => Ok(Scope::from(items)),
            Some(ScalarOrArray::Scalar(item)) => Ok(Scope(
                Permission::parse(&item)
                    .map(|p| p.to_flags())
                    .unwrap_or(PermissionFlags::empty()),
            )),
            None => Ok(Scope::with_no_permissions()),
        }
    }
}

impl From<String> for Scope {
    fn from(value: String) -> Self {
        Scope::parse(&value)
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use serde_json::{json, Value};

    mod parse {
        use super::*;

        #[test]
        fn empty_string_parses_to_empty_scope() {
            let scope = Scope::parse("");
            assert_eq!(scope, Scope::with_no_permissions());
        }

        #[test]
        fn single_scope_parses_correctly() {
            let scope = Scope::parse("dataset:create");
            assert!(scope.has_permission(Permission::Keyset(KeysetPermission::Create)));
            assert_eq!(scope.count_permissions(), 1);
        }

        #[test]
        fn multiple_scopes_parse_correctly() {
            let scope = Scope::parse("dataset:create client:list");
            assert!(scope.has_permission(Permission::Keyset(KeysetPermission::Create)));
            assert!(scope.has_permission(Permission::Client(ClientPermission::List)));
            assert_eq!(scope.count_permissions(), 2);
        }
    }

    mod serialize {
        use super::*;

        #[test]
        fn empty_scope_serializes_as_null() {
            let scope = Scope::default();
            let serialized = serde_json::to_value(scope).unwrap();
            assert_eq!(serialized, Value::Null);
        }

        #[test]
        fn single_scope_serializes_as_string() {
            let scope = Scope::parse("dataset:create");
            let serialized = serde_json::to_value(scope).unwrap();
            assert_eq!(serialized, json!("dataset:create"));
        }

        #[test]
        fn multiple_scopes_serializes_as_array() {
            let scope = Scope::parse("dataset:create client:list");
            let serialized = serde_json::to_value(scope).unwrap();

            assert!(
                serialized == json!(["dataset:create", "client:list"])
                    || serialized == json!(["client:list", "dataset:create"]),
                "Expected serialized scope to be an array of scopes, got: {serialized:?}"
            );
        }
    }

    mod deserialize {
        use super::*;

        #[test]
        fn empty_scope_deserializes_from_null() {
            let json = Value::Null;
            let scope: Scope = serde_json::from_value(json).unwrap();
            assert_eq!(scope, Scope::default());
        }

        #[test]
        fn empty_string_deserializes_to_empty_scope() {
            let json = json!("");
            let scope: Scope = serde_json::from_value(json).unwrap();
            assert_eq!(scope, Scope::default());
        }

        #[test]
        fn single_scope_deserializes_from_string() {
            let json = json!("dataset:create");
            let scope: Scope = serde_json::from_value(json).unwrap();
            assert_eq!(scope, Scope::parse("dataset:create"));
        }

        #[test]
        fn multiple_scopes_deserialize_from_array() {
            let json = json!(["dataset:create", "client:list"]);
            let scope: Scope = serde_json::from_value(json).unwrap();
            assert!(scope.has_permission(Permission::Keyset(KeysetPermission::Create)));
            assert!(scope.has_permission(Permission::Client(ClientPermission::List)));
            assert_eq!(
                scope.count_permissions(),
                2,
                "Expected scope to contain 2 items"
            );
        }

        #[test]
        fn invalid_scope_deserializes_from_invalid_value() {
            let json = json!(12345);
            let result: Result<Scope, _> = serde_json::from_value(json);
            assert!(
                result.is_err(),
                "Expected deserialization to fail for invalid scope"
            );
        }
    }

    mod merge {
        use super::*;

        #[test]
        fn non_overlapping() {
            let scope1 = Scope::parse("dataset:create");
            let scope2 = Scope::parse("client:list");
            let merged = scope1.merge(scope2);

            assert!(merged.has_permission(Permission::Keyset(KeysetPermission::Create)));
            assert!(merged.has_permission(Permission::Client(ClientPermission::List)));
            assert_eq!(merged.count_permissions(), 2);
        }

        #[test]
        fn overlapping() {
            let scope1 = Scope::parse("dataset:create client:list");
            let scope2 = Scope::parse("client:list data_key:generate");
            let merged = scope1.merge(scope2);
            assert!(merged.has_permission(Permission::Keyset(KeysetPermission::Create)));
            assert!(merged.has_permission(Permission::Client(ClientPermission::List)));
            assert!(merged.has_permission(Permission::DataKey(DataKeyPermission::Generate)));
            assert_eq!(merged.count_permissions(), 3);
        }
    }

    mod ensure_bitflag_representation_has_no_collisions {
        use super::*;

        mod scope_with_all_permissions {
            use super::*;

            #[test]
            fn for_each_permission_has_permission_returns_true() {
                let mut scope = Scope::with_no_permissions();
                for permission in Permission::ALL {
                    scope = scope.merge(Scope::with_permission(*permission));
                }

                for permission in Permission::ALL {
                    assert!(scope.has_permission(*permission))
                }
            }
        }

        mod scope_with_all_but_one_permssion {
            use super::*;

            #[test]
            fn has_permission_returns_true_for_all_except_one_permission() {
                for without_permission in Permission::ALL {
                    let permissions: Vec<_> = Permission::ALL
                        .iter()
                        .filter(|p| *p != without_permission)
                        .copied()
                        .collect();
                    let scope = Scope::with_permissions(PermissionFlags::from_iter(
                        permissions.into_iter(),
                    ));

                    for permission in Permission::ALL {
                        if permission != without_permission {
                            assert!(scope.has_permission(*permission))
                        } else {
                            assert!(!scope.has_permission(*permission))
                        }
                    }
                }
            }
        }
    }
}
