use url::{Url, ParseError};
use reqwest::StatusCode;
use std::fmt;
use chrono::Duration;
use colored::{Colorize, ColoredString};

/// Represents a target domain and metadata collected by the checker.
///
/// A `Domain` holds the canonical URL to check along with information
/// discovered during the population phase (HTTP status, certificate
/// time-to-expiry, and brand identifier extracted from HTML).
#[derive(Debug)]
pub struct Domain {
    /// Fully qualified URL (including scheme and host).
    pub domain: Url,
    /// Last observed HTTP status code from the server response, if available.
    pub status: Option<StatusCode>,
    /// Certificate time-to-expiry in seconds, if a server certificate was
    /// obtained and parsed.
    pub tte: Option<i64>,
    /// Brand identifier inferred from HTML (e.g., from stylesheet bundles), if
    /// detected.
    pub brand: Option<String>
}

impl Domain {
    /// Create a new `Domain` from a URL string.
    ///
    /// If the input is missing a URL scheme, `https://` is automatically
    /// prefixed before parsing. Returns an error if the resulting string is not
    /// a valid URL.
    ///
    /// Examples
    /// ```rust
    /// use domain_checker_lite::Domain;
    /// // Adds https:// when missing
    /// let d = Domain::new("example.com").unwrap();
    /// assert_eq!(d.domain.scheme(), "https");
    /// assert_eq!(d.domain.host_str(), Some("example.com"));
    ///
    /// // Accepts fully-qualified URLs
    /// let d2 = Domain::new("https://example.com").unwrap();
    /// assert_eq!(d2.domain.host_str(), Some("example.com"));
    /// ```
    pub fn new(in_domain: &str) -> Result<Domain, ParseError> {
        let out_domain: String;
        if !in_domain.starts_with("http") {
            // Adds the protocol if it isn't populated
            out_domain = format!("https://{}", in_domain);
        } else {
            out_domain = String::from(in_domain);
        }

        Ok(Domain {
            domain: Url::parse(out_domain.as_str())?,
            status: None,
            tte: None,
            brand: None
        })
    }
}

// Implements Display formatting for use in printing result
/// Display formatting prints a single-line summary with colored fields:
///
/// - `scheme://host` in default color
/// - `status` in green for success (2xx) or red otherwise
/// - `brand` in green when present, or `???` in red when unknown
/// - `TTE(days)` colored by threshold: <15 red, <30 yellow, else green
impl fmt::Display for Domain {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {

        let domain: ColoredString;
        let status: ColoredString;
        let brand: ColoredString;
        let tte: ColoredString;

        // If we don't have a usable value, this is default, in red
        let unknown: ColoredString = String::from("???").red();

        // If we don't have a usable status, set it as SERVICE_UNAVAILABLE
        let status_get: StatusCode = self.status.unwrap_or(StatusCode::SERVICE_UNAVAILABLE);

        // 
        // domain = self.domain.host_str().unwrap_or(&unknown).into();
        domain = format!("{}://{}", self.domain.scheme(), self.domain.host_str().unwrap_or(&unknown)).into();

        if status_get.is_success() {
            status = status_get.as_u16().to_string().green();
        } else {
            status = status_get.as_u16().to_string().red();
        }

        match self.brand.as_ref() {
            Some(value) => brand = value.as_str().green(),
            None => brand = unknown.clone()
        }

        tte=match self.tte {
            Some(s) => {
                let duration = Duration::seconds(s);

                if duration.num_days() < 15 {
                    duration.num_days().to_string().red()
                } else if duration.num_days() < 30 {
                    duration.num_days().to_string().yellow()
                } else {
                    duration.num_days().to_string().green()
                }
            },
            None => unknown.clone()
        };
        
        write!(f, "{domain} => status: {status}, brand: {brand}, TTE(days): {tte}")
    }
}