use super::message::{WireCodecError, WireValidate};
use super::rpc::error::{RetryAdvice, WireError, WireErrorKind};
use super::rpc::id::{CallId, IdempotencyKey, RequestId};
use super::rpc::number::{ByteCount, FrameCount, Seq};
use super::rpc::text::{ErrorHelp, ErrorMessage, ErrorPath, ErrorTag, MetadataKey, MetadataValue};
use super::rpc::{Ack, Deadline, End, Flow, In, Out, Reply, Timeout, WireMetadata, WireVersion};

fn validate_from_display<T>(value: &T) -> Result<(), WireCodecError>
where
    T: ToString + std::str::FromStr<Err = efdn_types::TypeError>,
{
    value
        .to_string()
        .parse::<T>()
        .map(|_| ())
        .map_err(WireCodecError::invalid_value)
}

fn validate_from_text<T>(value: &str) -> Result<(), WireCodecError>
where
    T: std::str::FromStr<Err = efdn_types::TypeError>,
{
    value
        .parse::<T>()
        .map(|_| ())
        .map_err(WireCodecError::invalid_value)
}

impl WireValidate for () {
    fn validate_wire(&self) -> Result<(), WireCodecError> {
        Ok(())
    }
}

impl WireValidate for Seq {
    fn validate_wire(&self) -> Result<(), WireCodecError> {
        Ok(())
    }
}

impl WireValidate for ByteCount {
    fn validate_wire(&self) -> Result<(), WireCodecError> {
        Ok(())
    }
}

impl WireValidate for FrameCount {
    fn validate_wire(&self) -> Result<(), WireCodecError> {
        Ok(())
    }
}

impl WireValidate for Ack {
    fn validate_wire(&self) -> Result<(), WireCodecError> {
        Ok(())
    }
}

impl WireValidate for WireErrorKind {
    fn validate_wire(&self) -> Result<(), WireCodecError> {
        Ok(())
    }
}

impl WireValidate for RetryAdvice {
    fn validate_wire(&self) -> Result<(), WireCodecError> {
        Ok(())
    }
}

impl WireValidate for ErrorPath {
    fn validate_wire(&self) -> Result<(), WireCodecError> {
        validate_from_text::<Self>(self.as_str())
    }
}

impl WireValidate for ErrorMessage {
    fn validate_wire(&self) -> Result<(), WireCodecError> {
        validate_from_text::<Self>(self.as_str())
    }
}

impl WireValidate for ErrorTag {
    fn validate_wire(&self) -> Result<(), WireCodecError> {
        validate_from_text::<Self>(self.as_str())
    }
}

impl WireValidate for ErrorHelp {
    fn validate_wire(&self) -> Result<(), WireCodecError> {
        validate_from_text::<Self>(self.as_str())
    }
}

impl WireValidate for MetadataKey {
    fn validate_wire(&self) -> Result<(), WireCodecError> {
        validate_from_text::<Self>(self.as_str())
    }
}

impl WireValidate for MetadataValue {
    fn validate_wire(&self) -> Result<(), WireCodecError> {
        validate_from_text::<Self>(self.as_str())
    }
}

impl WireValidate for WireVersion {
    fn validate_wire(&self) -> Result<(), WireCodecError> {
        Self::new(self.get())
            .map(|_| ())
            .map_err(WireCodecError::invalid_value)
    }
}

impl WireValidate for Timeout {
    fn validate_wire(&self) -> Result<(), WireCodecError> {
        Self::new(self.duration())
            .map(|_| ())
            .map_err(WireCodecError::invalid_value)
    }
}

impl WireValidate for Deadline {
    fn validate_wire(&self) -> Result<(), WireCodecError> {
        self.timestamp().validate_wire()
    }
}

impl WireValidate for WireMetadata {
    fn validate_wire(&self) -> Result<(), WireCodecError> {
        self.iter()
            .try_for_each(|(key, value)| key.validate_wire().and_then(|_| value.validate_wire()))
    }
}

impl WireValidate for CallId {
    fn validate_wire(&self) -> Result<(), WireCodecError> {
        self.as_uuid_v7().validate_wire()
    }
}

impl WireValidate for RequestId {
    fn validate_wire(&self) -> Result<(), WireCodecError> {
        self.as_uuid_v7().validate_wire()
    }
}

impl WireValidate for IdempotencyKey {
    fn validate_wire(&self) -> Result<(), WireCodecError> {
        self.as_uuid_v7().validate_wire()
    }
}

impl WireValidate for efdn_types::NanoIdLength {
    fn validate_wire(&self) -> Result<(), WireCodecError> {
        Self::new(self.get())
            .map(|_| ())
            .map_err(WireCodecError::invalid_value)
    }
}

impl WireValidate for efdn_types::UuidId {
    fn validate_wire(&self) -> Result<(), WireCodecError> {
        Ok(())
    }
}

impl WireValidate for efdn_types::UuidV4 {
    fn validate_wire(&self) -> Result<(), WireCodecError> {
        Self::new(*self.as_uuid())
            .map(|_| ())
            .map_err(WireCodecError::invalid_value)
    }
}

impl WireValidate for efdn_types::UuidV7 {
    fn validate_wire(&self) -> Result<(), WireCodecError> {
        Self::new(*self.as_uuid())
            .map(|_| ())
            .map_err(WireCodecError::invalid_value)
    }
}

impl WireValidate for efdn_types::NanoId {
    fn validate_wire(&self) -> Result<(), WireCodecError> {
        Self::new(self.as_str())
            .map(|_| ())
            .map_err(WireCodecError::invalid_value)
    }
}

impl WireValidate for efdn_types::Timestamp {
    fn validate_wire(&self) -> Result<(), WireCodecError> {
        validate_from_display(self)
    }
}

impl WireValidate for efdn_types::CivilDate {
    fn validate_wire(&self) -> Result<(), WireCodecError> {
        validate_from_display(self)
    }
}

impl WireValidate for efdn_types::CivilTime {
    fn validate_wire(&self) -> Result<(), WireCodecError> {
        validate_from_display(self)
    }
}

impl WireValidate for efdn_types::CivilDateTime {
    fn validate_wire(&self) -> Result<(), WireCodecError> {
        validate_from_display(self)
    }
}

impl WireValidate for efdn_types::ZonedDateTime {
    fn validate_wire(&self) -> Result<(), WireCodecError> {
        validate_from_display(self)
    }
}

impl WireValidate for efdn_types::Span {
    fn validate_wire(&self) -> Result<(), WireCodecError> {
        validate_from_display(self)
    }
}

impl WireValidate for efdn_types::SignedDuration {
    fn validate_wire(&self) -> Result<(), WireCodecError> {
        validate_from_display(self)
    }
}

#[cfg(feature = "types-net")]
impl WireValidate for efdn_types::Port {
    fn validate_wire(&self) -> Result<(), WireCodecError> {
        Self::new(self.get())
            .map(|_| ())
            .map_err(WireCodecError::invalid_value)
    }
}

#[cfg(feature = "types-net")]
impl WireValidate for efdn_types::Host {
    fn validate_wire(&self) -> Result<(), WireCodecError> {
        validate_from_display(self)
    }
}

#[cfg(feature = "types-net")]
impl WireValidate for efdn_types::IpAddress {
    fn validate_wire(&self) -> Result<(), WireCodecError> {
        validate_from_display(self)
    }
}

#[cfg(feature = "types-net")]
impl WireValidate for efdn_types::Ipv4Address {
    fn validate_wire(&self) -> Result<(), WireCodecError> {
        validate_from_display(self)
    }
}

#[cfg(feature = "types-net")]
impl WireValidate for efdn_types::Ipv6Address {
    fn validate_wire(&self) -> Result<(), WireCodecError> {
        validate_from_display(self)
    }
}

#[cfg(feature = "types-net")]
impl WireValidate for efdn_types::SocketAddress {
    fn validate_wire(&self) -> Result<(), WireCodecError> {
        validate_from_display(self)
    }
}

#[cfg(feature = "types-web")]
impl WireValidate for efdn_types::Url {
    fn validate_wire(&self) -> Result<(), WireCodecError> {
        validate_from_display(self)
    }
}

#[cfg(feature = "types-web")]
impl WireValidate for efdn_types::HttpUrl {
    fn validate_wire(&self) -> Result<(), WireCodecError> {
        validate_from_display(self)
    }
}

#[cfg(feature = "types-web")]
impl WireValidate for efdn_types::HttpsUrl {
    fn validate_wire(&self) -> Result<(), WireCodecError> {
        validate_from_display(self)
    }
}

#[cfg(feature = "types-web")]
impl WireValidate for efdn_types::Origin {
    fn validate_wire(&self) -> Result<(), WireCodecError> {
        validate_from_display(self)
    }
}

impl WireValidate for WireError {
    fn validate_wire(&self) -> Result<(), WireCodecError> {
        self.kind.validate_wire()?;
        self.tag.validate_wire()?;
        self.message.validate_wire()?;
        self.help.validate_wire()?;
        if let Some(path) = &self.path {
            path.validate_wire()?;
        }
        self.retry.validate_wire()
    }
}

impl WireValidate for End {
    fn validate_wire(&self) -> Result<(), WireCodecError> {
        self.total.validate_wire()
    }
}

impl WireValidate for Flow {
    fn validate_wire(&self) -> Result<(), WireCodecError> {
        match self {
            Self::Credit { bytes } => bytes.validate_wire(),
            Self::Cancel => Ok(()),
        }
    }
}

impl<T: WireValidate> WireValidate for Option<T> {
    fn validate_wire(&self) -> Result<(), WireCodecError> {
        match self {
            Some(value) => value.validate_wire(),
            None => Ok(()),
        }
    }
}

impl<T: WireValidate> WireValidate for Vec<T> {
    fn validate_wire(&self) -> Result<(), WireCodecError> {
        self.iter().try_for_each(WireValidate::validate_wire)
    }
}

impl<T: WireValidate> WireValidate for Reply<T> {
    fn validate_wire(&self) -> Result<(), WireCodecError> {
        match self {
            Self::Ok(value) => value.validate_wire(),
            Self::Err(error) => error.validate_wire(),
        }
    }
}

impl<T: WireValidate> WireValidate for In<T> {
    fn validate_wire(&self) -> Result<(), WireCodecError> {
        match self {
            Self::Item { seq, body } => seq.validate_wire().and_then(|_| body.validate_wire()),
            Self::End { total } => total.validate_wire(),
        }
    }
}

impl<T: WireValidate> WireValidate for Out<T> {
    fn validate_wire(&self) -> Result<(), WireCodecError> {
        match self {
            Self::Item { seq, body } => seq.validate_wire().and_then(|_| body.validate_wire()),
            Self::End(end) => end.validate_wire(),
            Self::Err(error) => error.validate_wire(),
        }
    }
}
