/// Description of an error from the REST APIs
#[derive(Debug,serde::Deserialize)]
pub struct RESTError {
    /// Error message
    #[serde(rename = "error-message")]
    pub error_message: String,
    /// Additonal error information
    #[serde(rename = "error-info")]
    pub error_info: serde_json::Value,
}

/// List of REST API error messages
#[derive(Debug,serde::Deserialize)]
pub struct RESTErrorList {
    /// Error messages
    pub errors: Vec<RESTError>,
}

/// Error returned by client functions
#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
pub enum Error {
    /// Login failed
    #[error("Login failed: {0}")]
    LoginFailure(String),

    /// Application login failed because an environment variable is missing
    #[error("Login failure, missing environment variable '{0}'")]
    LoginFailureMissingEnv(String),

    /// Failed returned by the HTTP server
    #[error("HTTP failed {0}, {1} - {2}")]
    WebServer(u16, String, String),

    /// Websocket error
    #[cfg(feature = "client")]
    #[error("Websocket error: {0}")]
    WebSocket(#[from] tokio_tungstenite::tungstenite::Error),

    /// JSON serialization/deserialization error
    #[error("Serde JSON error: {0}")]
    Serde(#[from] serde_json::Error),

    /// URL parsing error
    #[cfg(feature = "client")]
    #[error("URL: {0}")]
    URL(#[from] url::ParseError),

    /// HTTP client error
    #[cfg(feature = "client")]
    #[error("Reqwest: {0}")]
    HTTPClient(#[from] reqwest::Error),

    /// Volga error
    #[error("Error from Volga {0:?}")]
    Volga(Option<String>),

    /// This error is returned if we get data from the API we can't parse/understand
    #[error("API Error {0:?}")]
    API(String),

    /// This error is returned from the REST API, this typically means the client did something
    /// wrong.
    #[error("REST error {0:?}")]
    REST(RESTErrorList),

    /// TLS Errors
    #[cfg(feature = "client")]
    #[error("TLS error {0}")]
    TLS(#[from] tokio_rustls::rustls::Error),

    /// DNS Error
    #[cfg(feature = "client")]
    #[error("DNSName error {0}")]
    DNSName(#[from] tokio_rustls::rustls::pki_types::InvalidDnsNameError),

    /// IO Errors
    #[error("IO error {0}")]
    IO(#[from] std::io::Error),

    /// General Error
    #[error("Error {0}")]
    General(String),

    /// Convert web socket URL to URI
    #[error("Conversoin error {0}")]
    InvalidURI(#[from] http::uri::InvalidUri),
}

impl Error {
    /// Create a general error
    #[must_use]
    pub fn general(err: &str) -> Self {
        Self::General(err.to_string())
    }
}

/// Result type
pub type Result<T> = std::result::Result<T, Error>;

