// Unless explicitly stated otherwise all files in this repository are licensed under the Apache-2.0 License.
// This product includes software developed at Datadog (https://www.datadoghq.com/).
// Copyright 2019-Present Datadog, Inc.
use serde::de::{Error, MapAccess, Visitor};
use serde::{Deserialize, Deserializer, Serialize};
use serde_with::skip_serializing_none;
use std::fmt::{self, Formatter};

/// The JSON:API attributes of the vulnerability.
#[non_exhaustive]
#[skip_serializing_none]
#[derive(Clone, Debug, PartialEq, Serialize)]
pub struct VulnerabilityAttributes {
    /// Advisory associated with the vulnerability.
    #[serde(rename = "advisory")]
    pub advisory: Option<crate::datadogV2::model::VulnerabilityAdvisory>,
    /// Vulnerability advisory ID.
    #[serde(rename = "advisory_id")]
    pub advisory_id: Option<String>,
    /// Code vulnerability location.
    #[serde(rename = "code_location")]
    pub code_location: Option<crate::datadogV2::model::CodeLocation>,
    /// Vulnerability CVE list.
    #[serde(rename = "cve_list")]
    pub cve_list: Vec<String>,
    /// Vulnerability severities.
    #[serde(rename = "cvss")]
    pub cvss: crate::datadogV2::model::VulnerabilityCvss,
    /// Static library vulnerability location.
    #[serde(rename = "dependency_locations")]
    pub dependency_locations: Option<crate::datadogV2::model::VulnerabilityDependencyLocations>,
    /// Vulnerability description.
    #[serde(rename = "description")]
    pub description: String,
    /// The related vulnerability asset ecosystem.
    #[serde(rename = "ecosystem")]
    pub ecosystem: Option<crate::datadogV2::model::VulnerabilityEcosystem>,
    /// Vulnerability exposure time in seconds.
    #[serde(rename = "exposure_time")]
    pub exposure_time: i64,
    /// First detection of the vulnerability in [RFC 3339](<https://datatracker.ietf.org/doc/html/rfc3339>) format
    #[serde(rename = "first_detection")]
    pub first_detection: String,
    /// Whether the vulnerability has a remediation or not.
    #[serde(rename = "fix_available")]
    pub fix_available: bool,
    /// Vulnerability language.
    #[serde(rename = "language")]
    pub language: String,
    /// Last detection of the vulnerability in [RFC 3339](<https://datatracker.ietf.org/doc/html/rfc3339>) format
    #[serde(rename = "last_detection")]
    pub last_detection: String,
    /// Vulnerability library.
    #[serde(rename = "library")]
    pub library: Option<crate::datadogV2::model::Library>,
    /// Vulnerability origin.
    #[serde(rename = "origin")]
    pub origin: Vec<String>,
    /// List of remediations.
    #[serde(rename = "remediations")]
    pub remediations: Vec<crate::datadogV2::model::Remediation>,
    /// Vulnerability `repo_digest` list (when the vulnerability is related to `Image` asset).
    #[serde(rename = "repo_digests")]
    pub repo_digests: Option<Vec<String>>,
    /// Vulnerability risks.
    #[serde(rename = "risks")]
    pub risks: crate::datadogV2::model::VulnerabilityRisks,
    /// True if the vulnerability affects a package in the host’s running kernel, false if it affects a non-running kernel, and omit if it is not kernel-related.
    #[serde(rename = "running_kernel")]
    pub running_kernel: Option<bool>,
    /// The vulnerability status.
    #[serde(rename = "status")]
    pub status: crate::datadogV2::model::VulnerabilityStatus,
    /// Vulnerability title.
    #[serde(rename = "title")]
    pub title: String,
    /// The vulnerability tool.
    #[serde(rename = "tool")]
    pub tool: crate::datadogV2::model::VulnerabilityTool,
    /// The vulnerability type.
    #[serde(rename = "type")]
    pub type_: crate::datadogV2::model::VulnerabilityType,
    #[serde(flatten)]
    pub additional_properties: std::collections::BTreeMap<String, serde_json::Value>,
    #[serde(skip)]
    #[serde(default)]
    pub(crate) _unparsed: bool,
}

impl VulnerabilityAttributes {
    pub fn new(
        cve_list: Vec<String>,
        cvss: crate::datadogV2::model::VulnerabilityCvss,
        description: String,
        exposure_time: i64,
        first_detection: String,
        fix_available: bool,
        language: String,
        last_detection: String,
        origin: Vec<String>,
        remediations: Vec<crate::datadogV2::model::Remediation>,
        risks: crate::datadogV2::model::VulnerabilityRisks,
        status: crate::datadogV2::model::VulnerabilityStatus,
        title: String,
        tool: crate::datadogV2::model::VulnerabilityTool,
        type_: crate::datadogV2::model::VulnerabilityType,
    ) -> VulnerabilityAttributes {
        VulnerabilityAttributes {
            advisory: None,
            advisory_id: None,
            code_location: None,
            cve_list,
            cvss,
            dependency_locations: None,
            description,
            ecosystem: None,
            exposure_time,
            first_detection,
            fix_available,
            language,
            last_detection,
            library: None,
            origin,
            remediations,
            repo_digests: None,
            risks,
            running_kernel: None,
            status,
            title,
            tool,
            type_,
            additional_properties: std::collections::BTreeMap::new(),
            _unparsed: false,
        }
    }

    pub fn advisory(mut self, value: crate::datadogV2::model::VulnerabilityAdvisory) -> Self {
        self.advisory = Some(value);
        self
    }

    pub fn advisory_id(mut self, value: String) -> Self {
        self.advisory_id = Some(value);
        self
    }

    pub fn code_location(mut self, value: crate::datadogV2::model::CodeLocation) -> Self {
        self.code_location = Some(value);
        self
    }

    pub fn dependency_locations(
        mut self,
        value: crate::datadogV2::model::VulnerabilityDependencyLocations,
    ) -> Self {
        self.dependency_locations = Some(value);
        self
    }

    pub fn ecosystem(mut self, value: crate::datadogV2::model::VulnerabilityEcosystem) -> Self {
        self.ecosystem = Some(value);
        self
    }

    pub fn library(mut self, value: crate::datadogV2::model::Library) -> Self {
        self.library = Some(value);
        self
    }

    pub fn repo_digests(mut self, value: Vec<String>) -> Self {
        self.repo_digests = Some(value);
        self
    }

    pub fn running_kernel(mut self, value: bool) -> Self {
        self.running_kernel = Some(value);
        self
    }

    pub fn additional_properties(
        mut self,
        value: std::collections::BTreeMap<String, serde_json::Value>,
    ) -> Self {
        self.additional_properties = value;
        self
    }
}

impl<'de> Deserialize<'de> for VulnerabilityAttributes {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        struct VulnerabilityAttributesVisitor;
        impl<'a> Visitor<'a> for VulnerabilityAttributesVisitor {
            type Value = VulnerabilityAttributes;

            fn expecting(&self, f: &mut Formatter<'_>) -> fmt::Result {
                f.write_str("a mapping")
            }

            fn visit_map<M>(self, mut map: M) -> Result<Self::Value, M::Error>
            where
                M: MapAccess<'a>,
            {
                let mut advisory: Option<crate::datadogV2::model::VulnerabilityAdvisory> = None;
                let mut advisory_id: Option<String> = None;
                let mut code_location: Option<crate::datadogV2::model::CodeLocation> = None;
                let mut cve_list: Option<Vec<String>> = None;
                let mut cvss: Option<crate::datadogV2::model::VulnerabilityCvss> = None;
                let mut dependency_locations: Option<
                    crate::datadogV2::model::VulnerabilityDependencyLocations,
                > = None;
                let mut description: Option<String> = None;
                let mut ecosystem: Option<crate::datadogV2::model::VulnerabilityEcosystem> = None;
                let mut exposure_time: Option<i64> = None;
                let mut first_detection: Option<String> = None;
                let mut fix_available: Option<bool> = None;
                let mut language: Option<String> = None;
                let mut last_detection: Option<String> = None;
                let mut library: Option<crate::datadogV2::model::Library> = None;
                let mut origin: Option<Vec<String>> = None;
                let mut remediations: Option<Vec<crate::datadogV2::model::Remediation>> = None;
                let mut repo_digests: Option<Vec<String>> = None;
                let mut risks: Option<crate::datadogV2::model::VulnerabilityRisks> = None;
                let mut running_kernel: Option<bool> = None;
                let mut status: Option<crate::datadogV2::model::VulnerabilityStatus> = None;
                let mut title: Option<String> = None;
                let mut tool: Option<crate::datadogV2::model::VulnerabilityTool> = None;
                let mut type_: Option<crate::datadogV2::model::VulnerabilityType> = None;
                let mut additional_properties: std::collections::BTreeMap<
                    String,
                    serde_json::Value,
                > = std::collections::BTreeMap::new();
                let mut _unparsed = false;

                while let Some((k, v)) = map.next_entry::<String, serde_json::Value>()? {
                    match k.as_str() {
                        "advisory" => {
                            if v.is_null() {
                                continue;
                            }
                            advisory = Some(serde_json::from_value(v).map_err(M::Error::custom)?);
                        }
                        "advisory_id" => {
                            if v.is_null() {
                                continue;
                            }
                            advisory_id =
                                Some(serde_json::from_value(v).map_err(M::Error::custom)?);
                        }
                        "code_location" => {
                            if v.is_null() {
                                continue;
                            }
                            code_location =
                                Some(serde_json::from_value(v).map_err(M::Error::custom)?);
                        }
                        "cve_list" => {
                            cve_list = Some(serde_json::from_value(v).map_err(M::Error::custom)?);
                        }
                        "cvss" => {
                            cvss = Some(serde_json::from_value(v).map_err(M::Error::custom)?);
                        }
                        "dependency_locations" => {
                            if v.is_null() {
                                continue;
                            }
                            dependency_locations =
                                Some(serde_json::from_value(v).map_err(M::Error::custom)?);
                        }
                        "description" => {
                            description =
                                Some(serde_json::from_value(v).map_err(M::Error::custom)?);
                        }
                        "ecosystem" => {
                            if v.is_null() {
                                continue;
                            }
                            ecosystem = Some(serde_json::from_value(v).map_err(M::Error::custom)?);
                            if let Some(ref _ecosystem) = ecosystem {
                                match _ecosystem {
                                    crate::datadogV2::model::VulnerabilityEcosystem::UnparsedObject(_ecosystem) => {
                                        _unparsed = true;
                                    },
                                    _ => {}
                                }
                            }
                        }
                        "exposure_time" => {
                            exposure_time =
                                Some(serde_json::from_value(v).map_err(M::Error::custom)?);
                        }
                        "first_detection" => {
                            first_detection =
                                Some(serde_json::from_value(v).map_err(M::Error::custom)?);
                        }
                        "fix_available" => {
                            fix_available =
                                Some(serde_json::from_value(v).map_err(M::Error::custom)?);
                        }
                        "language" => {
                            language = Some(serde_json::from_value(v).map_err(M::Error::custom)?);
                        }
                        "last_detection" => {
                            last_detection =
                                Some(serde_json::from_value(v).map_err(M::Error::custom)?);
                        }
                        "library" => {
                            if v.is_null() {
                                continue;
                            }
                            library = Some(serde_json::from_value(v).map_err(M::Error::custom)?);
                        }
                        "origin" => {
                            origin = Some(serde_json::from_value(v).map_err(M::Error::custom)?);
                        }
                        "remediations" => {
                            remediations =
                                Some(serde_json::from_value(v).map_err(M::Error::custom)?);
                        }
                        "repo_digests" => {
                            if v.is_null() {
                                continue;
                            }
                            repo_digests =
                                Some(serde_json::from_value(v).map_err(M::Error::custom)?);
                        }
                        "risks" => {
                            risks = Some(serde_json::from_value(v).map_err(M::Error::custom)?);
                        }
                        "running_kernel" => {
                            if v.is_null() {
                                continue;
                            }
                            running_kernel =
                                Some(serde_json::from_value(v).map_err(M::Error::custom)?);
                        }
                        "status" => {
                            status = Some(serde_json::from_value(v).map_err(M::Error::custom)?);
                            if let Some(ref _status) = status {
                                match _status {
                                    crate::datadogV2::model::VulnerabilityStatus::UnparsedObject(_status) => {
                                        _unparsed = true;
                                    },
                                    _ => {}
                                }
                            }
                        }
                        "title" => {
                            title = Some(serde_json::from_value(v).map_err(M::Error::custom)?);
                        }
                        "tool" => {
                            tool = Some(serde_json::from_value(v).map_err(M::Error::custom)?);
                            if let Some(ref _tool) = tool {
                                match _tool {
                                    crate::datadogV2::model::VulnerabilityTool::UnparsedObject(
                                        _tool,
                                    ) => {
                                        _unparsed = true;
                                    }
                                    _ => {}
                                }
                            }
                        }
                        "type" => {
                            type_ = Some(serde_json::from_value(v).map_err(M::Error::custom)?);
                            if let Some(ref _type_) = type_ {
                                match _type_ {
                                    crate::datadogV2::model::VulnerabilityType::UnparsedObject(
                                        _type_,
                                    ) => {
                                        _unparsed = true;
                                    }
                                    _ => {}
                                }
                            }
                        }
                        &_ => {
                            if let Ok(value) = serde_json::from_value(v.clone()) {
                                additional_properties.insert(k, value);
                            }
                        }
                    }
                }
                let cve_list = cve_list.ok_or_else(|| M::Error::missing_field("cve_list"))?;
                let cvss = cvss.ok_or_else(|| M::Error::missing_field("cvss"))?;
                let description =
                    description.ok_or_else(|| M::Error::missing_field("description"))?;
                let exposure_time =
                    exposure_time.ok_or_else(|| M::Error::missing_field("exposure_time"))?;
                let first_detection =
                    first_detection.ok_or_else(|| M::Error::missing_field("first_detection"))?;
                let fix_available =
                    fix_available.ok_or_else(|| M::Error::missing_field("fix_available"))?;
                let language = language.ok_or_else(|| M::Error::missing_field("language"))?;
                let last_detection =
                    last_detection.ok_or_else(|| M::Error::missing_field("last_detection"))?;
                let origin = origin.ok_or_else(|| M::Error::missing_field("origin"))?;
                let remediations =
                    remediations.ok_or_else(|| M::Error::missing_field("remediations"))?;
                let risks = risks.ok_or_else(|| M::Error::missing_field("risks"))?;
                let status = status.ok_or_else(|| M::Error::missing_field("status"))?;
                let title = title.ok_or_else(|| M::Error::missing_field("title"))?;
                let tool = tool.ok_or_else(|| M::Error::missing_field("tool"))?;
                let type_ = type_.ok_or_else(|| M::Error::missing_field("type_"))?;

                let content = VulnerabilityAttributes {
                    advisory,
                    advisory_id,
                    code_location,
                    cve_list,
                    cvss,
                    dependency_locations,
                    description,
                    ecosystem,
                    exposure_time,
                    first_detection,
                    fix_available,
                    language,
                    last_detection,
                    library,
                    origin,
                    remediations,
                    repo_digests,
                    risks,
                    running_kernel,
                    status,
                    title,
                    tool,
                    type_,
                    additional_properties,
                    _unparsed,
                };

                Ok(content)
            }
        }

        deserializer.deserialize_any(VulnerabilityAttributesVisitor)
    }
}
