use crate::cve::Cve;
use crate::error::{Error, RequestResult};
use serde::Deserialize;
use std::convert::TryFrom;
use tokio::time::Duration;

#[derive(Debug, Clone)]
pub struct RedHat {
    http_client: reqwest::Client,
}

impl RedHat {
    pub fn new() -> Self {
        RedHat {
            http_client: reqwest::Client::new(),
        }
    }

    pub async fn cve_by_id(&self, id: &str) -> RequestResult<RedHatCve> {
        let url = format!("https://access.redhat.com/labs/securitydataapi/cve/{}.json", id);
        let resp = self
            .http_client
            .get(&url)
            .timeout(Duration::from_secs(5))
            .send()
            .await
            .map_err(|e| Error::HttpClientError {
                why: "call to RedHat failed",
                source: e,
            })?;

        if !resp.status().is_success() {
            return Err(Error::RequestFailed {
                id: id.to_string(),
                status_code: resp.status(),
            });
        }

        let redhat = resp.json::<RedHatCve>().await.map_err(|e| Error::ParserError {
            what: "HTTP body".to_string(),
            to: "RedHat",
            why: e.to_string(),
        })?;

        Ok(redhat)
    }
}

impl Default for RedHat {
    fn default() -> Self {
        RedHat::new()
    }
}

#[derive(Debug, Deserialize)]
pub struct RedHatCve {
    pub name: String,
    pub cvss3: Cvss3,
}

#[derive(Debug, Deserialize)]
#[serde(rename = "cvss3")]
pub struct Cvss3 {
    #[serde(rename = "cvss3_base_score")]
    pub base_score: String,
    #[serde(rename = "cvss3_scoring_vector")]
    pub scoring_vector: String,
}

impl TryFrom<RedHatCve> for Cve {
    type Error = Error;

    fn try_from(redhat: RedHatCve) -> Result<Self, Self::Error> {
        Cve::from_cve_str(redhat.name, redhat.cvss3.scoring_vector)
    }
}

#[cfg(test)]
mod tests {
    use crate::source::redhat::RedHatCve;
    use spectral::prelude::*;
    use std::path::PathBuf;

    #[test]
    fn parse_response() {
        let mut json_file = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
        json_file.push("tests/data/redhat-CVE-2020-14803.json");
        let json = std::fs::read_to_string(json_file).expect("Failed to read json file");

        let rhcve: Result<RedHatCve, _> = serde_json::from_str(&json);

        asserting("json has been read successfully").that(&rhcve).is_ok();
    }
}
