use std::borrow::Cow;
use std::fmt;
use percent_encoding::{utf8_percent_encode, AsciiSet, CONTROLS};
use crate::api::ParamValue;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum AccessLevel {
    
    Anonymous,
    
    Guest,
    
    Reporter,
    
    Developer,
    
    Maintainer,
    
    Owner,
    
    Admin,
}
impl AccessLevel {
    
    pub fn as_str(self) -> &'static str {
        match self {
            AccessLevel::Admin => "admin",
            AccessLevel::Owner => "owner",
            AccessLevel::Maintainer => "maintainer",
            AccessLevel::Developer => "developer",
            AccessLevel::Reporter => "reporter",
            AccessLevel::Guest => "guest",
            AccessLevel::Anonymous => "anonymous",
        }
    }
    
    pub fn as_u64(self) -> u64 {
        match self {
            AccessLevel::Admin => 60,
            AccessLevel::Owner => 50,
            AccessLevel::Maintainer => 40,
            AccessLevel::Developer => 30,
            AccessLevel::Reporter => 20,
            AccessLevel::Guest => 10,
            AccessLevel::Anonymous => 0,
        }
    }
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SortOrder {
    
    Ascending,
    
    Descending,
}
impl Default for SortOrder {
    fn default() -> Self {
        SortOrder::Descending
    }
}
impl SortOrder {
    
    pub fn as_str(self) -> &'static str {
        match self {
            SortOrder::Ascending => "asc",
            SortOrder::Descending => "desc",
        }
    }
}
impl ParamValue<'static> for SortOrder {
    fn as_value(self) -> Cow<'static, str> {
        self.as_str().into()
    }
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum EnableState {
    
    Enabled,
    
    Disabled,
}
impl EnableState {
    
    pub fn as_str(self) -> &'static str {
        match self {
            EnableState::Enabled => "enabled",
            EnableState::Disabled => "disabled",
        }
    }
}
impl From<bool> for EnableState {
    fn from(b: bool) -> Self {
        if b {
            EnableState::Enabled
        } else {
            EnableState::Disabled
        }
    }
}
impl ParamValue<'static> for EnableState {
    fn as_value(self) -> Cow<'static, str> {
        self.as_str().into()
    }
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum NameOrId<'a> {
    
    
    
    
    Name(Cow<'a, str>),
    
    Id(u64),
}
const PATH_SEGMENT_ENCODE_SET: &AsciiSet = &CONTROLS
    .add(b' ')
    .add(b'"')
    .add(b'#')
    .add(b'<')
    .add(b'>')
    .add(b'`')
    .add(b'?')
    .add(b'{')
    .add(b'}')
    .add(b'%')
    .add(b'/');
pub fn path_escaped<'a>(input: &'a str) -> impl fmt::Display + 'a {
    utf8_percent_encode(input, PATH_SEGMENT_ENCODE_SET)
}
impl<'a> fmt::Display for NameOrId<'a> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            NameOrId::Name(name) => write!(f, "{}", path_escaped(name)),
            NameOrId::Id(id) => write!(f, "{}", id),
        }
    }
}
impl<'a> From<u64> for NameOrId<'a> {
    fn from(id: u64) -> Self {
        NameOrId::Id(id)
    }
}
impl<'a> From<&'a str> for NameOrId<'a> {
    fn from(name: &'a str) -> Self {
        NameOrId::Name(name.into())
    }
}
impl<'a> From<String> for NameOrId<'a> {
    fn from(name: String) -> Self {
        NameOrId::Name(name.into())
    }
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum VisibilityLevel {
    
    Public,
    
    Internal,
    
    Private,
}
impl VisibilityLevel {
    
    pub fn as_str(self) -> &'static str {
        match self {
            VisibilityLevel::Public => "public",
            VisibilityLevel::Internal => "internal",
            VisibilityLevel::Private => "private",
        }
    }
}
impl ParamValue<'static> for VisibilityLevel {
    fn as_value(self) -> Cow<'static, str> {
        self.as_str().into()
    }
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum YesNo {
    
    Yes,
    
    No,
}
impl YesNo {
    
    pub fn as_str(self) -> &'static str {
        match self {
            YesNo::Yes => "yes",
            YesNo::No => "no",
        }
    }
}
impl From<bool> for YesNo {
    fn from(b: bool) -> Self {
        if b {
            YesNo::Yes
        } else {
            YesNo::No
        }
    }
}
impl ParamValue<'static> for YesNo {
    fn as_value(self) -> Cow<'static, str> {
        self.as_str().into()
    }
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum ProtectedAccessLevel {
    
    Developer,
    
    Maintainer,
    
    Admin,
    
    NoAccess,
}
impl Default for ProtectedAccessLevel {
    fn default() -> Self {
        ProtectedAccessLevel::Maintainer
    }
}
impl ProtectedAccessLevel {
    fn as_str(self) -> &'static str {
        match self {
            ProtectedAccessLevel::Developer => "30",
            ProtectedAccessLevel::Maintainer => "40",
            ProtectedAccessLevel::Admin => "60",
            ProtectedAccessLevel::NoAccess => "0",
        }
    }
}
impl ParamValue<'static> for ProtectedAccessLevel {
    fn as_value(self) -> Cow<'static, str> {
        self.as_str().into()
    }
}
#[cfg(test)]
mod tests {
    use std::cmp;
    use crate::api::common::{
        AccessLevel, EnableState, NameOrId, ProtectedAccessLevel, SortOrder, VisibilityLevel, YesNo,
    };
    #[test]
    fn access_level_as_str() {
        let items = &[
            (AccessLevel::Anonymous, "anonymous", 0),
            (AccessLevel::Guest, "guest", 10),
            (AccessLevel::Reporter, "reporter", 20),
            (AccessLevel::Developer, "developer", 30),
            (AccessLevel::Maintainer, "maintainer", 40),
            (AccessLevel::Owner, "owner", 50),
            (AccessLevel::Admin, "admin", 60),
        ];
        for (i, s, u) in items {
            assert_eq!(i.as_str(), *s);
            assert_eq!(i.as_u64(), *u);
        }
    }
    #[test]
    fn access_level_ordering() {
        let items = &[
            AccessLevel::Anonymous,
            AccessLevel::Guest,
            AccessLevel::Reporter,
            AccessLevel::Developer,
            AccessLevel::Maintainer,
            AccessLevel::Owner,
            AccessLevel::Admin,
        ];
        let mut last = None;
        for item in items {
            if let Some(prev) = last {
                assert!(prev < item);
            }
            last = Some(item);
        }
    }
    #[test]
    fn sort_order_default() {
        assert_eq!(SortOrder::default(), SortOrder::Descending);
    }
    #[test]
    fn sort_order_as_str() {
        let items = &[
            (SortOrder::Ascending, "asc"),
            (SortOrder::Descending, "desc"),
        ];
        for (i, s) in items {
            assert_eq!(i.as_str(), *s);
        }
    }
    #[test]
    fn enable_state_as_str() {
        let items = &[
            (EnableState::Enabled, "enabled"),
            (EnableState::Disabled, "disabled"),
        ];
        for (i, s) in items {
            assert_eq!(i.as_str(), *s);
        }
    }
    #[test]
    fn enable_state_from_bool() {
        let items = &[(EnableState::Enabled, true), (EnableState::Disabled, false)];
        for (i, s) in items {
            assert_eq!(*i, (*s).into());
        }
    }
    #[test]
    fn name_or_id_as_str() {
        let items: &[(NameOrId, _)] = &[
            ("user".into(), "user"),
            ("special/name".into(), "special%2Fname"),
            (
                "special/name?string".to_string().into(),
                "special%2Fname%3Fstring",
            ),
            (1.into(), "1"),
        ];
        for (i, s) in items {
            assert_eq!(i.to_string(), *s);
        }
    }
    #[test]
    fn visibility_level_as_str() {
        let items = &[
            (VisibilityLevel::Public, "public"),
            (VisibilityLevel::Internal, "internal"),
            (VisibilityLevel::Private, "private"),
        ];
        for (i, s) in items {
            assert_eq!(i.as_str(), *s);
        }
    }
    #[test]
    fn yes_no_as_str() {
        let items = &[(YesNo::Yes, "yes"), (YesNo::No, "no")];
        for (i, s) in items {
            assert_eq!(i.as_str(), *s);
        }
    }
    #[test]
    fn yes_no_from_bool() {
        let items = &[(YesNo::Yes, true), (YesNo::No, false)];
        for (i, s) in items {
            assert_eq!(*i, (*s).into());
        }
    }
    #[test]
    fn protected_access_level_default() {
        assert_eq!(
            ProtectedAccessLevel::default(),
            ProtectedAccessLevel::Maintainer,
        );
    }
    #[test]
    fn protected_access_level_ord() {
        let items = &[
            ProtectedAccessLevel::Developer,
            ProtectedAccessLevel::Maintainer,
            ProtectedAccessLevel::Admin,
            ProtectedAccessLevel::NoAccess,
        ];
        for i in items {
            assert_eq!(*i, *i);
            assert_eq!(i.cmp(i), cmp::Ordering::Equal);
            let mut expect = cmp::Ordering::Greater;
            for j in items {
                let is_same = i == j;
                if is_same {
                    expect = cmp::Ordering::Equal;
                }
                assert_eq!(i.cmp(j), expect);
                if is_same {
                    expect = cmp::Ordering::Less;
                }
            }
            let mut expect = cmp::Ordering::Less;
            for j in items.iter().rev() {
                let is_same = i == j;
                if is_same {
                    expect = cmp::Ordering::Equal;
                }
                assert_eq!(i.cmp(j), expect);
                if is_same {
                    expect = cmp::Ordering::Greater;
                }
            }
        }
    }
    #[test]
    fn protected_access_level_as_str() {
        let items = &[
            (ProtectedAccessLevel::Developer, "30"),
            (ProtectedAccessLevel::Maintainer, "40"),
            (ProtectedAccessLevel::Admin, "60"),
            (ProtectedAccessLevel::NoAccess, "0"),
        ];
        for (i, s) in items {
            assert_eq!(i.as_str(), *s);
        }
    }
}