/*
 * This file is part of CycloneDX Rust Cargo.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * SPDX-License-Identifier: Apache-2.0
 */

use crate::models::organization::{OrganizationalContact, OrganizationalEntity};
use crate::validation::{Validate, ValidationContext, ValidationResult};

use super::bom::SpecVersion;

/// Provides credits to organizations or individuals who contributed to a vulnerability.
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct VulnerabilityCredits {
    pub organizations: Option<Vec<OrganizationalEntity>>,
    pub individuals: Option<Vec<OrganizationalContact>>,
}

impl Validate for VulnerabilityCredits {
    fn validate_version(&self, version: SpecVersion) -> ValidationResult {
        ValidationContext::new()
            .add_list_option("organizations", self.organizations.as_ref(), |org| {
                org.validate_version(version)
            })
            .add_list_option("individuals", self.individuals.as_ref(), |individual| {
                individual.validate_version(version)
            })
            .into()
    }
}

#[cfg(test)]
mod test {
    use crate::{external_models::normalized_string::NormalizedString, validation};

    use super::*;
    use pretty_assertions::assert_eq;

    #[test]
    fn valid_vulnerability_credits_should_pass_validation() {
        let validation_result = VulnerabilityCredits {
            organizations: Some(vec![OrganizationalEntity::new("name")]),
            individuals: Some(vec![OrganizationalContact {
                bom_ref: None,
                name: Some(NormalizedString::new("name")),
                email: None,
                phone: None,
            }]),
        }
        .validate();

        assert!(validation_result.passed());
    }

    #[test]
    fn invalid_vulnerability_credits_should_fail_validation() {
        let validation_result = VulnerabilityCredits {
            organizations: Some(vec![OrganizationalEntity::new("invalid\tname")]),
            individuals: Some(vec![OrganizationalContact {
                bom_ref: None,
                name: Some(NormalizedString("invalid\tname".to_string())),
                email: None,
                phone: None,
            }]),
        }
        .validate();

        assert_eq!(
            validation_result,
            vec![
                validation::list(
                    "organizations",
                    [(
                        0,
                        validation::field(
                            "name",
                            "NormalizedString contains invalid characters \\r \\n \\t or \\r\\n"
                        )
                    )]
                ),
                validation::list(
                    "individuals",
                    [(
                        0,
                        validation::field(
                            "name",
                            "NormalizedString contains invalid characters \\r \\n \\t or \\r\\n"
                        )
                    )]
                )
            ]
            .into()
        );
    }
}
