// 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 crate::{
    AgendaItemUrl, ConsultationUrl, DateTime, Keyword, MeetingUrl, OrganizationUrl, PaperUrl, Url,
    namespace::ConsultationNamespaceUrl,
};

#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Consultation {
    pub id: ConsultationUrl,

    #[serde(rename = "type")]
    pub namespace: ConsultationNamespaceUrl,

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

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

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

    #[serde(default, skip_serializing_if = "Vec::is_empty")]
    pub organization: Vec<OrganizationUrl>,

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

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

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

    #[serde(default, skip_serializing_if = "Vec::is_empty")]
    pub keyword: Vec<Keyword>,

    pub created: DateTime,

    pub modified: DateTime,

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

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

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

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

    use super::Consultation;
    use crate::namespace::ConsultationNamespaceUrl;

    fn example_consultation() -> Consultation {
        Consultation {
            id: "https://oparl.example.org/consultation/47594"
                .parse()
                .expect("value must be parseable as id"),
            namespace: ConsultationNamespaceUrl::Identifier,
            paper: Some(
                "https://oparl.example.org/paper/749"
                    .parse()
                    .expect("value must be parseable as url"),
            ),
            agenda_item: Some(
                "https://oparl.example.org/agendaitem/15569"
                    .parse()
                    .expect("value must be parseable as url"),
            ),
            meeting: Some(
                "https://oparl.example.org/meeting/243"
                    .parse()
                    .expect("value must be parseable as url"),
            ),
            organization: vec![
                "https://oparl.example.org/organization/96"
                    .parse()
                    .expect("value must be parseable as url"),
            ],
            authoritative: Some(false),
            role: Some("Beschlussfassung".into()),
            license: None,
            keyword: vec![],
            created: datetime!(2012-01-08 14:05:27 +01:00).into(),
            modified: datetime!(2012-01-08 14:05:27 +01:00).into(),
            web: None,
            deleted: None,
            extensions: serde_json::Map::new(),
        }
    }

    fn example_consultation_json() -> serde_json::Value {
        json!({
            "id": "https://oparl.example.org/consultation/47594",
            "type": "https://schema.oparl.org/1.1/Consultation",
            "paper": "https://oparl.example.org/paper/749",
            "agendaItem": "https://oparl.example.org/agendaitem/15569",
            "meeting": "https://oparl.example.org/meeting/243",
            "organization": [
                "https://oparl.example.org/organization/96"
            ],
            "authoritative": false,
            "role": "Beschlussfassung",
            "created": "2012-01-08T14:05:27+01:00",
            "modified": "2012-01-08T14:05:27+01:00"
        })
    }

    #[test]
    fn serialize() {
        assert_eq!(json!(example_consultation()), example_consultation_json());
    }

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

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