use serde::Deserialize;
use std::path::Path;

#[derive(Deserialize, Debug, Clone, Default)]
pub struct Config {
    pub server: ServerConfig,
    pub protocols: ProtocolsConfig,
    #[serde(default)]
    pub metrics: MetricsConfig,
}

#[derive(Deserialize, Debug, Clone, Default)]
pub struct ServerConfig {
    pub ip: String,
    pub port: u16,
}

#[derive(Deserialize, Debug, Clone, Default)]
pub struct ProtocolsConfig {
    #[serde(default)]
    pub udp: UdpConfig,
    #[serde(default)]
    pub mqtt: MqttConfig,
}

#[derive(Deserialize, Debug, Clone, Default)]
pub struct UdpConfig {
    #[serde(default)]
    pub enabled: bool,
}

#[derive(Deserialize, Debug, Clone)]
pub struct MqttConfig {
    #[serde(default)]
    pub enabled: bool,
    #[serde(default = "default_mqtt_host")]
    pub host: String,
    #[serde(default = "default_mqtt_port")]
    pub port: u16,
    #[serde(default = "default_topic_base")]
    pub topic_base: String,
    #[serde(default = "default_true")]
    pub auto_discovery: bool,
    #[serde(default = "default_true")]
    pub birth_message: bool,
    #[serde(default = "default_true")]
    pub last_will: bool,
}

impl Default for MqttConfig {
    fn default() -> Self {
        MqttConfig {
            enabled: false,
            host: default_mqtt_host(),
            port: default_mqtt_port(),
            topic_base: default_topic_base(),
            auto_discovery: true,
            birth_message: true,
            last_will: true,
        }
    }
}

fn default_mqtt_host() -> String {
    "10.0.0.3".to_string()
}
fn default_mqtt_port() -> u16 {
    1883
}
fn default_topic_base() -> String {
    "nanometrics/agents".to_string()
}
fn default_true() -> bool {
    true
}

#[derive(Deserialize, Debug, Clone, Default)]
pub struct MetricsConfig {
    #[serde(default)]
    pub cpu: MetricProto,
    #[serde(default)]
    pub memory: MetricProto,
    #[serde(default)]
    pub disk: MetricProto,
    #[serde(default)]
    pub network: MetricProto,
    #[serde(default)]
    pub uptime: MetricProto,
    #[serde(default)]
    pub temperature: MetricProto,
    #[serde(default)]
    pub smart: MetricProto,
}

#[derive(Deserialize, Debug, Clone, Default)]
pub struct MetricProto {
    #[serde(default)]
    pub udp: bool,
    #[serde(default)]
    pub mqtt: bool,
}

pub fn load(path: &Path) -> Result<Config, Box<dyn std::error::Error>> {
    let content = std::fs::read_to_string(path)?;
    let config: Config = toml::from_str(&content)?;
    Ok(config)
}
