use std::{
    convert::TryFrom,
    error::Error,
    fmt::{self, Display},
    str::FromStr,
};
pub use crate::packet::{RejectReason, ServerRejectReason};
pub use crate::settings::{AcceptParameters, StreamAcceptor};
#[derive(Debug, PartialEq, Eq)]
pub struct AccessControlList(pub Vec<AccessControlEntry>);
#[derive(Debug, PartialEq, Eq)]
pub struct AccessControlEntry {
    pub key: String,
    pub value: String,
}
#[non_exhaustive]
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum ConnectionType {
    Stream,
    File,
    Auth,
}
#[non_exhaustive]
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum ConnectionMode {
    Request,
    Publish,
    Bidirectional,
}
#[derive(Debug, Clone)]
pub enum StandardAccessControlEntry {
    UserName(String),
    ResourceName(String),
    HostName(String),
    SessionId(String),
    Type(ConnectionType),
    Mode(ConnectionMode),
}
#[derive(Debug, Eq, PartialEq)]
#[non_exhaustive]
pub enum ParseAccessControlEntryError {
    NoValue,
    WrongStart,
}
impl Display for AccessControlList {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let mut iter = self.0.iter().fuse();
        if let Some(item) = iter.next() {
            write!(f, "#!::{item}")?;
        }
        for item in iter {
            write!(f, ",{item}")?;
        }
        Ok(())
    }
}
impl FromStr for AccessControlList {
    type Err = ParseAccessControlEntryError;
    fn from_str(mut s: &str) -> Result<Self, Self::Err> {
        if !s.starts_with("#!::") {
            return Err(ParseAccessControlEntryError::WrongStart);
        }
        s = &s[4..]; Ok(AccessControlList(
            s.split(',')
                .map(str::parse)
                .collect::<Result<Vec<_>, _>>()?,
        ))
    }
}
impl AccessControlEntry {
    fn new(key: impl Into<String>, value: impl Into<String>) -> AccessControlEntry {
        AccessControlEntry {
            key: key.into(),
            value: value.into(),
        }
    }
}
impl Display for AccessControlEntry {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}={}", self.key, self.value)
    }
}
impl FromStr for AccessControlEntry {
    type Err = ParseAccessControlEntryError;
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let eq = s.find('=').ok_or(ParseAccessControlEntryError::NoValue)?;
        let (k, v) = s.split_at(eq);
        Ok(AccessControlEntry::new(k, &v[1..])) }
}
impl Display for ConnectionType {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            ConnectionType::Stream => write!(f, "stream"),
            ConnectionType::File => write!(f, "file"),
            ConnectionType::Auth => write!(f, "auth"),
        }
    }
}
impl FromStr for ConnectionType {
    type Err = ();
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "stream" => Ok(ConnectionType::Stream),
            "file" => Ok(ConnectionType::File),
            "auth" => Ok(ConnectionType::Auth),
            _ => Err(()),
        }
    }
}
impl Display for ConnectionMode {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            ConnectionMode::Request => write!(f, "request"),
            ConnectionMode::Publish => write!(f, "publish"),
            ConnectionMode::Bidirectional => write!(f, "bidirectional"),
        }
    }
}
impl FromStr for ConnectionMode {
    type Err = ();
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "request" => Ok(ConnectionMode::Request),
            "publish" => Ok(ConnectionMode::Publish),
            "bidirectional" => Ok(ConnectionMode::Bidirectional),
            _ => Err(()),
        }
    }
}
impl TryFrom<AccessControlEntry> for StandardAccessControlEntry {
    type Error = ();
    fn try_from(value: AccessControlEntry) -> Result<Self, Self::Error> {
        match &value.key[..] {
            "u" => Ok(StandardAccessControlEntry::UserName(value.value)),
            "r" => Ok(StandardAccessControlEntry::ResourceName(value.value)),
            "h" => Ok(StandardAccessControlEntry::HostName(value.value)),
            "s" => Ok(StandardAccessControlEntry::SessionId(value.value)),
            "t" => Ok(StandardAccessControlEntry::Type(value.value.parse()?)),
            "m" => Ok(StandardAccessControlEntry::Mode(value.value.parse()?)),
            _ => Err(()),
        }
    }
}
impl From<StandardAccessControlEntry> for AccessControlEntry {
    fn from(sace: StandardAccessControlEntry) -> Self {
        match sace {
            StandardAccessControlEntry::UserName(un) => AccessControlEntry::new("u", un),
            StandardAccessControlEntry::ResourceName(rn) => AccessControlEntry::new("r", rn),
            StandardAccessControlEntry::HostName(hn) => AccessControlEntry::new("h", hn),
            StandardAccessControlEntry::SessionId(sid) => AccessControlEntry::new("s", sid),
            StandardAccessControlEntry::Type(ty) => AccessControlEntry::new("t", format!("{ty}")),
            StandardAccessControlEntry::Mode(m) => AccessControlEntry::new("m", format!("{m}")),
        }
    }
}
impl Display for StandardAccessControlEntry {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        AccessControlEntry::from(self.clone()).fmt(f)
    }
}
impl Display for ParseAccessControlEntryError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            ParseAccessControlEntryError::NoValue => write!(f, "No value to corresponding key"),
            ParseAccessControlEntryError::WrongStart => {
                write!(f, "Access control entry did not start with #!::")
            }
        }
    }
}
impl Error for ParseAccessControlEntryError {}
#[cfg(test)]
mod tests {
    use super::*;
    #[test]
    fn parse_ace() {
        let ace_str = "#!::u=admin,r=bluesbrothers1_hi";
        let ace = ace_str.parse::<AccessControlList>().unwrap();
        assert_eq!(
            ace,
            AccessControlList(vec![
                AccessControlEntry::new("u", "admin"),
                AccessControlEntry::new("r", "bluesbrothers1_hi")
            ])
        );
        assert_eq!(ace_str, format!("{ace}"))
    }
}