use std::path::PathBuf;

#[cfg(test)]
use serde_json::json;
use serde_json::Value;

use crate::error::{ConfigError, Result};
use crate::format;

/// Deployment mode configuration loaded from deployment-mode.ncl
#[derive(Debug, Clone)]
pub struct DeploymentConfig {
    /// The full deployment configuration as JSON
    pub config: Value,
    /// Path to the deployment-mode.ncl file that was loaded
    pub source_path: PathBuf,
}

impl DeploymentConfig {
    /// Returns the service-specific configuration section
    pub fn get_service_config(&self, service_name: &str) -> Result<Value> {
        self.config.get(service_name).cloned().ok_or_else(|| {
            ConfigError::validation_failed(format!(
                "Service '{}' not found in deployment config",
                service_name
            ))
        })
    }

    /// Returns the enabled_services list by reading services.X.enabled
    pub fn enabled_services(&self) -> Result<Vec<String>> {
        self.config
            .get("services")
            .and_then(|v| v.as_object())
            .map(|services| {
                services
                    .iter()
                    .filter_map(|(name, config)| {
                        if config
                            .get("enabled")
                            .and_then(|v| v.as_bool())
                            .unwrap_or(false)
                        {
                            Some(name.clone())
                        } else {
                            None
                        }
                    })
                    .collect()
            })
            .ok_or_else(|| {
                ConfigError::validation_failed("services not found or not a record".to_string())
            })
    }

    /// Checks if a service is enabled
    pub fn is_service_enabled(&self, service_name: &str) -> Result<bool> {
        Ok(self.enabled_services()?.iter().any(|s| s == service_name))
    }

    /// Pretty-prints deployment configuration details
    pub fn display_info(&self) -> String {
        let mut info = String::new();
        info.push_str("📋 Deployment Configuration\n");
        info.push_str("─────────────────────────────────\n");

        if let Ok(services) = self.enabled_services() {
            info.push_str("Services:\n");
            for service in services {
                info.push_str(&format!("  • {}\n", service));
            }
        }

        info.push_str(&format!("Source: {}\n", self.source_path.display()));
        info
    }
}

/// Loads deployment-mode configuration from standard locations
///
/// Searches in this order:
/// 1. DEPLOYMENT_MODE_CONFIG env var (explicit path)
/// 2. Platform-specific user location:
///    - macOS: ~/Library/Application
///      Support/provisioning/platform/deployment-mode.ncl
///    - Linux: ~/.config/provisioning/platform/deployment-mode.ncl
/// 3. /etc/provisioning/platform/deployment-mode.ncl (system location)
/// 4. provisioning/platform/deployment-mode.ncl (relative to cwd)
pub fn load_deployment_mode() -> Result<DeploymentConfig> {
    let path = find_deployment_mode_path()?;
    tracing::info!("Loading deployment config from: {:?}", path);

    let config = format::load_config(&path)?;

    Ok(DeploymentConfig {
        config,
        source_path: path,
    })
}

/// Finds the deployment-mode.ncl file location
fn find_deployment_mode_path() -> Result<PathBuf> {
    // Priority 1: Explicit env var
    if let Ok(path) = std::env::var("DEPLOYMENT_MODE_CONFIG") {
        let config_path = PathBuf::from(&path);
        if config_path.exists() {
            tracing::debug!("Using deployment config from env var: {:?}", config_path);
            return Ok(config_path);
        }
        return Err(ConfigError::not_found(&path));
    }

    // Priority 2: Platform-specific user config directory
    if let Ok(home) = std::env::var("HOME") {
        let user_path = {
            #[cfg(target_os = "macos")]
            {
                PathBuf::from(home)
                    .join("Library/Application Support/provisioning/platform/deployment-mode.ncl")
            }
            #[cfg(not(target_os = "macos"))]
            {
                PathBuf::from(home).join(".config/provisioning/platform/deployment-mode.ncl")
            }
        };

        if user_path.exists() {
            tracing::debug!("Using deployment config from user home: {:?}", user_path);
            return Ok(user_path);
        }
    }

    // Priority 3: System config directory
    let system_path = PathBuf::from("/etc/provisioning/platform/deployment-mode.ncl");
    if system_path.exists() {
        tracing::debug!("Using deployment config from system: {:?}", system_path);
        return Ok(system_path);
    }

    // Priority 4: Relative to cwd
    let cwd_path = PathBuf::from("provisioning/platform/deployment-mode.ncl");
    if cwd_path.exists() {
        tracing::debug!("Using deployment config from cwd: {:?}", cwd_path);
        return Ok(cwd_path);
    }

    Err(ConfigError::not_found(
        "deployment-mode.ncl not found in standard locations",
    ))
}

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

    #[test]
    fn test_deployment_config_display() {
        let config = DeploymentConfig {
            config: json!({
                "enabled_services": ["orchestrator", "vault_service"],
                "orchestrator": { "enabled": true },
                "vault_service": { "enabled": true }
            }),
            source_path: PathBuf::from("/test/deployment-mode.ncl"),
        };

        let display = config.display_info();
        assert!(display.contains("Deployment Configuration"));
        assert!(display.contains("orchestrator"));
        assert!(display.contains("vault_service"));
    }

    #[test]
    fn test_get_service_config() {
        let config = DeploymentConfig {
            config: json!({
                "orchestrator": { "port": 9090 },
                "vault_service": { "port": 8082 }
            }),
            source_path: PathBuf::from("/test/deployment-mode.ncl"),
        };

        let orch = config.get_service_config("orchestrator");
        assert!(orch.is_ok());
        assert_eq!(orch.unwrap()["port"], 9090);
    }

    #[test]
    fn test_enabled_services() {
        let config = DeploymentConfig {
            config: json!({
                "enabled_services": ["orchestrator", "vault_service"],
            }),
            source_path: PathBuf::from("/test/deployment-mode.ncl"),
        };

        let services = config.enabled_services().unwrap();
        assert_eq!(services.len(), 2);
        assert!(services.contains(&"orchestrator".to_string()));
    }

    #[test]
    fn test_is_service_enabled() {
        let config = DeploymentConfig {
            config: json!({
                "enabled_services": ["orchestrator"],
            }),
            source_path: PathBuf::from("/test/deployment-mode.ncl"),
        };

        assert!(config.is_service_enabled("orchestrator").unwrap());
        assert!(!config.is_service_enabled("vault_service").unwrap());
    }
}
