use crate::sip::{Error, Scheme};

#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, Default)]
pub enum Transport {
    #[default]
    Udp,
    Tcp,
    Tls,
    TlsSctp,
    Sctp,
    Ws,
    Wss,
}

impl Transport {
    pub fn default_port(&self) -> Port {
        match self {
            Self::Udp => Port(5060),
            Self::Tcp => Port(5060),
            Self::Sctp => Port(5060),
            Self::TlsSctp => Port(5061),
            Self::Tls => Port(5061),
            Self::Ws => Port(80),
            Self::Wss => Port(443),
        }
    }

    pub fn protocol(&self) -> Self {
        match self {
            Self::Tls => Self::Tcp,
            Self::TlsSctp => Self::Sctp,
            Self::Wss => Self::Ws,
            _ => *self,
        }
    }

    pub fn is_secure(&self) -> bool {
        matches!(self, Self::Tls | Self::TlsSctp | Self::Wss)
    }

    pub fn sip_scheme(&self) -> Scheme {
        if self.is_secure() {
            Scheme::Sips
        } else {
            Scheme::Sip
        }
    }
}

impl std::fmt::Display for Transport {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            Self::Udp => write!(f, "UDP"),
            Self::Tcp => write!(f, "TCP"),
            Self::Tls => write!(f, "TLS"),
            Self::Sctp => write!(f, "SCTP"),
            Self::TlsSctp => write!(f, "TLS-SCTP"),
            Self::Ws => write!(f, "WS"),
            Self::Wss => write!(f, "WSS"),
        }
    }
}

impl std::str::FromStr for Transport {
    type Err = Error;
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s.trim() {
            s if s.eq_ignore_ascii_case("UDP") => Ok(Self::Udp),
            s if s.eq_ignore_ascii_case("TCP") => Ok(Self::Tcp),
            s if s.eq_ignore_ascii_case("TLS") => Ok(Self::Tls),
            s if s.eq_ignore_ascii_case("SCTP") => Ok(Self::Sctp),
            s if s.eq_ignore_ascii_case("TLS-SCTP") => Ok(Self::TlsSctp),
            s if s.eq_ignore_ascii_case("WS") => Ok(Self::Ws),
            s if s.eq_ignore_ascii_case("WSS") => Ok(Self::Wss),
            s => Err(Error::ParseError(format!("unknown transport: {}", s))),
        }
    }
}

impl std::convert::TryFrom<&str> for Transport {
    type Error = Error;
    fn try_from(s: &str) -> Result<Self, Self::Error> {
        s.parse()
    }
}

impl std::convert::TryFrom<&[u8]> for Transport {
    type Error = Error;
    fn try_from(b: &[u8]) -> Result<Self, Self::Error> {
        std::str::from_utf8(b)?.parse()
    }
}

#[derive(Debug, PartialEq, Eq, Ord, PartialOrd, Clone, Copy, Hash)]
pub struct Port(pub u16);

impl Port {
    pub fn value(&self) -> u16 {
        self.0
    }
}

impl std::fmt::Display for Port {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}", self.0)
    }
}

impl From<u16> for Port {
    fn from(v: u16) -> Self {
        Self(v)
    }
}

impl From<Port> for u16 {
    fn from(p: Port) -> u16 {
        p.0
    }
}

impl std::str::FromStr for Port {
    type Err = Error;
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        Ok(Port(s.trim().parse()?))
    }
}

impl std::convert::TryFrom<&str> for Port {
    type Error = Error;
    fn try_from(s: &str) -> Result<Self, Self::Error> {
        s.parse()
    }
}
