use shared::core::{self, ChainData};
use std::fmt;
use std::net::SocketAddr;

pub const MAX_MESSAGE_SIZE: usize = 1_000_000;

#[derive(serde::Serialize, serde::Deserialize, Debug, Clone)]
pub enum ProtocolMessage {
  BootstrapRequest {
    peer_id: uuid::Uuid,
    version: String,
  },
  BootstrapResponse {
    blocks: Option<String>,
  },
  GetPeersRequest {
    peer_id: uuid::Uuid,
  },
  GetPeersResponse {
    peer_addresses: Vec<SocketAddr>,
  },
  Handshake {
    peer_id: uuid::Uuid,
    version: String,
  },
  HandshakeAck {
    peer_id: uuid::Uuid,
    version: String,
  },
  Block {
    peer_id: uuid::Uuid,
    height: u64,
    block: core::Block,
  },
  ChainData {
    peer_id: uuid::Uuid,
    data: ChainData,
  },
  Ping {
    peer_id: uuid::Uuid,
  },
  Pong {
    peer_id: uuid::Uuid,
  },
  Disconnect {
    peer_id: uuid::Uuid,
  },
}

impl fmt::Display for ProtocolMessage {
  fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
    match self {
      ProtocolMessage::BootstrapRequest { peer_id, version } => {
        write!(f, "BootstrapRequest from {} (v{})", peer_id, version)
      }
      ProtocolMessage::BootstrapResponse { blocks } => {
        write!(
          f,
          "BootstrapResponse with {:?} blocks",
          blocks.clone().unwrap_or_default().len()
        )
      }
      ProtocolMessage::GetPeersRequest { peer_id } => {
        write!(f, "GetPeersRequest from {}", peer_id)
      }
      ProtocolMessage::GetPeersResponse { peer_addresses } => {
        write!(f, "GetPeersResponse with {} peers", peer_addresses.len())
      }
      ProtocolMessage::Handshake { peer_id, version } => {
        write!(f, "Handshake from {} (v{})", peer_id, version)
      }
      ProtocolMessage::HandshakeAck { peer_id, version } => {
        write!(f, "HandshakeAck from {} (v{})", peer_id, version)
      }
      ProtocolMessage::Block {
        peer_id,
        height,
        block: _,
      } => {
        write!(f, "Block #{} from {}", height, peer_id)
      }
      ProtocolMessage::ChainData { peer_id, data: _ } => {
        write!(f, "ChainData from {}", peer_id)
      }
      ProtocolMessage::Ping { peer_id } => {
        write!(f, "Ping from {}", peer_id)
      }
      ProtocolMessage::Pong { peer_id } => {
        write!(f, "Pong from {}", peer_id)
      }
      ProtocolMessage::Disconnect { peer_id } => {
        write!(f, "Disconnect from {}", peer_id)
      }
    }
  }
}
