//! Provisioning Daemon configuration wrapper
//!
//! Loads configuration from provisioning-daemon.ncl using platform-config crate
//! This module handles loading the service operational config (port, polling,
//! workers) and converting it to the format needed by daemon-cli.

use std::net::SocketAddr;

use serde::{Deserialize, Serialize};

/// Service operational configuration loaded from provisioning-daemon.ncl
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ProvisioningDaemonConfig {
    pub server: ServerConfig,
    #[serde(default)]
    pub daemon: DaemonConfig,
    #[serde(default)]
    pub logging: LoggingConfig,
    #[serde(default)]
    pub actions: ActionsConfig,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ServerConfig {
    #[serde(default = "default_host")]
    pub host: String,
    pub port: u16,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DaemonConfig {
    #[serde(default)]
    pub enabled: bool,
    #[serde(default = "default_poll_interval")]
    pub poll_interval: u64,
    #[serde(default = "default_max_workers")]
    pub max_workers: usize,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LoggingConfig {
    #[serde(default = "default_log_level")]
    pub level: String,
    pub file: Option<String>,
}

#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct ActionsConfig {
    #[serde(default)]
    pub auto_cleanup: bool,
    #[serde(default)]
    pub auto_update: bool,
}

fn default_host() -> String {
    "0.0.0.0".to_string()
}

fn default_poll_interval() -> u64 {
    60
}

fn default_max_workers() -> usize {
    2
}

fn default_log_level() -> String {
    "info".to_string()
}

impl ProvisioningDaemonConfig {
    /// Load configuration from provisioning-daemon.ncl via platform-config
    pub fn load() -> Result<Self, Box<dyn std::error::Error>> {
        let config_json = platform_config::load_service_config_from_ncl("nu-daemon")?;

        let config_value = config_json
            .get("nu_daemon")
            .cloned()
            .unwrap_or(config_json);

        let config: ProvisioningDaemonConfig = serde_json::from_value(config_value)?;
        Ok(config)
    }

    /// Get the bind address for the HTTP server
    #[allow(dead_code)]
    pub fn bind_addr(&self) -> SocketAddr {
        format!("{}:{}", self.server.host, self.server.port)
            .parse()
            .unwrap_or_else(|_| ([127, 0, 0, 1], 9090).into())
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_config_defaults() {
        let config = ProvisioningDaemonConfig {
            server: ServerConfig {
                host: "0.0.0.0".to_string(),
                port: 9095,
            },
            daemon: DaemonConfig {
                enabled: true,
                poll_interval: 60,
                max_workers: 2,
            },
            logging: LoggingConfig {
                level: "info".to_string(),
                file: None,
            },
            actions: ActionsConfig {
                auto_cleanup: false,
                auto_update: false,
            },
        };

        assert_eq!(config.server.port, 9095);
        assert!(config.daemon.enabled);
    }

    #[test]
    fn test_bind_addr() {
        let config = ProvisioningDaemonConfig {
            server: ServerConfig {
                host: "127.0.0.1".to_string(),
                port: 9095,
            },
            daemon: Default::default(),
            logging: Default::default(),
            actions: Default::default(),
        };

        let addr = config.bind_addr();
        assert_eq!(addr.port(), 9095);
    }
}

impl Default for DaemonConfig {
    fn default() -> Self {
        Self {
            enabled: true,
            poll_interval: default_poll_interval(),
            max_workers: default_max_workers(),
        }
    }
}

impl Default for LoggingConfig {
    fn default() -> Self {
        Self {
            level: default_log_level(),
            file: None,
        }
    }
}
