use strum_macros::Display;
use tonic::Status;

#[derive(Debug, Display)]
pub enum Error {
    AlreadyExists(String),
    FailedPrecondition(String),
    Internal(String),
    InvalidArgument(String),
    NotFound(String),
    Unavailable(String),
    Unimplemented(String),
}

impl Error {
    pub fn already_exists(message: impl Into<String>) -> Error {
        Self::AlreadyExists(message.into())
    }
    pub fn failed_precondition(message: impl Into<String>) -> Error {
        Self::FailedPrecondition(message.into())
    }
    pub fn internal(message: impl Into<String>) -> Error {
        Self::Internal(message.into())
    }
    pub fn invalid_argument(message: impl Into<String>) -> Error {
        Self::InvalidArgument(message.into())
    }
    pub fn not_found(message: impl Into<String>) -> Error {
        Self::NotFound(message.into())
    }
    pub fn unavailable(message: impl Into<String>) -> Error {
        Self::Unavailable(message.into())
    }
    pub fn unimplemented(message: impl Into<String>) -> Error {
        Self::Unimplemented(message.into())
    }
}

impl std::error::Error for Error {}

impl From<Error> for Status {
    fn from(e: Error) -> Self {
        match e {
            Error::AlreadyExists(message) => Status::already_exists(message),
            Error::FailedPrecondition(message) => Status::failed_precondition(message),
            Error::Internal(message) => Status::internal(message),
            Error::InvalidArgument(message) => Status::invalid_argument(message),
            Error::NotFound(message) => Status::not_found(message),
            Error::Unavailable(message) => Status::unavailable(message),
            Error::Unimplemented(message) => Status::unimplemented(message),
        }
    }
}

impl From<pbbson::Error> for Error {
    fn from(e: pbbson::Error) -> Self {
        Self::invalid_argument(e.to_string())
    }
}
