use std::{borrow::Cow, fmt, time::Duration};
use serde::Serialize;
pub use crate::sdam::description::{server::ServerType, topology::TopologyType};
use crate::{
    bson::DateTime,
    error::Error,
    hello::HelloCommandResponse,
    options::ServerAddress,
    sdam::ServerDescription,
    selection_criteria::TagSet,
};
#[derive(Clone)]
pub struct ServerInfo<'a> {
    pub(crate) description: Cow<'a, ServerDescription>,
}
impl<'a> Serialize for ServerInfo<'a> {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: serde::Serializer,
    {
        self.description.serialize(serializer)
    }
}
impl<'a> ServerInfo<'a> {
    pub(crate) fn new_borrowed(description: &'a ServerDescription) -> Self {
        Self {
            description: Cow::Borrowed(description),
        }
    }
    pub(crate) fn new_owned(description: ServerDescription) -> Self {
        Self {
            description: Cow::Owned(description),
        }
    }
    fn command_response_getter<T>(
        &'a self,
        f: impl Fn(&'a HelloCommandResponse) -> Option<T>,
    ) -> Option<T> {
        self.description
            .reply
            .as_ref()
            .ok()
            .and_then(|reply| reply.as_ref().and_then(|r| f(&r.command_response)))
    }
    pub fn address(&self) -> &ServerAddress {
        &self.description.address
    }
    pub fn average_round_trip_time(&self) -> Option<Duration> {
        self.description.average_round_trip_time
    }
    pub fn last_update_time(&self) -> Option<DateTime> {
        self.description.last_update_time
    }
    pub fn max_wire_version(&self) -> Option<i32> {
        self.command_response_getter(|r| r.max_wire_version)
    }
    pub fn min_wire_version(&self) -> Option<i32> {
        self.command_response_getter(|r| r.min_wire_version)
    }
    pub fn replica_set_name(&self) -> Option<&str> {
        self.command_response_getter(|r| r.set_name.as_deref())
    }
    pub fn replica_set_version(&self) -> Option<i32> {
        self.command_response_getter(|r| r.set_version)
    }
    pub fn server_type(&self) -> ServerType {
        self.description.server_type
    }
    pub fn tags(&self) -> Option<&TagSet> {
        self.command_response_getter(|r| r.tags.as_ref())
    }
    pub fn error(&self) -> Option<&Error> {
        self.description.reply.as_ref().err()
    }
}
impl<'a> fmt::Debug for ServerInfo<'a> {
    fn fmt(&self, f: &mut fmt::Formatter) -> std::result::Result<(), fmt::Error> {
        match self.description.reply {
            Ok(_) => f
                .debug_struct("Server Description")
                .field("Address", self.address())
                .field("Type", &self.server_type())
                .field("Average RTT", &self.average_round_trip_time())
                .field("Last Update Time", &self.last_update_time())
                .field("Max Wire Version", &self.max_wire_version())
                .field("Min Wire Version", &self.min_wire_version())
                .field("Replica Set Name", &self.replica_set_name())
                .field("Replica Set Version", &self.replica_set_version())
                .field("Tags", &self.tags())
                .field(
                    "Compatibility Error",
                    &self.description.compatibility_error_message(),
                )
                .finish(),
            Err(ref e) => f
                .debug_struct("Server Description")
                .field("Address", self.address())
                .field("Type", &self.server_type())
                .field("Error", e)
                .finish(),
        }
    }
}
impl<'a> fmt::Display for ServerInfo<'a> {
    fn fmt(&self, f: &mut fmt::Formatter) -> std::result::Result<(), fmt::Error> {
        write!(
            f,
            "{{ Address: {}, Type: {:?}",
            self.address(),
            self.server_type()
        )?;
        match self.description.reply {
            Ok(_) => {
                if let Some(avg_rtt) = self.average_round_trip_time() {
                    write!(f, ", Average RTT: {:?}", avg_rtt)?;
                }
                if let Some(last_update_time) = self.last_update_time() {
                    write!(f, ", Last Update Time: {}", last_update_time)?;
                }
                if let Some(max_wire_version) = self.max_wire_version() {
                    write!(f, ", Max Wire Version: {}", max_wire_version)?;
                }
                if let Some(min_wire_version) = self.min_wire_version() {
                    write!(f, ", Min Wire Version: {}", min_wire_version)?;
                }
                if let Some(rs_name) = self.replica_set_name() {
                    write!(f, ", Replica Set Name: {}", rs_name)?;
                }
                if let Some(rs_version) = self.replica_set_version() {
                    write!(f, ", Replica Set Version: {}", rs_version)?;
                }
                if let Some(tags) = self.tags() {
                    write!(f, ", Tags: {:?}", tags)?;
                }
                if let Some(compatibility_error) = self.description.compatibility_error_message() {
                    write!(f, ", Compatiblity Error: {}", compatibility_error)?;
                }
            }
            Err(ref e) => {
                write!(f, ", Error: {}", e)?;
            }
        }
        write!(f, " }}")
    }
}