//! Layered Override System
//!
//! This module implements the layered override system that allows configuration
//! and component resolution with clear precedence: Local > Feature > Template >
//! Framework

use std::collections::HashMap;
use std::fs;
use std::path::{Path, PathBuf};

use anyhow::{Context, Result};
use serde::{Deserialize, Serialize};
use tracing::{debug, info, warn};

/// Trait for types that can be merged in the layered system
pub trait Mergeable {
    /// Merge another instance into this one, with the other taking precedence
    fn merge(&mut self, other: Self);

    /// Create a default instance
    fn default() -> Self;
}

/// Configuration layer in the override hierarchy
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ConfigLayer {
    /// Local project overrides (highest precedence)
    Local(PathBuf),
    /// Feature-specific configurations
    Feature(String, PathBuf),
    /// Template defaults
    Template(PathBuf),
    /// Framework defaults (lowest precedence)
    Framework,
}

impl ConfigLayer {
    /// Get the precedence value (higher = higher precedence)
    pub fn precedence(&self) -> u8 {
        match self {
            ConfigLayer::Local(_) => 100,
            ConfigLayer::Feature(_, _) => 75,
            ConfigLayer::Template(_) => 50,
            ConfigLayer::Framework => 25,
        }
    }

    /// Get a human-readable description
    pub fn description(&self) -> String {
        match self {
            ConfigLayer::Local(path) => format!("Local override ({})", path.display()),
            ConfigLayer::Feature(name, path) => format!("Feature '{}' ({})", name, path.display()),
            ConfigLayer::Template(path) => format!("Template default ({})", path.display()),
            ConfigLayer::Framework => "Framework default".to_string(),
        }
    }
}

/// Layered configuration resolver
#[derive(Debug)]
pub struct LayeredConfig {
    /// Project root directory
    project_root: PathBuf,
    /// Ordered layers (from lowest to highest precedence)
    layers: Vec<ConfigLayer>,
    /// Cache for resolved configurations
    cache: HashMap<String, Box<dyn std::any::Any + Send + Sync>>,
}

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

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

    /// Discover all available configuration layers
    fn discover_layers(&mut self) -> Result<()> {
        self.layers.clear();

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

        // Template layer
        let template_dir = self.project_root.join("config/templates");
        if template_dir.exists() {
            self.layers.push(ConfigLayer::Template(template_dir));
        }

        // Feature layers (discovered from filesystem)
        let features_dir = self.project_root.join("config/features");
        if features_dir.exists() {
            for entry in fs::read_dir(&features_dir)? {
                let entry = entry?;
                if entry.file_type()?.is_dir() {
                    if let Some(feature_name) = entry.file_name().to_str() {
                        self.layers
                            .push(ConfigLayer::Feature(feature_name.to_string(), entry.path()));
                    }
                }
            }
        }

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

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

        info!("Discovered {} configuration layers", self.layers.len());
        for layer in &self.layers {
            debug!("  {}", layer.description());
        }

        Ok(())
    }

    /// Resolve a configuration file through the layer hierarchy
    pub fn resolve<T>(&mut self, config_name: &str) -> Result<T>
    where
        T: for<'de> Deserialize<'de> + Mergeable + Clone + Send + Sync + 'static,
    {
        // Check cache first
        let cache_key = format!("{}::{}", std::any::type_name::<T>(), config_name);
        if let Some(cached) = self.cache.get(&cache_key) {
            if let Some(result) = cached.downcast_ref::<T>() {
                return Ok(result.clone());
            }
        }

        let mut result = T::default();
        let mut layers_found = Vec::new();

        // Apply layers from lowest to highest precedence
        for layer in &self.layers {
            if let Some(config) = self.load_config_from_layer::<T>(layer, config_name)? {
                result.merge(config);
                layers_found.push(layer.clone());
            }
        }

        info!(
            "Resolved '{}' from {} layers",
            config_name,
            layers_found.len()
        );
        for layer in &layers_found {
            debug!("  Applied: {}", layer.description());
        }

        // Cache the result
        self.cache.insert(cache_key, Box::new(result.clone()));

        Ok(result)
    }

    /// Load configuration from a specific layer
    fn load_config_from_layer<T>(&self, layer: &ConfigLayer, config_name: &str) -> Result<Option<T>>
    where
        T: for<'de> Deserialize<'de>,
    {
        let config_path = match layer {
            ConfigLayer::Local(path) => path.join(config_name),
            ConfigLayer::Feature(_, path) => path.join(config_name),
            ConfigLayer::Template(path) => path.join(config_name),
            ConfigLayer::Framework => {
                // Framework defaults would come from embedded resources or defaults
                return Ok(None);
            }
        };

        if !config_path.exists() {
            return Ok(None);
        }

        let content = fs::read_to_string(&config_path)
            .with_context(|| format!("Failed to read config file: {}", config_path.display()))?;

        let config: T = if config_path.extension().and_then(|s| s.to_str()) == Some("toml") {
            toml::from_str(&content).with_context(|| {
                format!("Failed to parse TOML config: {}", config_path.display())
            })?
        } else if config_path.extension().and_then(|s| s.to_str()) == Some("json") {
            serde_json::from_str(&content).with_context(|| {
                format!("Failed to parse JSON config: {}", config_path.display())
            })?
        } else {
            return Err(anyhow::anyhow!(
                "Unsupported config file format: {}",
                config_path.display()
            ));
        };

        debug!(
            "Loaded config '{}' from {}",
            config_name,
            layer.description()
        );
        Ok(Some(config))
    }

    /// Get a trace of how a configuration is resolved
    pub fn trace_resolution(&self, config_name: &str) -> Vec<LayerTrace> {
        let mut trace = Vec::new();

        for layer in &self.layers {
            let config_path = match layer {
                ConfigLayer::Local(path) => path.join(config_name),
                ConfigLayer::Feature(_, path) => path.join(config_name),
                ConfigLayer::Template(path) => path.join(config_name),
                ConfigLayer::Framework => PathBuf::from("framework_defaults"),
            };

            let exists = match layer {
                ConfigLayer::Framework => true, // Always available conceptually
                _ => config_path.exists(),
            };

            trace.push(LayerTrace {
                layer: layer.clone(),
                path: config_path,
                exists,
                used: exists, // In this simple implementation, all existing configs are used
            });
        }

        trace
    }

    /// Clear the configuration cache
    pub fn clear_cache(&mut self) {
        self.cache.clear();
    }

    /// Get all discovered layers
    pub fn get_layers(&self) -> &[ConfigLayer] {
        &self.layers
    }
}

/// Information about how a layer was resolved
#[derive(Debug, Clone)]
pub struct LayerTrace {
    pub layer: ConfigLayer,
    pub path: PathBuf,
    pub exists: bool,
    pub used: bool,
}

impl LayerTrace {
    pub fn status_icon(&self) -> &'static str {
        match (self.exists, self.used) {
            (true, true) => "✅",
            (true, false) => "🔸",
            (false, _) => "❌",
        }
    }
}

/// Component resolver using layered system
#[derive(Debug)]
pub struct LayeredComponents {
    project_root: PathBuf,
    component_dirs: Vec<ComponentDir>,
}

#[derive(Debug, Clone)]
pub enum ComponentDir {
    Local(PathBuf),
    Feature(String, PathBuf),
    Template(PathBuf),
    Framework,
}

impl ComponentDir {
    pub fn precedence(&self) -> u8 {
        match self {
            ComponentDir::Local(_) => 100,
            ComponentDir::Feature(_, _) => 75,
            ComponentDir::Template(_) => 50,
            ComponentDir::Framework => 25,
        }
    }
}

impl LayeredComponents {
    /// Create a new layered component resolver
    pub fn new(project_root: PathBuf) -> Result<Self> {
        let mut resolver = Self {
            project_root: project_root.clone(),
            component_dirs: Vec::new(),
        };

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

    /// Discover all component directories
    fn discover_component_dirs(&mut self) -> Result<()> {
        self.component_dirs.clear();

        // Framework components (always available)
        self.component_dirs.push(ComponentDir::Framework);

        // Template components
        let template_dir = self.project_root.join("src/components/templates");
        if template_dir.exists() {
            self.component_dirs
                .push(ComponentDir::Template(template_dir));
        }

        // Feature components
        let features_dir = self.project_root.join("src/components/features");
        if features_dir.exists() {
            for entry in fs::read_dir(&features_dir)? {
                let entry = entry?;
                if entry.file_type()?.is_dir() {
                    if let Some(feature_name) = entry.file_name().to_str() {
                        self.component_dirs.push(ComponentDir::Feature(
                            feature_name.to_string(),
                            entry.path(),
                        ));
                    }
                }
            }
        }

        // Local components (highest precedence)
        let local_dir = self.project_root.join("src/components/local");
        if local_dir.exists() {
            self.component_dirs.push(ComponentDir::Local(local_dir));
        }

        // Sort by precedence (highest first for components - first found wins)
        self.component_dirs
            .sort_by_key(|dir| std::cmp::Reverse(dir.precedence()));

        Ok(())
    }

    /// Find a component through the layer hierarchy
    pub fn find_component(&self, name: &str) -> Option<PathBuf> {
        for dir in &self.component_dirs {
            if let Some(path) = self.search_component_in_dir(dir, name) {
                debug!("Found component '{}' in {:?}", name, dir);
                return Some(path);
            }
        }

        warn!("Component '{}' not found in any layer", name);
        None
    }

    /// Search for a component in a specific directory
    fn search_component_in_dir(&self, dir: &ComponentDir, name: &str) -> Option<PathBuf> {
        let search_path = match dir {
            ComponentDir::Local(path) => path.clone(),
            ComponentDir::Feature(_, path) => path.clone(),
            ComponentDir::Template(path) => path.clone(),
            ComponentDir::Framework => return None, // Framework components handled separately
        };

        // Try common Rust component file patterns
        let patterns = [
            format!("{}.rs", name),
            format!("{}/mod.rs", name),
            format!("{}/index.rs", name),
        ];

        for pattern in &patterns {
            let component_path = search_path.join(pattern);
            if component_path.exists() {
                return Some(component_path);
            }
        }

        None
    }

    /// Get trace information for component resolution
    pub fn trace_component(&self, name: &str) -> Vec<ComponentTrace> {
        let mut trace = Vec::new();

        for dir in &self.component_dirs {
            let found_path = self.search_component_in_dir(dir, name);

            trace.push(ComponentTrace {
                dir: dir.clone(),
                found_path: found_path.clone(),
                used: found_path.is_some(),
            });
        }

        trace
    }
}

/// Information about component resolution
#[derive(Debug, Clone)]
pub struct ComponentTrace {
    pub dir: ComponentDir,
    pub found_path: Option<PathBuf>,
    pub used: bool,
}

impl ComponentTrace {
    pub fn status_icon(&self) -> &'static str {
        if self.used {
            "✅"
        } else if self.found_path.is_some() {
            "🔸"
        } else {
            "❌"
        }
    }
}
