use chrono::{DateTime, Utc};
use is_empty::IsEmpty;
use serde::{Deserialize, Serialize};
use self::barcode::Barcode;
use self::beacon::Beacon;
use self::location::Location;
use self::nfc::NFC;
use self::semantic_tags::SemanticTags;
use self::visual_appearance::VisualAppearance;
use self::web_service::WebService;
pub mod barcode;
pub mod beacon;
mod date_format;
pub mod fields;
pub mod location;
pub mod nfc;
pub mod semantic_tags;
pub mod visual_appearance;
pub mod web_service;
#[derive(Serialize, Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
pub struct PassConfig {
        pub organization_name: String,
        pub description: String,
            pub pass_type_identifier: String,
        pub team_identifier: String,
            pub serial_number: String,
}
#[derive(Serialize, Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
pub struct Pass {
        format_version: u32,
        #[serde(flatten)]
    pub config: PassConfig,
                        #[serde(default)]
    #[serde(skip_serializing_if = "Option::is_none")]
    pub grouping_identifier: Option<String>,
        #[serde(default)]
    #[serde(flatten)]
    #[serde(skip_serializing_if = "Option::is_none")]
    pub appearance: Option<VisualAppearance>,
        #[serde(default)]
    #[serde(skip_serializing_if = "Option::is_none")]
    pub logo_text: Option<String>,
        #[serde(default)]
    #[serde(skip_serializing_if = "Option::is_none")]
    #[serde(with = "date_format")]
    pub relevant_date: Option<DateTime<Utc>>,
        #[serde(default)]
    #[serde(skip_serializing_if = "Option::is_none")]
    #[serde(with = "date_format")]
    pub expiration_date: Option<DateTime<Utc>>,
        #[serde(default)]
    #[serde(skip_serializing_if = "Option::is_none")]
    #[serde(rename = "appLaunchURL")]
    pub app_launch_url: Option<String>,
        #[serde(default)]
    #[serde(skip_serializing_if = "Vec::is_empty")]
    pub associated_store_identifiers: Vec<i32>,
                #[serde(default)]
    #[serde(flatten)]
    #[serde(skip_serializing_if = "Option::is_none")]
    pub web_service: Option<WebService>,
                        #[serde(default)]
    #[serde(skip_serializing_if = "_is_false")]
    pub sharing_prohibited: bool,
                #[serde(default = "_default_true")]
    #[serde(skip_serializing_if = "_is_true")]
    pub suppress_strip_shine: bool,
                #[serde(default)]
    #[serde(skip_serializing_if = "_is_false")]
    pub voided: bool,
                #[serde(default)]
    #[serde(skip_serializing_if = "Vec::is_empty")]
    pub barcodes: Vec<Barcode>,
        #[serde(default)]
    #[serde(skip_serializing_if = "Vec::is_empty")]
    pub beacons: Vec<Beacon>,
        #[serde(default)]
    #[serde(skip_serializing_if = "Vec::is_empty")]
    pub locations: Vec<Location>,
        #[serde(default)]
    #[serde(skip_serializing_if = "Option::is_none")]
    pub max_distance: Option<u32>,
        #[serde(default)]
    #[serde(skip_serializing_if = "Option::is_none")]
    pub nfc: Option<NFC>,
                    #[serde(default)]
    #[serde(skip_serializing_if = "SemanticTags::is_empty")]
    pub semantics: SemanticTags,
        #[serde(flatten)]
    pub fields: fields::Type,
        }
impl Pass {
                                                                                                                                            pub fn make_json(&self) -> Result<String, serde_json::Error> {
        let json = serde_json::to_string_pretty(&self)?;
        Ok(json)
    }
                                                                                                        pub fn from_json(data: &str) -> Result<Self, serde_json::Error> {
        let pass: Pass = serde_json::from_str(data)?;
        Ok(pass)
    }
}
pub struct PassBuilder {
    pass: Pass,
}
impl PassBuilder {
        pub fn new(config: PassConfig) -> Self {
        let pass = Pass {
                        format_version: 1,
            config,
                        grouping_identifier: None,
            appearance: None,
            logo_text: None,
            relevant_date: None,
            expiration_date: None,
            app_launch_url: None,
            associated_store_identifiers: Vec::new(),
            web_service: None,
            sharing_prohibited: false,
            suppress_strip_shine: true,
            voided: false,
            barcodes: Vec::new(),
            beacons: Vec::new(),
            locations: Vec::new(),
            max_distance: None,
            nfc: None,
            semantics: Default::default(),
            fields: fields::Type::Generic {
                pass_fields: fields::Fields {
                    ..Default::default()
                },
            },
        };
        Self { pass }
    }
        pub fn grouping_identifier(mut self, field: String) -> PassBuilder {
        self.pass.grouping_identifier = Some(field);
        self
    }
        pub fn appearance(mut self, field: VisualAppearance) -> PassBuilder {
        self.pass.appearance = Some(field);
        self
    }
        pub fn logo_text(mut self, field: String) -> PassBuilder {
        self.pass.logo_text = Some(field);
        self
    }
                                                            pub fn relevant_date(mut self, field: DateTime<Utc>) -> PassBuilder {
        self.pass.relevant_date = Some(field);
        self
    }
        pub fn expiration_date(mut self, field: DateTime<Utc>) -> PassBuilder {
        self.pass.expiration_date = Some(field);
        self
    }
        pub fn app_launch_url(mut self, field: String) -> PassBuilder {
        self.pass.app_launch_url = Some(field);
        self
    }
        pub fn add_associated_store_identifier(mut self, id: i32) -> PassBuilder {
        self.pass.associated_store_identifiers.push(id);
        self
    }
        pub fn web_service(mut self, web_service: WebService) -> PassBuilder {
        self.pass.web_service = Some(web_service);
        self
    }
        pub fn set_sharing_prohibited(mut self, field: bool) -> PassBuilder {
        self.pass.sharing_prohibited = field;
        self
    }
        pub fn set_suppress_strip_shine(mut self, field: bool) -> PassBuilder {
        self.pass.suppress_strip_shine = field;
        self
    }
        pub fn voided(mut self, field: bool) -> PassBuilder {
        self.pass.voided = field;
        self
    }
        pub fn add_barcode(mut self, barcode: Barcode) -> PassBuilder {
        self.pass.barcodes.push(barcode);
        self
    }
        pub fn add_beacon(mut self, beacon: Beacon) -> PassBuilder {
        self.pass.beacons.push(beacon);
        self
    }
        pub fn add_location(mut self, location: Location) -> PassBuilder {
        assert!(
            self.pass.locations.len() < 10,
            "Reached limit for geographic locations (maximum - 10)"
        );
        self.pass.locations.push(location);
        self
    }
        pub fn max_distance(mut self, field: u32) -> PassBuilder {
        self.pass.max_distance = Some(field);
        self
    }
        pub fn nfc(mut self, field: NFC) -> PassBuilder {
        self.pass.nfc = Some(field);
        self
    }
                                                                                        pub fn semantics(mut self, field: SemanticTags) -> PassBuilder {
        self.pass.semantics = field;
        self
    }
                                                                                                                                                    pub fn fields(mut self, field: fields::Type) -> PassBuilder {
        self.pass.fields = field;
        self
    }
        pub fn build(self) -> Pass {
        self.pass
    }
}
#[cfg(test)]
mod tests {
    use chrono::prelude::*;
    use tests::{fields, semantic_tags::SemanticTagLocation, visual_appearance::Color};
    use super::*;
    #[test]
    fn make_minimal_pass() {
                let pass = PassBuilder::new(PassConfig {
            organization_name: String::from("Apple inc."),
            description: String::from("Example pass"),
            pass_type_identifier: String::from("com.example.pass"),
            team_identifier: String::from("AA00AA0A0A"),
            serial_number: String::from("ABCDEFG1234567890"),
        })
        .build();
        let json = pass.make_json().unwrap();
        println!("{}", json);
        let json_expected = r#"{
  "formatVersion": 1,
  "organizationName": "Apple inc.",
  "description": "Example pass",
  "passTypeIdentifier": "com.example.pass",
  "teamIdentifier": "AA00AA0A0A",
  "serialNumber": "ABCDEFG1234567890",
  "generic": {
    "auxiliaryFields": [],
    "backFields": [],
    "headerFields": [],
    "primaryFields": [],
    "secondaryFields": []
  }
}"#;
        assert_eq!(json_expected, json);
                let pass: Pass = Pass::from_json(json_expected).unwrap();
        let json = pass.make_json().unwrap();
        assert_eq!(json_expected, json);
    }
    #[test]
    fn make_pass() {
                let pass = PassBuilder::new(PassConfig {
            organization_name: String::from("Apple inc."),
            description: String::from("Example pass"),
            pass_type_identifier: String::from("com.example.pass"),
            team_identifier: String::from("AA00AA0A0A"),
            serial_number: String::from("ABCDEFG1234567890"),
        })
        .grouping_identifier(String::from("com.example.pass.app"))
        .appearance(VisualAppearance {
            label_color: None,
            foreground_color: Color::new(250, 10, 10),
            background_color: Color::white(),
        })
        .logo_text(String::from("Test pass"))
        .relevant_date(Utc.with_ymd_and_hms(2024, 02, 07, 0, 0, 0).unwrap())
        .expiration_date(Utc.with_ymd_and_hms(2024, 02, 08, 0, 0, 0).unwrap())
        .app_launch_url(String::from("testapp:param?index=1"))
        .add_associated_store_identifier(100)
        .web_service(WebService {
            authentication_token: String::from("abcdefg01234567890abcdefg"),
            web_service_url: String::from("https://example.com/passes/"),
        })
        .set_sharing_prohibited(false)
        .set_suppress_strip_shine(false)
        .voided(false)
        .add_barcode(Barcode {
            message: String::from("Hello world!"),
            format: barcode::BarcodeFormat::QR,
            alt_text: Some(String::from("test by test")),
            ..Default::default()
        })
        .add_beacon(Beacon {
            proximity_uuid: String::from("e286373b-15b5-4f4e-bf91-e9e64787724a"),
            major: Some(2),
            minor: Some(150),
            relevant_text: Some(String::from("The simple beacon")),
        })
        .add_location(Location {
            latitude: 37.334606,
            longitude: -122.009102,
            relevant_text: Some(String::from("Apple Park, Cupertino, CA, USA")),
            ..Default::default()
        })
        .max_distance(1000)
        .nfc(NFC {
            encryption_public_key: String::from("ABCDEFG_0011223344556677889900"),
            message: String::from("test message"),
            ..Default::default()
        })
        .semantics(SemanticTags {
            airline_code: String::from("EX123").into(),
            departure_location: SemanticTagLocation {
                latitude: 43.3948533,
                longitude: 132.1451673,
            }
            .into(),
            ..Default::default()
        })
        .fields(
            fields::Type::BoardingPass {
                pass_fields: fields::Fields {
                    ..Default::default()
                },
                transit_type: fields::TransitType::Air,
            }
            .add_header_field(fields::Content::new(
                "serial",
                "1122",
                fields::ContentOptions {
                    label: String::from("SERIAL").into(),
                    ..Default::default()
                },
            ))
            .add_header_field(fields::Content::new(
                "number",
                "0011223344",
                fields::ContentOptions {
                    label: String::from("NUMBER").into(),
                    text_alignment: fields::TextAlignment::Right.into(),
                    ..Default::default()
                },
            ))
            .add_primary_field(fields::Content::new(
                "from",
                "UHWW",
                fields::ContentOptions {
                    label: String::from("FROM").into(),
                    text_alignment: fields::TextAlignment::Left.into(),
                    ..Default::default()
                },
            ))
            .add_primary_field(fields::Content::new(
                "to",
                "RKSI",
                fields::ContentOptions {
                    label: String::from("TO").into(),
                    text_alignment: fields::TextAlignment::Right.into(),
                    ..Default::default()
                },
            ))
            .add_auxiliary_field(fields::Content::new(
                "date_departure",
                "20.02.2024",
                fields::ContentOptions {
                    label: String::from("Departure date").into(),
                    ..Default::default()
                },
            )),
        )
        .build();
        let json = pass.make_json().unwrap();
        println!("{}", json);
        let json_expected = r#"{
  "formatVersion": 1,
  "organizationName": "Apple inc.",
  "description": "Example pass",
  "passTypeIdentifier": "com.example.pass",
  "teamIdentifier": "AA00AA0A0A",
  "serialNumber": "ABCDEFG1234567890",
  "groupingIdentifier": "com.example.pass.app",
  "foregroundColor": "rgb(250, 10, 10)",
  "backgroundColor": "rgb(255, 255, 255)",
  "logoText": "Test pass",
  "relevantDate": "2024-02-07T00:00:00+00:00",
  "expirationDate": "2024-02-08T00:00:00+00:00",
  "appLaunchURL": "testapp:param?index=1",
  "associatedStoreIdentifiers": [
    100
  ],
  "authenticationToken": "abcdefg01234567890abcdefg",
  "webServiceURL": "https://example.com/passes/",
  "suppressStripShine": false,
  "barcodes": [
    {
      "message": "Hello world!",
      "format": "PKBarcodeFormatQR",
      "altText": "test by test",
      "messageEncoding": "iso-8859-1"
    }
  ],
  "beacons": [
    {
      "proximityUUID": "e286373b-15b5-4f4e-bf91-e9e64787724a",
      "major": 2,
      "minor": 150,
      "relevantText": "The simple beacon"
    }
  ],
  "locations": [
    {
      "latitude": 37.334606,
      "longitude": -122.009102,
      "relevantText": "Apple Park, Cupertino, CA, USA"
    }
  ],
  "maxDistance": 1000,
  "nfc": {
    "encryptionPublicKey": "ABCDEFG_0011223344556677889900",
    "message": "test message",
    "requiresAuthentication": false
  },
  "semantics": {
    "airlineCode": "EX123",
    "departureLocation": {
      "latitude": 43.3948533,
      "longitude": 132.1451673
    }
  },
  "boardingPass": {
    "auxiliaryFields": [
      {
        "key": "date_departure",
        "value": "20.02.2024",
        "label": "Departure date"
      }
    ],
    "backFields": [],
    "headerFields": [
      {
        "key": "serial",
        "value": "1122",
        "label": "SERIAL"
      },
      {
        "key": "number",
        "value": "0011223344",
        "label": "NUMBER",
        "textAlignment": "PKTextAlignmentRight"
      }
    ],
    "primaryFields": [
      {
        "key": "from",
        "value": "UHWW",
        "label": "FROM",
        "textAlignment": "PKTextAlignmentLeft"
      },
      {
        "key": "to",
        "value": "RKSI",
        "label": "TO",
        "textAlignment": "PKTextAlignmentRight"
      }
    ],
    "secondaryFields": [],
    "transitType": "PKTransitTypeAir"
  }
}"#;
        assert_eq!(json_expected, json);
                let pass: Pass = Pass::from_json(json_expected).unwrap();
        let json = pass.make_json().unwrap();
        assert_eq!(json_expected, json);
    }
}
fn _is_false(b: &bool) -> bool {
    !b
}
fn _is_true(b: &bool) -> bool {
    *b
}
const fn _default_true() -> bool {
    true
}