//! Centralized configuration management for all platform services
//!
//! The ConfigManager handles loading, validating, and caching configurations
//! for all platform services. It provides fail-fast validation on startup
//! to prevent cascading crashes.

use std::collections::HashMap;
use std::sync::RwLock;

use anyhow::Result;
use serde_json::Value;
use tracing::{debug, error, info};

/// Central config manager for all platform services
///
/// This struct manages loading and validating configs for all platform
/// services. It caches loaded configs to avoid repeated parsing and provides
/// fail-fast validation on startup.
pub struct ConfigManager {
    /// Cache of loaded configs (JSON representation)
    /// Key format: "{service_name}.{mode}" (e.g., "orchestrator.solo")
    cache: RwLock<HashMap<String, Value>>,

    /// Current deployment mode
    mode: String,
}

impl ConfigManager {
    /// Creates a new ConfigManager with the current deployment mode
    pub fn new() -> Self {
        let mode = std::env::var("PROVISIONING_MODE").unwrap_or_else(|_| "solo".to_string());

        Self {
            cache: RwLock::new(HashMap::new()),
            mode,
        }
    }

    /// Creates a ConfigManager with a specific mode
    pub fn with_mode<S: Into<String>>(mode: S) -> Self {
        Self {
            cache: RwLock::new(HashMap::new()),
            mode: mode.into(),
        }
    }

    /// Loads a service config as JSON (generic, without type information)
    ///
    /// This is useful for validation without knowing the concrete type.
    pub fn load_as_json(&self, service_name: &str) -> Result<Value> {
        let cache_key = format!("{}.{}", service_name, self.mode);

        // Check cache first
        {
            let cache_read = self.cache.read().unwrap();
            if let Some(cached) = cache_read.get(&cache_key) {
                debug!("Config cache hit for {}", cache_key);
                return Ok(cached.clone());
            }
        }

        // Cache miss - load from disk
        debug!("Config cache miss for {} - loading from disk", cache_key);

        let path =
            platform_config::find_config_file(service_name, &self.mode).ok_or_else(|| {
                anyhow::anyhow!("Config file not found for {}.{}", service_name, self.mode)
            })?;

        let json_value: Value = platform_config::format::load_config(&path)
            .map_err(|e| anyhow::anyhow!("Failed to load config for {}: {}", service_name, e))?;

        // Store in cache
        {
            let mut cache_write = self.cache.write().unwrap();
            cache_write.insert(cache_key.clone(), json_value.clone());
        }

        Ok(json_value)
    }

    /// Validates that ALL known platform services have valid configs
    ///
    /// This is the main validation entry point for pre-flight checks.
    /// Returns Ok if all configs are valid, Err with detailed failure info
    /// otherwise.
    pub fn validate_all_services(&self) -> Result<()> {
        let services = vec![
            "orchestrator",
            "control-center",
            "mcp-server",
            "ai-service",
            "extension-registry",
            "rag",
            "vault-service",
            "provisioning-daemon",
        ];

        info!(
            "Validating configs for {} services (mode: {})",
            services.len(),
            self.mode
        );

        let mut errors = Vec::new();

        for service in services {
            match self.load_as_json(service) {
                Ok(_) => {
                    debug!("✅ {}.{} config valid", service, self.mode);
                }
                Err(e) => {
                    let error_msg = format!("❌ {}.{} config invalid: {}", service, self.mode, e);
                    error!("{}", error_msg);
                    errors.push(error_msg);
                }
            }
        }

        if !errors.is_empty() {
            let summary = format!(
                "Config validation failed for {} services:\n{}",
                errors.len(),
                errors.join("\n")
            );
            error!("{}", summary);
            return Err(anyhow::anyhow!("{}", summary));
        }

        info!("✅ All service configs validated successfully");
        Ok(())
    }

    /// Validates a subset of services
    pub fn validate_services(&self, services: &[&str]) -> Result<()> {
        debug!(
            "Validating {} specific services (mode: {})",
            services.len(),
            self.mode
        );

        let mut errors = Vec::new();

        for service in services {
            match self.load_as_json(service) {
                Ok(_) => {
                    debug!("✅ {}.{} config valid", service, self.mode);
                }
                Err(e) => {
                    errors.push(format!("❌ {}: {}", service, e));
                }
            }
        }

        if !errors.is_empty() {
            let summary = errors.join("\n");
            error!("{}", summary);
            return Err(anyhow::anyhow!("Validation failed:\n{}", summary));
        }

        Ok(())
    }

    /// Clears the entire config cache
    pub fn clear_cache(&self) {
        let mut cache = self.cache.write().unwrap();
        cache.clear();
        debug!("Config cache cleared");
    }

    /// Invalidates cache for a specific service
    pub fn invalidate_service(&self, service_name: &str) {
        let cache_key = format!("{}.{}", service_name, self.mode);
        let mut cache = self.cache.write().unwrap();
        cache.remove(&cache_key);
        debug!("Invalidated cache for {}", cache_key);
    }

    /// Gets the current deployment mode
    pub fn mode(&self) -> &str {
        &self.mode
    }

    /// Gets the number of cached configs
    pub fn cache_size(&self) -> usize {
        self.cache.read().unwrap().len()
    }
}

impl Default for ConfigManager {
    fn default() -> Self {
        Self::new()
    }
}

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

    #[test]
    fn test_config_manager_creation() {
        let mgr = ConfigManager::new();
        assert!(!mgr.mode().is_empty());
    }

    #[test]
    fn test_config_manager_with_mode() {
        let mgr = ConfigManager::with_mode("test");
        assert_eq!(mgr.mode(), "test");
    }

    #[test]
    fn test_cache_operations() {
        let mgr = ConfigManager::new();
        assert_eq!(mgr.cache_size(), 0);

        // Clear doesn't fail even when empty
        mgr.clear_cache();
        assert_eq!(mgr.cache_size(), 0);
    }
}
