// module to handle pre-boot params of the monitor
use anyhow::{Result, Ok, Error};
use clap::Parser;
use std::path::PathBuf;

#[derive(clap::ValueEnum, Debug, Clone)]
pub enum MetricsPrebootParams {
    Full, 
    System,
    Processes,
    Net,
    None,
}

impl std::fmt::Display for MetricsPrebootParams { 
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 
        match self {
            MetricsPrebootParams::Full => write!(f, "full"),
            MetricsPrebootParams::System => write!(f, "system"),
            MetricsPrebootParams::Processes => write!(f, "processes"),
            MetricsPrebootParams::Net => write!(f, "net"),
            MetricsPrebootParams::None => write!(f, "none"),
        }
    } 
} 

#[derive(Debug, Parser)]
pub struct PrebootParams {
    // actions
    #[arg(
        long = "no-hagent", 
        action, 
        conflicts_with="socket_path", 
        help="To disable work with host-agent"
    )]
    pub no_hostagent : bool,
    #[arg(
        long = "no-logs", 
        action, 
        conflicts_with="log_to", 
        help="To disable logs"
    )]
    pub no_logs: bool,
    #[arg(
        long = "refresh-logs", 
        action, 
        conflicts_with="no_logs", 
        help="To clear logs directory"
    )]
    pub refresh_logs : bool,
    #[arg(
        long = "no-remote-config", 
        action, 
        help="To disable work with remote config server", 
        conflicts_with="no_sub")]
    pub no_remote_config : bool,
    #[arg(
        long = "no-sub", 
        action, 
        help="To disable subscription mechanism", 
        conflicts_with="no_remote_config")]
    pub no_sub : bool,
    
    // params (socket_path, log_to, remote_server_url, config)
    #[arg(
        long = "socket-path", 
        default_value="/var/run/enode/hostagent.sock", 
        conflicts_with="no_hostagent", 
        help="To set .sock file's path used in communication with host-agent"
    )]
    pub socket_path : PathBuf,
    #[arg(
        long = "log-to", 
        default_value="./", 
        conflicts_with="no_logs", 
        help="To set a path to logs directory"
    )]
    pub log_to : PathBuf,
    #[arg(
        long = "remote-server-url", 
        default_value="localhost", 
        conflicts_with="no_remote_config", 
        help = "To set url of remote config server using in remote config pulling mechanism"
    )]
    pub remote_server_url : String,
    #[arg(
        long = "config", 
        short, 
        default_value="settings.json", 
        help="To set local config file path"
    )]
    pub config : PathBuf,

    // value enum params (metrics)
    #[arg(
        long = "metrics", 
        short, 
        default_value_t=MetricsPrebootParams::Full, 
        help="To set metrics grubbing mode"
    )]
    pub metrics: MetricsPrebootParams,
}

impl PrebootParams {
    pub fn validate(self) -> Result<Self> {
        if !self.socket_path.exists() {
            return Err(Error::msg("Socket-file not found or Noxis can't read it. Cannot start"));
        }
        // existing log dir
        if !self.log_to.exists() {
            return Err(Error::msg("Log Directory Not Found or Noxis can't read it. Cannot start"));
        }
        // existing sock file
        if !self.config.exists() {
            return Err(Error::msg("Local Config Not Found or Noxis can't read it. Cannot start"));
        }
        // redis server check
        Ok(self)
    }
}


// unit tests of preboot params parsing mech
#[cfg(test)]
mod preboot_unitests{
    use super::*;

    #[test]
    fn parsing_zero_args() {
        assert!(PrebootParams::try_parse_from(vec!["runner-rs"]).is_ok())    
    }
    #[test]
    fn parsing_hagent_valid_args() {
        assert!(PrebootParams::try_parse_from(vec![
            "runner-rs", 
            "--socket-path", "/path/to/socket"
        ]).is_ok())    
}
    #[test]
    fn parsing_hagent_invalid_args() {
        assert!(PrebootParams::try_parse_from(vec![
            "runner-rs", 
            "--socket-path", "/path/to/socket", 
            "--no-hagent"
        ]).is_err())    
    }
    #[test]
    fn parsing_log_valid_args() {
        assert!(PrebootParams::try_parse_from(vec![
            "runner-rs", 
            "--log-to", "/path/to/log/dir"
        ]).is_ok())    
    }
    #[test]
    fn parsing_log_invalid_args() {
        assert!(PrebootParams::try_parse_from(vec![
            "runner-rs", 
            "--log-to /path/to/log/dir", 
            "--no-logs"
        ]).is_err())
    }
    #[test]
    fn parsing_config_valid_args() {
        assert!(PrebootParams::try_parse_from(vec![
            "runner-rs", 
            "--no-sub", 
            "--remote-server-url", "redis://127.0.0.1"
        ]).is_ok())    
    }
    #[test]
    fn parsing_config_invalid_args_noremote_nosub() {
        assert!(PrebootParams::try_parse_from(vec![
            "runner-rs", 
            "--no-remote-config", "--no-sub"
        ]).is_err())    
    }
    #[test]
    fn parsing_config_invalid_args_noremote_remoteurl() {
        assert!(PrebootParams::try_parse_from(vec![
            "runner-rs", 
            "--no-remote-config", 
            "--remote-server-url", "redis://127.0.0.1"
        ]).is_err())    
    }
    #[test]
    fn parsing_metrics_args_using_value_enum() {
        assert!(PrebootParams::try_parse_from(vec!["runner-rs", "--metrics", "full"]).is_ok());
        assert!(PrebootParams::try_parse_from(vec!["runner-rs", "--metrics", "system"]).is_ok());
        assert!(PrebootParams::try_parse_from(vec!["runner-rs", "--metrics", "processes"]).is_ok());
        assert!(PrebootParams::try_parse_from(vec!["runner-rs", "--metrics", "net"]).is_ok());
        assert!(PrebootParams::try_parse_from(vec!["runner-rs", "--metrics", "none"]).is_ok());
        assert!(PrebootParams::try_parse_from(vec!["runner-rs", "--metrics", "unusual_value"]).is_err());
    }
}