// SPDX-FileCopyrightText: Politik im Blick developers
// SPDX-FileCopyrightText: Wolfgang Silbermayr <wolfgang@silbermayr.at>
//
// SPDX-License-Identifier: AGPL-3.0-or-later OR EUPL-1.2

use std::fmt::Display;

use crate::namespace::ErrorNamespaceUrl;

#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Error {
    #[serde(rename = "type")]
    pub namespace: ErrorNamespaceUrl,

    pub message: String,

    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub debug: Option<String>,

    #[serde(default, flatten)]
    pub extensions: serde_json::Map<String, serde_json::Value>,
}

impl Display for Error {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        self.message.fmt(f)
    }
}

#[cfg(test)]
mod serde_tests {
    use pretty_assertions::assert_eq;
    use serde_json::json;

    use super::Error;
    use crate::namespace::ErrorNamespaceUrl;

    fn example_error() -> Error {
        Error {
            namespace: ErrorNamespaceUrl::Identifier,
            message: "Something went wrong".into(),
            debug: Some("Could not connect to database".into()),
            extensions: serde_json::Map::new(),
        }
    }

    fn example_error_json() -> serde_json::Value {
        json!(        {
            "type": "https://schema.oparl.org/1.1/Error",
            "message": "Something went wrong",
            "debug": "Could not connect to database",
        })
    }

    #[test]
    fn serialize() {
        assert_eq!(json!(example_error()), example_error_json());
    }

    #[test]
    fn deserialize_good() {
        let deserialized: Error = serde_json::from_value(example_error_json())
            .expect("value must be deserializable as Error");
        assert_eq!(deserialized, example_error());
    }

    #[test]
    fn deserialize_bad() {
        assert!(serde_json::from_value::<Error>(json!("xyzabcd")).is_err());
        assert!(serde_json::from_value::<Error>(json!(true)).is_err());
        assert!(serde_json::from_value::<Error>(json!(123)).is_err());
    }
}
