//! Configuration Resolver Template Implementation
//!
//! This module demonstrates how to implement configuration resolution traits
//! from core-types in your Rustelo applications.
//!
//! # Usage
//!
//! ```rust
//! use crate::config_resolver::*;
//! use std::path::PathBuf;
//!
//! // Basic usage with default resolver
//! let mut resolver = MyConfigResolver::new(PathBuf::from("."))?;
//! let config = resolver.resolve()?;
//!
//! // Build-time usage (in build.rs)
//! let config = MyConfigResolver::resolve_for_build(&project_root)?;
//! MyConfigResolver::generate_build_artifacts(&config, &out_dir)?;
//! ```

use rustelo_core_types::traits_config::*;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::path::{Path, PathBuf};
use std::fs;
use std::error::Error as StdError;
use std::fmt;

/// Example application configuration structure
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MyAppConfig {
    /// Server configuration
    pub server: ServerConfig,
    /// Content management settings
    pub content: ContentConfig,
    /// Feature flags
    pub features: HashMap<String, bool>,
    /// Application metadata
    pub app: AppMetadata,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ServerConfig {
    pub host: String,
    pub port: u16,
    pub debug: bool,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ContentConfig {
    pub root_path: PathBuf,
    pub public_path: PathBuf,
    pub cache_enabled: bool,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AppMetadata {
    pub name: String,
    pub version: String,
    pub environment: String,
}

impl Default for MyAppConfig {
    fn default() -> Self {
        Self {
            server: ServerConfig {
                host: "127.0.0.1".to_string(),
                port: 3030,
                debug: true,
            },
            content: ContentConfig {
                root_path: PathBuf::from("content"),
                public_path: PathBuf::from("public"),
                cache_enabled: true,
            },
            features: HashMap::new(),
            app: AppMetadata {
                name: "My Rustelo App".to_string(),
                version: "0.1.0".to_string(),
                environment: "development".to_string(),
            },
        }
    }
}

/// Custom error type for configuration resolution
#[derive(Debug)]
pub enum MyConfigError {
    FileNotFound(PathBuf),
    ParseError(String),
    ValidationError(String),
    IoError(std::io::Error),
}

impl fmt::Display for MyConfigError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            MyConfigError::FileNotFound(path) => write!(f, "Config file not found: {}", path.display()),
            MyConfigError::ParseError(msg) => write!(f, "Config parse error: {}", msg),
            MyConfigError::ValidationError(msg) => write!(f, "Config validation error: {}", msg),
            MyConfigError::IoError(err) => write!(f, "Config I/O error: {}", err),
        }
    }
}

impl StdError for MyConfigError {}

/// Example configuration resolver implementation
pub struct MyConfigResolver {
    project_root: PathBuf,
    layers: Vec<ConfigLayer>,
    cache: HashMap<String, MyAppConfig>,
}

impl MyConfigResolver {
    /// Create a new configuration resolver
    pub fn new(project_root: PathBuf) -> Result<Self, MyConfigError> {
        let mut resolver = Self {
            project_root: project_root.clone(),
            layers: Vec::new(),
            cache: HashMap::new(),
        };

        resolver.discover_layers()?;
        Ok(resolver)
    }

    /// Discover configuration layers from the project structure
    fn discover_layers(&mut self) -> Result<(), MyConfigError> {
        self.layers.clear();

        // Framework defaults (always present)
        self.layers.push(ConfigLayer::Framework);

        // Template defaults
        let template_config = self.project_root.join("config/template.toml");
        if template_config.exists() {
            self.layers.push(ConfigLayer::Template(template_config));
        }

        // Environment-specific config
        let env = std::env::var("RUSTELO_ENV").unwrap_or_else(|_| "development".to_string());
        let env_config = self.project_root.join(format!("config/{}.toml", env));
        if env_config.exists() {
            self.layers.push(ConfigLayer::Environment(env.clone(), env_config));
        }

        // Feature-specific configs
        let features_dir = self.project_root.join("config/features");
        if features_dir.exists() {
            if let Ok(entries) = fs::read_dir(&features_dir) {
                for entry in entries {
                    if let Ok(entry) = entry {
                        if entry.path().extension() == Some("toml".as_ref()) {
                            if let Some(feature_name) = entry.path().file_stem().and_then(|n| n.to_str()) {
                                self.layers.push(ConfigLayer::Feature(
                                    feature_name.to_string(),
                                    entry.path(),
                                ));
                            }
                        }
                    }
                }
            }
        }

        // Local overrides (highest precedence)
        let local_config = self.project_root.join("config/local.toml");
        if local_config.exists() {
            self.layers.push(ConfigLayer::Local(local_config));
        }

        // Sort by precedence
        self.layers.sort_by_key(|layer| layer.precedence());

        Ok(())
    }

    /// Load configuration from a specific file
    fn load_config_file(&self, path: &Path) -> Result<MyAppConfig, MyConfigError> {
        let content = fs::read_to_string(path)
            .map_err(|e| MyConfigError::IoError(e))?;

        let config: MyAppConfig = toml::from_str(&content)
            .map_err(|e| MyConfigError::ParseError(e.to_string()))?;

        Ok(config)
    }
}

// Implement the core ConfigResolver trait
impl ConfigResolver for MyConfigResolver {
    type Config = MyAppConfig;
    type Error = MyConfigError;

    fn resolve(&mut self) -> Result<Self::Config, Self::Error> {
        // Start with default configuration
        let mut result = MyAppConfig::default();

        // Apply layers in precedence order
        for layer in &self.layers {
            match layer {
                ConfigLayer::Framework => {
                    // Framework defaults already applied via Default implementation
                }
                ConfigLayer::Template(path) |
                ConfigLayer::Environment(_, path) |
                ConfigLayer::Feature(_, path) |
                ConfigLayer::Local(path) => {
                    if path.exists() {
                        let layer_config = self.load_config_file(path)?;
                        result.merge(layer_config);
                    }
                }
            }
        }

        // Apply environment variable overrides
        if let Ok(host) = std::env::var("SERVER_HOST") {
            result.server.host = host;
        }
        if let Ok(port) = std::env::var("SERVER_PORT") {
            if let Ok(port_num) = port.parse::<u16>() {
                result.server.port = port_num;
            }
        }

        // Cache the result
        self.cache.insert("resolved".to_string(), result.clone());

        Ok(result)
    }

    fn resolve_section<T>(&mut self, section: &str) -> Result<T, Self::Error>
    where
        T: for<'de> Deserialize<'de> + Clone + Send + Sync + 'static,
    {
        let config = self.resolve()?;

        // This is a simplified implementation - in practice, you'd want to
        // extract the specific section from the configuration
        match section {
            "server" => {
                // Serialize and deserialize to convert types
                let json = serde_json::to_string(&config.server)
                    .map_err(|e| MyConfigError::ParseError(e.to_string()))?;
                let section_config: T = serde_json::from_str(&json)
                    .map_err(|e| MyConfigError::ParseError(e.to_string()))?;
                Ok(section_config)
            }
            _ => Err(MyConfigError::ValidationError(format!("Unknown section: {}", section))),
        }
    }

    fn has_changed(&self) -> bool {
        // In a real implementation, you'd check file modification times
        false
    }

    fn clear_cache(&mut self) {
        self.cache.clear();
    }
}

// Implement LayeredResolver for hierarchical configuration
impl LayeredResolver for MyConfigResolver {
    fn add_layer(&mut self, layer: ConfigLayer) -> Result<(), Self::Error> {
        self.layers.push(layer);
        self.layers.sort_by_key(|l| l.precedence());
        self.clear_cache();
        Ok(())
    }

    fn remove_layer(&mut self, layer_id: &str) -> Result<(), Self::Error> {
        let initial_len = self.layers.len();
        self.layers.retain(|layer| layer.id() != layer_id);

        if self.layers.len() == initial_len {
            return Err(MyConfigError::ValidationError(
                format!("Layer not found: {}", layer_id)
            ));
        }

        self.clear_cache();
        Ok(())
    }

    fn refresh_layers(&mut self) -> Result<(), Self::Error> {
        self.discover_layers()?;
        self.clear_cache();
        Ok(())
    }

    fn get_layers(&self) -> &[ConfigLayer] {
        &self.layers
    }

    fn resolve_layer<T>(&mut self, layer_id: &str) -> Result<Option<T>, Self::Error>
    where
        T: for<'de> Deserialize<'de> + Clone + Send + Sync + 'static,
    {
        for layer in &self.layers {
            if layer.id() == layer_id {
                match layer {
                    ConfigLayer::Framework => {
                        // Return default config for framework layer
                        let default_config = MyAppConfig::default();
                        let json = serde_json::to_string(&default_config)
                            .map_err(|e| MyConfigError::ParseError(e.to_string()))?;
                        let result: T = serde_json::from_str(&json)
                            .map_err(|e| MyConfigError::ParseError(e.to_string()))?;
                        return Ok(Some(result));
                    }
                    ConfigLayer::Template(path) |
                    ConfigLayer::Environment(_, path) |
                    ConfigLayer::Feature(_, path) |
                    ConfigLayer::Local(path) => {
                        if path.exists() {
                            let config = self.load_config_file(path)?;
                            let json = serde_json::to_string(&config)
                                .map_err(|e| MyConfigError::ParseError(e.to_string()))?;
                            let result: T = serde_json::from_str(&json)
                                .map_err(|e| MyConfigError::ParseError(e.to_string()))?;
                            return Ok(Some(result));
                        }
                    }
                }
            }
        }

        Ok(None)
    }
}

// Implement WASM constant generation
impl WasmConstantGenerator for MyConfigResolver {
    type Config = MyAppConfig;

    fn generate_constants(&self, config: &Self::Config) -> WasmConstants {
        let mut constants = WasmConstants::new();

        // Server constants
        constants.add_string("SERVER_HOST", &config.server.host);
        constants.add_numeric("SERVER_PORT", config.server.port as i64);
        constants.add_boolean("SERVER_DEBUG", config.server.debug);

        // Content constants
        constants.add_path("CONTENT_ROOT_PATH", config.content.root_path.clone());
        constants.add_path("CONTENT_PUBLIC_PATH", config.content.public_path.clone());
        constants.add_boolean("CONTENT_CACHE_ENABLED", config.content.cache_enabled);

        // App constants
        constants.add_string("APP_NAME", &config.app.name);
        constants.add_string("APP_VERSION", &config.app.version);
        constants.add_string("APP_ENVIRONMENT", &config.app.environment);

        constants
    }

    fn write_constants_file(
        &self,
        constants: &WasmConstants,
        out_path: &Path,
    ) -> Result<(), Box<dyn StdError>> {
        let code = constants.generate_rust_code();
        let getters = self.generate_getters(constants);

        let full_content = format!(
            "{}\n// Convenience getter functions\n{}\n",
            code,
            getters
        );

        fs::write(out_path, full_content)?;
        Ok(())
    }
}

// Implement build-time resolution
impl BuildTimeResolver for MyConfigResolver {
    type Config = MyAppConfig;
    type Error = MyConfigError;

    fn resolve_for_build(project_root: &Path) -> Result<Self::Config, Self::Error> {
        let mut resolver = Self::new(project_root.to_path_buf())?;
        resolver.resolve()
    }

    fn generate_build_artifacts(
        config: &Self::Config,
        out_dir: &Path,
    ) -> Result<(), Box<dyn StdError>> {
        // Generate WASM constants
        let resolver = Self::new(PathBuf::from("."))?;
        let constants = resolver.generate_constants(config);
        let constants_file = out_dir.join("config_constants.rs");
        resolver.write_constants_file(&constants, &constants_file)?;

        // Generate configuration documentation
        let config_json = serde_json::to_string_pretty(config)?;
        let docs_file = out_dir.join("config_schema.json");
        fs::write(&docs_file, config_json)?;

        println!("cargo:warning=Generated config constants: {}", constants_file.display());
        println!("cargo:warning=Generated config schema: {}", docs_file.display());

        Ok(())
    }

    fn get_watch_files(project_root: &Path) -> Vec<PathBuf> {
        vec![
            project_root.join("config"),
            project_root.join("config.toml"),
            project_root.join("config/development.toml"),
            project_root.join("config/production.toml"),
            project_root.join("config/local.toml"),
            project_root.join(".env"),
        ]
    }
}

// Implement configuration merging
impl Mergeable for MyAppConfig {
    fn merge(&mut self, other: Self) {
        // Merge server config
        if other.server.host != ServerConfig::default().host {
            self.server.host = other.server.host;
        }
        if other.server.port != ServerConfig::default().port {
            self.server.port = other.server.port;
        }
        if other.server.debug != ServerConfig::default().debug {
            self.server.debug = other.server.debug;
        }

        // Merge content config
        if other.content.root_path != ContentConfig::default().root_path {
            self.content.root_path = other.content.root_path;
        }
        if other.content.public_path != ContentConfig::default().public_path {
            self.content.public_path = other.content.public_path;
        }
        if other.content.cache_enabled != ContentConfig::default().cache_enabled {
            self.content.cache_enabled = other.content.cache_enabled;
        }

        // Merge features (other takes precedence)
        for (key, value) in other.features {
            self.features.insert(key, value);
        }

        // Merge app metadata
        if other.app.name != AppMetadata::default().name {
            self.app.name = other.app.name;
        }
        if other.app.version != AppMetadata::default().version {
            self.app.version = other.app.version;
        }
        if other.app.environment != AppMetadata::default().environment {
            self.app.environment = other.app.environment;
        }
    }

    fn is_empty(&self) -> bool {
        *self == MyAppConfig::default()
    }
}

impl Default for ServerConfig {
    fn default() -> Self {
        Self {
            host: "127.0.0.1".to_string(),
            port: 3030,
            debug: true,
        }
    }
}

impl Default for ContentConfig {
    fn default() -> Self {
        Self {
            root_path: PathBuf::from("content"),
            public_path: PathBuf::from("public"),
            cache_enabled: true,
        }
    }
}

impl Default for AppMetadata {
    fn default() -> Self {
        Self {
            name: "My Rustelo App".to_string(),
            version: "0.1.0".to_string(),
            environment: "development".to_string(),
        }
    }
}

// Implement configuration validation
impl Validatable for MyAppConfig {
    type Error = MyConfigError;

    fn validate(&self) -> Result<(), Self::Error> {
        // Validate server config
        if self.server.host.is_empty() {
            return Err(MyConfigError::ValidationError("Server host cannot be empty".to_string()));
        }

        if self.server.port == 0 {
            return Err(MyConfigError::ValidationError("Server port must be greater than 0".to_string()));
        }

        // Validate paths exist
        if !self.content.root_path.exists() {
            return Err(MyConfigError::ValidationError(
                format!("Content root path does not exist: {}", self.content.root_path.display())
            ));
        }

        // Validate app metadata
        if self.app.name.is_empty() {
            return Err(MyConfigError::ValidationError("App name cannot be empty".to_string()));
        }

        Ok(())
    }

    fn warnings(&self) -> Vec<String> {
        let mut warnings = Vec::new();

        if self.server.debug && self.app.environment == "production" {
            warnings.push("Debug mode is enabled in production environment".to_string());
        }

        if !self.content.cache_enabled {
            warnings.push("Content caching is disabled - performance may be impacted".to_string());
        }

        warnings
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::fs;
    use tempfile::tempdir;

    #[test]
    fn test_config_resolver_basic() {
        let temp_dir = tempdir().unwrap();
        let mut resolver = MyConfigResolver::new(temp_dir.path().to_path_buf()).unwrap();
        let config = resolver.resolve().unwrap();

        assert_eq!(config.server.host, "127.0.0.1");
        assert_eq!(config.server.port, 3030);
    }

    #[test]
    fn test_config_layered_resolution() {
        let temp_dir = tempdir().unwrap();
        let config_dir = temp_dir.path().join("config");
        fs::create_dir_all(&config_dir).unwrap();

        // Create a development config file
        let dev_config = r#"
[server]
host = "localhost"
port = 8080
debug = true

[app]
environment = "development"
"#;

        fs::write(config_dir.join("development.toml"), dev_config).unwrap();

        std::env::set_var("RUSTELO_ENV", "development");

        let mut resolver = MyConfigResolver::new(temp_dir.path().to_path_buf()).unwrap();
        let config = resolver.resolve().unwrap();

        assert_eq!(config.server.host, "localhost");
        assert_eq!(config.server.port, 8080);
        assert_eq!(config.app.environment, "development");
    }

    #[test]
    fn test_wasm_constants_generation() {
        let config = MyAppConfig::default();
        let resolver = MyConfigResolver::new(PathBuf::from(".")).unwrap();
        let constants = resolver.generate_constants(&config);

        assert!(constants.string_constants.contains_key("SERVER_HOST"));
        assert!(constants.numeric_constants.contains_key("SERVER_PORT"));
        assert!(constants.boolean_constants.contains_key("SERVER_DEBUG"));
    }

    #[test]
    fn test_config_validation() {
        let mut config = MyAppConfig::default();
        assert!(config.validate().is_ok());

        // Test validation failure
        config.server.host = String::new();
        assert!(config.validate().is_err());
    }

    #[test]
    fn test_config_merging() {
        let mut base_config = MyAppConfig::default();
        let mut override_config = MyAppConfig::default();
        override_config.server.port = 9000;
        override_config.app.environment = "production".to_string();

        base_config.merge(override_config);

        assert_eq!(base_config.server.port, 9000);
        assert_eq!(base_config.app.environment, "production");
        assert_eq!(base_config.server.host, "127.0.0.1"); // Should remain unchanged
    }
}
