use anyhow::Result;
use std::time::Instant;

use serde::{Deserialize, Serialize};

use crate::{doctor::Doctor, report::TestReport};

#[derive(Debug, Deserialize, Clone, Serialize)]
pub struct Host {
    url: String,
    name: String,
}

impl Host {
    pub fn new(url: String, name: String) -> Self {
        Self { url, name }
    }

    pub fn test_by(self, a_doctor: &impl Doctor) -> TestReport {
        let start = Instant::now();
        match a_doctor.test(self.url()) {
            Ok(test_result) => {
                TestReport::new(true, self, start.elapsed().as_millis(), test_result)
            }
            Err(err) => {
                TestReport::new(false, self, start.elapsed().as_millis(), format!("{}", err))
            }
        }
    }

    fn url(&self) -> &str {
        &self.url
    }

    pub fn name(&self) -> &str {
        &self.name
    }
}

#[derive(Debug, Deserialize, Clone)]
pub struct Hosts {
    #[serde(rename(deserialize = "hosts"))]
    items: Vec<Host>,
}

impl Hosts {
    pub fn all(&self) -> &Vec<Host> {
        &self.items
    }

    pub fn from(raw_data: &str) -> Result<Self> {
        Ok(serde_json::from_str(raw_data)?)
    }
}

impl Default for Hosts {
    fn default() -> Self {
        Self {
            items: vec![Host::new("https://google.com".into(), "Google".into())],
        }
    }
}
