#[derive(Clone, Debug, Default, PartialEq)]
#[non_exhaustive]
pub struct Any(serde_json::Map<String, serde_json::Value>);
#[derive(thiserror::Error, Debug)]
pub enum AnyError {
    #[error("cannot serialize object into an Any, source={0:?}")]
    SerializationError(#[source] BoxedError),
    #[error("cannot deserialize from an Any, source={0:?}")]
    DeserializationError(#[source] BoxedError),
    #[error("expected type mismatch in Any deserialization type={0}")]
    TypeMismatchError(String),
}
impl AnyError {
    pub(crate) fn ser<T: Into<BoxedError>>(v: T) -> Self {
        Self::SerializationError(v.into())
    }
    pub(crate) fn deser<T: Into<BoxedError>>(v: T) -> Self {
        Self::DeserializationError(v.into())
    }
}
type BoxedError = Box<dyn std::error::Error + Send + Sync>;
type Error = AnyError;
impl Any {
    pub fn try_from<T>(message: &T) -> Result<Self, Error>
    where
        T: serde::ser::Serialize + crate::message::Message,
    {
        use serde_json::{Map, Value};
        let value = serde_json::to_value(message).map_err(Error::ser)?;
        let value = match value {
            Value::Object(mut map) => {
                map.insert(
                    "@type".to_string(),
                    Value::String(T::typename().to_string()),
                );
                map
            }
            Value::String(s) => {
                let map: Map<String, serde_json::Value> =
                    [("@type", T::typename().to_string()), ("value", s)]
                        .into_iter()
                        .map(|(k, v)| (k.to_string(), Value::String(v)))
                        .collect();
                map
            }
            _ => {
                return Err(Self::unexpected_json_type());
            }
        };
        Ok(Any(value))
    }
    pub fn try_into_message<T>(&self) -> Result<T, Error>
    where
        T: serde::de::DeserializeOwned + crate::message::Message,
    {
        let map = &self.0;
        let r#type = map
            .get("@type")
            .and_then(|v| v.as_str())
            .ok_or_else(|| "@type field is missing or is not a string".to_string())
            .map_err(Error::deser)?;
        Self::check_typename(r#type, T::typename())?;
        if r#type.starts_with("type.googleapis.com/google.protobuf.")
            && r#type != "type.googleapis.com/google.protobuf.Empty"
            && r#type != "type.googleapis.com/google.protobuf.FieldMask"
        {
            return map
                .get("value")
                .map(|v| serde_json::from_value::<T>(v.clone()))
                .ok_or_else(Self::missing_value_field)?
                .map_err(Error::deser);
        }
        serde_json::from_value::<T>(serde_json::Value::Object(map.clone())).map_err(Error::deser)
    }
    fn missing_value_field() -> Error {
        Error::deser("value field is missing")
    }
    fn unexpected_json_type() -> Error {
        Error::ser("unexpected JSON type, only Object and String are supported")
    }
    fn check_typename(got: &str, want: &str) -> Result<(), Error> {
        if got == want {
            return Ok(());
        }
        Err(Error::deser(format!("mismatched typenames extracting from Any, the any has {got}, the target type is {want}")))
    }
}
impl serde::ser::Serialize for Any {
    fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
    where
        S: serde::ser::Serializer,
    {
        self.0.serialize(serializer)
    }
}
impl<'de> serde::de::Deserialize<'de> for Any {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: serde::Deserializer<'de>,
    {
        let value = serde_json::Map::<String, serde_json::Value>::deserialize(deserializer)?;
        Ok(Any(value))
    }
}
#[cfg(test)]
mod test {
    use super::*;
    use crate::duration::*;
    use crate::empty::Empty;
    use crate::field_mask::*;
    use crate::timestamp::*;
    use serde_json::json;
    type Result = std::result::Result<(), Box<dyn std::error::Error>>;
    #[derive(Clone, Debug, Default, PartialEq, serde::Deserialize, serde::Serialize)]
    #[serde(rename_all = "camelCase")]
    #[non_exhaustive]
    pub struct Stored {
        #[serde(skip_serializing_if = "String::is_empty")]
        pub parent: String,
        #[serde(skip_serializing_if = "String::is_empty")]
        pub id: String,
    }
    impl crate::message::Message for Stored {
        fn typename() -> &'static str {
            "type.googleapis.com/wkt.test.Stored"
        }
    }
    #[test]
    fn serialize_duration() -> Result {
        let d = Duration::clamp(60, 0);
        let any = Any::try_from(&d)?;
        let got = serde_json::to_value(any)?;
        let want = json!({"@type": "type.googleapis.com/google.protobuf.Duration", "value": "60s"});
        assert_eq!(got, want);
        Ok(())
    }
    #[test]
    fn deserialize_duration() -> Result {
        let input =
            json!({"@type": "type.googleapis.com/google.protobuf.Duration", "value": "60s"});
        let any = Any(input.as_object().unwrap().clone());
        let d = any.try_into_message::<Duration>()?;
        assert_eq!(d, Duration::clamp(60, 0));
        Ok(())
    }
    #[test]
    fn serialize_empty() -> Result {
        let empty = Empty::default();
        let any = Any::try_from(&empty)?;
        let got = serde_json::to_value(any)?;
        let want = json!({"@type": "type.googleapis.com/google.protobuf.Empty"});
        assert_eq!(got, want);
        Ok(())
    }
    #[test]
    fn deserialize_empty() -> Result {
        let input = json!({"@type": "type.googleapis.com/google.protobuf.Empty"});
        let any = Any(input.as_object().unwrap().clone());
        let empty = any.try_into_message::<Empty>()?;
        assert_eq!(empty, Empty::default());
        Ok(())
    }
    #[test]
    fn serialize_field_mask() -> Result {
        let d = FieldMask::default().set_paths(["a", "b"].map(str::to_string).to_vec());
        let any = Any::try_from(&d)?;
        let got = serde_json::to_value(any)?;
        let want =
            json!({"@type": "type.googleapis.com/google.protobuf.FieldMask", "paths": "a,b"});
        assert_eq!(got, want);
        Ok(())
    }
    #[test]
    fn deserialize_field_mask() -> Result {
        let input =
            json!({"@type": "type.googleapis.com/google.protobuf.FieldMask", "paths": "a,b"});
        let any = Any(input.as_object().unwrap().clone());
        let d = any.try_into_message::<FieldMask>()?;
        assert_eq!(
            d,
            FieldMask::default().set_paths(["a", "b"].map(str::to_string).to_vec())
        );
        Ok(())
    }
    #[test]
    fn serialize_timestamp() -> Result {
        let d = Timestamp::clamp(123, 0);
        let any = Any::try_from(&d)?;
        let got = serde_json::to_value(any)?;
        let want = json!({"@type": "type.googleapis.com/google.protobuf.Timestamp", "value": "1970-01-01T00:02:03Z"});
        assert_eq!(got, want);
        Ok(())
    }
    #[test]
    fn deserialize_timestamp() -> Result {
        let input = json!({"@type": "type.googleapis.com/google.protobuf.Timestamp", "value": "1970-01-01T00:02:03Z"});
        let any = Any(input.as_object().unwrap().clone());
        let d = any.try_into_message::<Timestamp>()?;
        assert_eq!(d, Timestamp::clamp(123, 0));
        Ok(())
    }
    #[test]
    fn serialize_generic() -> Result {
        let d = Stored {
            parent: "parent".to_string(),
            id: "id".to_string(),
        };
        let any = Any::try_from(&d)?;
        let got = serde_json::to_value(any)?;
        let want =
            json!({"@type": "type.googleapis.com/wkt.test.Stored", "parent": "parent", "id": "id"});
        assert_eq!(got, want);
        Ok(())
    }
    #[test]
    fn deserialize_generic() -> Result {
        let input =
            json!({"@type": "type.googleapis.com/wkt.test.Stored", "parent": "parent", "id": "id"});
        let any = Any(input.as_object().unwrap().clone());
        let d = any.try_into_message::<Stored>()?;
        assert_eq!(
            d,
            Stored {
                parent: "parent".to_string(),
                id: "id".to_string()
            }
        );
        Ok(())
    }
    #[derive(Default, serde::Serialize)]
    struct DetectBadMessages(serde_json::Value);
    impl crate::message::Message for DetectBadMessages {
        fn typename() -> &'static str {
            "not used"
        }
    }
    #[test]
    fn try_from_error() -> Result {
        let input = DetectBadMessages(json!([2, 3]));
        let got = Any::try_from(&input);
        assert!(got.is_err(), "{got:?}");
        Ok(())
    }
    #[test]
    fn deserialize_missing_type_field() -> Result {
        let input = json!({"@type-is-missing": ""});
        let any = serde_json::from_value::<Any>(input)?;
        let got = any.try_into_message::<Stored>();
        assert!(got.is_err());
        Ok(())
    }
    #[test]
    fn deserialize_invalid_type_field() -> Result {
        let input = json!({"@type": [1, 2, 3]});
        let any = serde_json::from_value::<Any>(input)?;
        let got = any.try_into_message::<Stored>();
        assert!(got.is_err());
        Ok(())
    }
    #[test]
    fn deserialize_missing_value_field() -> Result {
        let input = json!({"@type": "type.googleapis.com/google.protobuf.Duration", "value-is-missing": "1.2s"});
        let any = serde_json::from_value::<Any>(input)?;
        let got = any.try_into_message::<Duration>();
        assert!(got.is_err());
        Ok(())
    }
    #[test]
    fn deserialize_invalid_value_field() -> Result {
        let input =
            json!({"@type": "type.googleapis.com/google.protobuf.Duration", "value": ["1.2s"]});
        let any = serde_json::from_value::<Any>(input)?;
        let got = any.try_into_message::<Duration>();
        assert!(got.is_err());
        Ok(())
    }
    #[test]
    fn deserialize_type_mismatch() -> Result {
        let input =
            json!({"@type": "type.googleapis.com/google.protobuf.Duration", "value": "1.2s"});
        let any = serde_json::from_value::<Any>(input)?;
        let got = any.try_into_message::<Timestamp>();
        assert!(got.is_err());
        let error = got.err().unwrap();
        assert!(
            format!("{error}").contains("type.googleapis.com/google.protobuf.Duration"),
            "{error}"
        );
        assert!(
            format!("{error}").contains("type.googleapis.com/google.protobuf.Timestamp"),
            "{error}"
        );
        Ok(())
    }
}