// 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::{
    DateTime, Keyword, MembershipUrl, OrganizationUrl, PersonUrl, Url, date::Date,
    namespace::MembershipNamespaceUrl,
};

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

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

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

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

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

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

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

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

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

    #[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::{date, datetime};

    use super::Membership;
    use crate::namespace::MembershipNamespaceUrl;

    fn example_membership() -> Membership {
        Membership {
            id: "https://oparl.example.org/memberships/385"
                .parse()
                .expect("value must be parseable as id"),
            namespace: MembershipNamespaceUrl::Identifier,
            person: Some(
                "https://oparl.example.org/person/29"
                    .parse()
                    .expect("value must be parseable as url"),
            ),
            organization: Some(
                "https://oparl.example.org/organizations/5"
                    .parse()
                    .expect("value must be parseable as url"),
            ),
            role: Some("Vorsitzende".into()),
            voting_right: Some(true),
            start_date: Some(date!(2013 - 12 - 03).into()),
            end_date: None,
            on_behalf_of: None,
            license: None,
            keyword: vec![],
            created: datetime!(2011-11-11 11:11:00 +01:00).into(),
            modified: datetime!(2012-08-16 14:05:27 +02:00).into(),
            web: None,
            deleted: None,
            extensions: serde_json::Map::new(),
        }
    }

    fn example_membership_json() -> serde_json::Value {
        json!({
            "id": "https://oparl.example.org/memberships/385",
            "type": "https://schema.oparl.org/1.1/Membership",
            "person": "https://oparl.example.org/person/29",
            "organization": "https://oparl.example.org/organizations/5",
            "role": "Vorsitzende",
            "votingRight": true,
            "startDate": "2013-12-03",
            "created": "2011-11-11T11:11:00+01:00",
            "modified": "2012-08-16T14:05:27+02:00"
        })
    }

    #[test]
    fn serialize() {
        assert_eq!(json!(example_membership()), example_membership_json());
    }

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

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