use std::sync::Arc;
use crate::{
  JoinMessageTransformError, LeaveMessageTransformError, MemberTransformError,
  PushPullMessageTransformError, QueryMessageTransformError, QueryResponseMessageTransformError,
  UserEventMessageTransformError,
};
use super::{
  Encodable, JoinMessage, LeaveMessage, Member, PushPullMessage, PushPullMessageRef, QueryMessage,
  QueryResponseMessage, Transformable, UserEventMessage,
};
#[cfg(feature = "encryption")]
use super::{KeyRequestMessage, KeyResponseMessage};
const LEAVE_MESSAGE_TAG: u8 = 0;
const JOIN_MESSAGE_TAG: u8 = 1;
const PUSH_PULL_MESSAGE_TAG: u8 = 2;
const USER_EVENT_MESSAGE_TAG: u8 = 3;
const QUERY_MESSAGE_TAG: u8 = 4;
const QUERY_RESPONSE_MESSAGE_TAG: u8 = 5;
const CONFLICT_RESPONSE_MESSAGE_TAG: u8 = 6;
const RELAY_MESSAGE_TAG: u8 = 7;
#[cfg(feature = "encryption")]
const KEY_REQUEST_MESSAGE_TAG: u8 = 253;
#[cfg(feature = "encryption")]
const KEY_RESPONSE_MESSAGE_TAG: u8 = 254;
#[derive(Debug, thiserror::Error)]
#[error("unknown message type byte: {0}")]
pub struct UnknownMessageType(u8);
impl TryFrom<u8> for MessageType {
  type Error = UnknownMessageType;
  fn try_from(value: u8) -> Result<Self, Self::Error> {
    Ok(match value {
      LEAVE_MESSAGE_TAG => Self::Leave,
      JOIN_MESSAGE_TAG => Self::Join,
      PUSH_PULL_MESSAGE_TAG => Self::PushPull,
      USER_EVENT_MESSAGE_TAG => Self::UserEvent,
      QUERY_MESSAGE_TAG => Self::Query,
      QUERY_RESPONSE_MESSAGE_TAG => Self::QueryResponse,
      CONFLICT_RESPONSE_MESSAGE_TAG => Self::ConflictResponse,
      RELAY_MESSAGE_TAG => Self::Relay,
      #[cfg(feature = "encryption")]
      KEY_REQUEST_MESSAGE_TAG => Self::KeyRequest,
      #[cfg(feature = "encryption")]
      KEY_RESPONSE_MESSAGE_TAG => Self::KeyResponse,
      _ => return Err(UnknownMessageType(value)),
    })
  }
}
impl From<MessageType> for u8 {
  fn from(value: MessageType) -> Self {
    value as u8
  }
}
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
#[repr(u8)]
#[non_exhaustive]
pub enum MessageType {
  Leave = LEAVE_MESSAGE_TAG,
  Join = JOIN_MESSAGE_TAG,
  PushPull = PUSH_PULL_MESSAGE_TAG,
  UserEvent = USER_EVENT_MESSAGE_TAG,
  Query = QUERY_MESSAGE_TAG,
  QueryResponse = QUERY_RESPONSE_MESSAGE_TAG,
  ConflictResponse = CONFLICT_RESPONSE_MESSAGE_TAG,
  Relay = RELAY_MESSAGE_TAG,
  #[cfg(feature = "encryption")]
  KeyRequest = KEY_REQUEST_MESSAGE_TAG,
  #[cfg(feature = "encryption")]
  KeyResponse = KEY_RESPONSE_MESSAGE_TAG,
}
impl MessageType {
  #[inline]
  pub const fn as_str(&self) -> &'static str {
    match self {
      Self::Leave => "leave",
      Self::Join => "join",
      Self::PushPull => "push pull",
      Self::UserEvent => "user event",
      Self::Query => "query",
      Self::QueryResponse => "query response",
      Self::ConflictResponse => "conflict response",
      Self::Relay => "relay",
      #[cfg(feature = "encryption")]
      Self::KeyRequest => "key request",
      #[cfg(feature = "encryption")]
      Self::KeyResponse => "key response",
    }
  }
}
pub trait AsMessageRef<I, A> {
  fn as_message_ref(&self) -> SerfMessageRef<'_, I, A>;
}
#[derive(Debug)]
pub enum SerfMessageRef<'a, I, A> {
  Leave(&'a LeaveMessage<I>),
  Join(&'a JoinMessage<I>),
  PushPull(PushPullMessageRef<'a, I>),
  UserEvent(&'a UserEventMessage),
  Query(&'a QueryMessage<I, A>),
  QueryResponse(&'a QueryResponseMessage<I, A>),
  ConflictResponse(&'a Member<I, A>),
  #[cfg(feature = "encryption")]
  KeyRequest(&'a KeyRequestMessage),
  #[cfg(feature = "encryption")]
  KeyResponse(&'a KeyResponseMessage),
}
impl<'a, I, A> Clone for SerfMessageRef<'a, I, A> {
  fn clone(&self) -> Self {
    *self
  }
}
impl<'a, I, A> Copy for SerfMessageRef<'a, I, A> {}
impl<'a, I, A> AsMessageRef<I, A> for SerfMessageRef<'a, I, A> {
  fn as_message_ref(&self) -> SerfMessageRef<I, A> {
    *self
  }
}
#[derive(Debug, Clone)]
pub enum SerfMessage<I, A> {
  Leave(LeaveMessage<I>),
  Join(JoinMessage<I>),
  PushPull(PushPullMessage<I>),
  UserEvent(UserEventMessage),
  Query(QueryMessage<I, A>),
  QueryResponse(QueryResponseMessage<I, A>),
  ConflictResponse(Member<I, A>),
  #[cfg(feature = "encryption")]
  KeyRequest(KeyRequestMessage),
  #[cfg(feature = "encryption")]
  KeyResponse(KeyResponseMessage),
}
impl<'a, I, A> From<&'a SerfMessage<I, A>> for MessageType {
  fn from(msg: &'a SerfMessage<I, A>) -> Self {
    match msg {
      SerfMessage::Leave(_) => MessageType::Leave,
      SerfMessage::Join(_) => MessageType::Join,
      SerfMessage::PushPull(_) => MessageType::PushPull,
      SerfMessage::UserEvent(_) => MessageType::UserEvent,
      SerfMessage::Query(_) => MessageType::Query,
      SerfMessage::QueryResponse(_) => MessageType::QueryResponse,
      SerfMessage::ConflictResponse(_) => MessageType::ConflictResponse,
      #[cfg(feature = "encryption")]
      SerfMessage::KeyRequest(_) => MessageType::KeyRequest,
      #[cfg(feature = "encryption")]
      SerfMessage::KeyResponse(_) => MessageType::KeyResponse,
    }
  }
}
impl<I, A> AsMessageRef<I, A> for QueryMessage<I, A> {
  fn as_message_ref(&self) -> SerfMessageRef<I, A> {
    SerfMessageRef::Query(self)
  }
}
impl<I, A> AsMessageRef<I, A> for QueryResponseMessage<I, A> {
  fn as_message_ref(&self) -> SerfMessageRef<I, A> {
    SerfMessageRef::QueryResponse(self)
  }
}
impl<I, A> AsMessageRef<I, A> for JoinMessage<I> {
  fn as_message_ref(&self) -> SerfMessageRef<I, A> {
    SerfMessageRef::Join(self)
  }
}
impl<I, A> AsMessageRef<I, A> for UserEventMessage {
  fn as_message_ref(&self) -> SerfMessageRef<I, A> {
    SerfMessageRef::UserEvent(self)
  }
}
impl<'a, I, A> AsMessageRef<I, A> for &'a QueryMessage<I, A> {
  fn as_message_ref(&self) -> SerfMessageRef<I, A> {
    SerfMessageRef::Query(self)
  }
}
impl<'a, I, A> AsMessageRef<I, A> for &'a QueryResponseMessage<I, A> {
  fn as_message_ref(&self) -> SerfMessageRef<I, A> {
    SerfMessageRef::QueryResponse(self)
  }
}
impl<'a, I, A> AsMessageRef<I, A> for &'a JoinMessage<I> {
  fn as_message_ref(&self) -> SerfMessageRef<I, A> {
    SerfMessageRef::Join(self)
  }
}
impl<'a, I, A> AsMessageRef<I, A> for PushPullMessageRef<'a, I> {
  fn as_message_ref(&self) -> SerfMessageRef<I, A> {
    SerfMessageRef::PushPull(*self)
  }
}
impl<'a, I, A> AsMessageRef<I, A> for &'a PushPullMessage<I> {
  fn as_message_ref(&self) -> SerfMessageRef<I, A> {
    SerfMessageRef::PushPull(PushPullMessageRef {
      ltime: self.ltime,
      status_ltimes: &self.status_ltimes,
      left_members: &self.left_members,
      event_ltime: self.event_ltime,
      events: &self.events,
      query_ltime: self.query_ltime,
    })
  }
}
impl<'a, I, A> AsMessageRef<I, A> for &'a UserEventMessage {
  fn as_message_ref(&self) -> SerfMessageRef<I, A> {
    SerfMessageRef::UserEvent(self)
  }
}
impl<'a, I, A> AsMessageRef<I, A> for &'a LeaveMessage<I> {
  fn as_message_ref(&self) -> SerfMessageRef<I, A> {
    SerfMessageRef::Leave(self)
  }
}
impl<'a, I, A> AsMessageRef<I, A> for &'a Member<I, A> {
  fn as_message_ref(&self) -> SerfMessageRef<I, A> {
    SerfMessageRef::ConflictResponse(self)
  }
}
impl<'a, I, A> AsMessageRef<I, A> for &'a Arc<Member<I, A>> {
  fn as_message_ref(&self) -> SerfMessageRef<I, A> {
    SerfMessageRef::ConflictResponse(self)
  }
}
#[cfg(feature = "encryption")]
impl<'a, I, A> AsMessageRef<I, A> for &'a KeyRequestMessage {
  fn as_message_ref(&self) -> SerfMessageRef<I, A> {
    SerfMessageRef::KeyRequest(self)
  }
}
#[cfg(feature = "encryption")]
impl<'a, I, A> AsMessageRef<I, A> for &'a KeyResponseMessage {
  fn as_message_ref(&self) -> SerfMessageRef<I, A> {
    SerfMessageRef::KeyResponse(self)
  }
}
impl<I, A> AsMessageRef<I, A> for SerfMessage<I, A> {
  fn as_message_ref(&self) -> SerfMessageRef<I, A> {
    match self {
      Self::Leave(l) => SerfMessageRef::Leave(l),
      Self::Join(j) => SerfMessageRef::Join(j),
      Self::PushPull(pp) => SerfMessageRef::PushPull(PushPullMessageRef {
        ltime: pp.ltime,
        status_ltimes: &pp.status_ltimes,
        left_members: &pp.left_members,
        event_ltime: pp.event_ltime,
        events: &pp.events,
        query_ltime: pp.query_ltime,
      }),
      Self::UserEvent(u) => SerfMessageRef::UserEvent(u),
      Self::Query(q) => SerfMessageRef::Query(q),
      Self::QueryResponse(q) => SerfMessageRef::QueryResponse(q),
      Self::ConflictResponse(m) => SerfMessageRef::ConflictResponse(m),
      #[cfg(feature = "encryption")]
      Self::KeyRequest(kr) => SerfMessageRef::KeyRequest(kr),
      #[cfg(feature = "encryption")]
      Self::KeyResponse(kr) => SerfMessageRef::KeyResponse(kr),
    }
  }
}
impl<'b, I, A> AsMessageRef<I, A> for &'b SerfMessage<I, A> {
  fn as_message_ref(&self) -> SerfMessageRef<I, A> {
    match self {
      SerfMessage::Leave(l) => SerfMessageRef::Leave(l),
      SerfMessage::Join(j) => SerfMessageRef::Join(j),
      SerfMessage::PushPull(pp) => SerfMessageRef::PushPull(PushPullMessageRef {
        ltime: pp.ltime,
        status_ltimes: &pp.status_ltimes,
        left_members: &pp.left_members,
        event_ltime: pp.event_ltime,
        events: &pp.events,
        query_ltime: pp.query_ltime,
      }),
      SerfMessage::UserEvent(u) => SerfMessageRef::UserEvent(u),
      SerfMessage::Query(q) => SerfMessageRef::Query(q),
      SerfMessage::QueryResponse(q) => SerfMessageRef::QueryResponse(q),
      SerfMessage::ConflictResponse(m) => SerfMessageRef::ConflictResponse(m),
      #[cfg(feature = "encryption")]
      SerfMessage::KeyRequest(kr) => SerfMessageRef::KeyRequest(kr),
      #[cfg(feature = "encryption")]
      SerfMessage::KeyResponse(kr) => SerfMessageRef::KeyResponse(kr),
    }
  }
}
impl<I, A> core::fmt::Display for SerfMessage<I, A> {
  fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
    write!(f, "{}", self.ty().as_str())
  }
}
impl<I, A> SerfMessage<I, A> {
  #[inline]
  pub const fn ty(&self) -> MessageType {
    match self {
      Self::Leave(_) => MessageType::Leave,
      Self::Join(_) => MessageType::Join,
      Self::PushPull(_) => MessageType::PushPull,
      Self::UserEvent(_) => MessageType::UserEvent,
      Self::Query(_) => MessageType::Query,
      Self::QueryResponse(_) => MessageType::QueryResponse,
      Self::ConflictResponse(_) => MessageType::ConflictResponse,
      #[cfg(feature = "encryption")]
      Self::KeyRequest(_) => MessageType::KeyRequest,
      #[cfg(feature = "encryption")]
      Self::KeyResponse(_) => MessageType::KeyResponse,
    }
  }
}
#[derive(thiserror::Error)]
pub enum SerfMessageTransformError<I, A>
where
  I: Transformable + core::hash::Hash + Eq,
  A: Transformable + core::hash::Hash + Eq,
{
  #[error(transparent)]
  Leave(#[from] LeaveMessageTransformError<I>),
  #[error(transparent)]
  Join(#[from] JoinMessageTransformError<I>),
  #[error(transparent)]
  PushPull(#[from] PushPullMessageTransformError<I>),
  #[error(transparent)]
  UserEvent(#[from] UserEventMessageTransformError),
  #[error(transparent)]
  Query(#[from] QueryMessageTransformError<I, A>),
  #[error(transparent)]
  QueryResponse(#[from] QueryResponseMessageTransformError<I, A>),
  #[error(transparent)]
  ConflictResponse(#[from] MemberTransformError<I, A>),
  #[cfg(feature = "encryption")]
  #[error(transparent)]
  KeyRequest(#[from] crate::key::OptionSecretKeyTransformError),
  #[cfg(feature = "encryption")]
  #[error(transparent)]
  KeyResponse(#[from] crate::key::KeyResponseMessageTransformError),
}
impl<I, A> core::fmt::Debug for SerfMessageTransformError<I, A>
where
  I: Transformable + core::hash::Hash + Eq,
  A: Transformable + core::hash::Hash + Eq,
{
  fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
    write!(f, "{}", self)
  }
}
impl<'a, I, A> Encodable for SerfMessageRef<'a, I, A>
where
  I: Transformable + core::hash::Hash + Eq,
  A: Transformable + core::hash::Hash + Eq,
{
  type Error = SerfMessageTransformError<I, A>;
  #[inline]
  fn encoded_len(&self) -> usize {
    match *self {
      Self::Leave(msg) => Transformable::encoded_len(msg),
      Self::Join(msg) => Transformable::encoded_len(msg),
      Self::PushPull(msg) => Encodable::encoded_len(&msg),
      Self::UserEvent(msg) => Transformable::encoded_len(msg),
      Self::Query(msg) => Transformable::encoded_len(msg),
      Self::QueryResponse(msg) => Transformable::encoded_len(msg),
      Self::ConflictResponse(msg) => Transformable::encoded_len(msg),
      #[cfg(feature = "encryption")]
      Self::KeyRequest(msg) => Transformable::encoded_len(msg),
      #[cfg(feature = "encryption")]
      Self::KeyResponse(msg) => Transformable::encoded_len(msg),
    }
  }
  #[inline]
  fn encode(&self, dst: &mut [u8]) -> Result<usize, Self::Error> {
    match *self {
      Self::Leave(msg) => Transformable::encode(msg, dst).map_err(Into::into),
      Self::Join(msg) => Transformable::encode(msg, dst).map_err(Into::into),
      Self::PushPull(msg) => Encodable::encode(&msg, dst).map_err(Into::into),
      Self::UserEvent(msg) => Transformable::encode(msg, dst).map_err(Into::into),
      Self::Query(msg) => Transformable::encode(msg, dst).map_err(Into::into),
      Self::QueryResponse(msg) => Transformable::encode(msg, dst).map_err(Into::into),
      Self::ConflictResponse(msg) => Transformable::encode(msg, dst).map_err(Into::into),
      #[cfg(feature = "encryption")]
      Self::KeyRequest(msg) => Transformable::encode(msg, dst).map_err(Into::into),
      #[cfg(feature = "encryption")]
      Self::KeyResponse(msg) => Transformable::encode(msg, dst).map_err(Into::into),
    }
  }
}