//! Configuration file integration with intelligent merging

// use std::collections::HashMap; // TODO: For config mapping
use std::fs;
use std::path::Path;

use anyhow::Result;
use serde_json::Value;

use crate::commands::feature::FeatureManifest;

pub struct ConfigurationIntegrator<'a> {
    project_root: &'a Path,
    features_path: &'a Path,
}

impl<'a> ConfigurationIntegrator<'a> {
    pub fn new(project_root: &'a Path, features_path: &'a Path) -> Self {
        Self {
            project_root,
            features_path,
        }
    }

    pub fn integrate(&self, manifest: &FeatureManifest) -> Result<()> {
        println!("  ⚙️  Integrating configuration files...");

        if let Some(config) = &manifest.configuration {
            for file in &config.files {
                let dest_path = self.project_root.join(&file.path);

                // Create directory if it doesn't exist
                if let Some(parent) = dest_path.parent() {
                    fs::create_dir_all(parent)?;
                }

                if let Some(template) = &file.template {
                    let template_path = self
                        .features_path
                        .join(&manifest.feature.name)
                        .join(template);

                    if template_path.exists() {
                        if file.merge.unwrap_or(false) && dest_path.exists() {
                            // Intelligent merge based on file type
                            self.merge_config_file(&template_path, &dest_path)?;
                            println!("    ✓ Merged config: {}", file.path);
                        } else {
                            // Simple copy for new files
                            fs::copy(&template_path, &dest_path)?;
                            println!("    ✓ Installed config: {}", file.path);
                        }
                    } else {
                        println!("    ⚠️  Template not found: {}", template);
                    }
                }
            }

            println!("    ✅ Configuration files integrated successfully");
        } else {
            println!("    ↪ No configuration files in feature manifest");
        }

        Ok(())
    }

    fn merge_config_file(&self, template_path: &Path, dest_path: &Path) -> Result<()> {
        let extension = dest_path.extension().and_then(|s| s.to_str()).unwrap_or("");

        match extension {
            "toml" => self.merge_toml_files(template_path, dest_path),
            "json" => self.merge_json_files(template_path, dest_path),
            "yml" | "yaml" => self.merge_yaml_files(template_path, dest_path),
            _ => {
                // For unknown formats, append with comments
                self.append_with_comments(template_path, dest_path)
            }
        }
    }

    fn merge_toml_files(&self, template_path: &Path, dest_path: &Path) -> Result<()> {
        let template_content = fs::read_to_string(template_path)?;
        let dest_content = fs::read_to_string(dest_path)?;

        let template_data: toml::Value = toml::from_str(&template_content)?;
        let mut dest_data: toml::Value = toml::from_str(&dest_content)?;

        // Deep merge TOML values
        self.deep_merge_toml(&mut dest_data, &template_data);

        // Write merged result
        let merged_content = toml::to_string_pretty(&dest_data)?;
        fs::write(dest_path, merged_content)?;

        Ok(())
    }

    fn deep_merge_toml(&self, dest: &mut toml::Value, src: &toml::Value) {
        match (dest, src) {
            (toml::Value::Table(dest_table), toml::Value::Table(src_table)) => {
                for (key, value) in src_table {
                    if let Some(dest_value) = dest_table.get_mut(key) {
                        self.deep_merge_toml(dest_value, value);
                    } else {
                        dest_table.insert(key.clone(), value.clone());
                    }
                }
            }
            (dest_val, src_val) => {
                *dest_val = src_val.clone();
            }
        }
    }

    fn merge_json_files(&self, template_path: &Path, dest_path: &Path) -> Result<()> {
        let template_content = fs::read_to_string(template_path)?;
        let dest_content = fs::read_to_string(dest_path)?;

        let template_data: Value = serde_json::from_str(&template_content)?;
        let mut dest_data: Value = serde_json::from_str(&dest_content)?;

        // Deep merge JSON values
        self.deep_merge_json(&mut dest_data, &template_data);

        // Write merged result
        let merged_content = serde_json::to_string_pretty(&dest_data)?;
        fs::write(dest_path, merged_content)?;

        Ok(())
    }

    fn deep_merge_json(&self, dest: &mut Value, src: &Value) {
        match (dest, src) {
            (Value::Object(dest_obj), Value::Object(src_obj)) => {
                for (key, value) in src_obj {
                    if let Some(dest_value) = dest_obj.get_mut(key) {
                        self.deep_merge_json(dest_value, value);
                    } else {
                        dest_obj.insert(key.clone(), value.clone());
                    }
                }
            }
            (dest_val, src_val) => {
                *dest_val = src_val.clone();
            }
        }
    }

    fn merge_yaml_files(&self, template_path: &Path, dest_path: &Path) -> Result<()> {
        // YAML merging would use serde_yaml crate
        // For now, implement as append
        self.append_with_comments(template_path, dest_path)
    }

    fn append_with_comments(&self, template_path: &Path, dest_path: &Path) -> Result<()> {
        let template_content = fs::read_to_string(template_path)?;
        let dest_content = fs::read_to_string(dest_path)?;

        let merged_content = format!(
            "{}\n\n# Added by {} feature\n{}",
            dest_content.trim(),
            template_path
                .file_stem()
                .and_then(|s| s.to_str())
                .unwrap_or("unknown"),
            template_content
        );

        fs::write(dest_path, merged_content)?;
        Ok(())
    }
}
