//! Feature management commands for Rustelo CLI

use std::fs;
use std::path::PathBuf;

use anyhow::{anyhow, Result};
use serde::{Deserialize, Serialize};
use toml;

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FeatureManifest {
    pub feature: FeatureInfo,
    pub dependencies: FeatureDependencies,
    pub environment: Option<EnvironmentConfig>,
    pub configuration: Option<ConfigurationFiles>,
    pub resources: Option<ResourceFiles>,
    pub scripts: Option<Vec<ScriptFile>>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FeatureInfo {
    pub name: String,
    pub version: String,
    pub source: String,
    pub description: String,
    pub requires: Option<Vec<String>>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FeatureDependencies {
    pub workspace: Option<Vec<String>>,
    pub external: Option<Vec<String>>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EnvironmentConfig {
    pub variables: Vec<EnvVariable>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EnvVariable {
    pub name: String,
    pub default: String,
    pub required: bool,
    pub secret: Option<bool>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ConfigurationFiles {
    pub files: Vec<ConfigFile>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ConfigFile {
    pub path: String,
    pub template: Option<String>,
    pub merge: Option<bool>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ResourceFiles {
    pub public: Option<Vec<ResourceFile>>,
    pub site: Option<SiteResources>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ResourceFile {
    pub from: String,
    pub to: String,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SiteResources {
    pub content: Option<Vec<ResourceFile>>,
    pub i18n: Option<Vec<ResourceFile>>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ScriptFile {
    pub from: String,
    pub to: String,
}

#[derive(Clone)]
pub struct FeatureManager {
    pub features_path: PathBuf,
    pub registry_path: PathBuf,
    #[allow(dead_code)]
    pub project_root: PathBuf,
}

impl FeatureManager {
    pub fn new(project_root: impl Into<PathBuf>) -> Result<Self> {
        let project_root = project_root.into();
        let features_path = project_root.join("features");
        let registry_path = project_root.join("registry");

        Ok(Self {
            features_path,
            registry_path,
            project_root,
        })
    }

    pub fn list_available_features(&self) -> Result<Vec<String>> {
        let registry_file = self.registry_path.join("features.toml");
        if !registry_file.exists() {
            return Ok(vec![]);
        }

        let content = fs::read_to_string(&registry_file)?;
        let registry: toml::Value = toml::from_str(&content)?;

        let mut features = Vec::new();
        if let Some(features_table) = registry.get("features").and_then(|v| v.as_table()) {
            for (name, _) in features_table {
                features.push(name.clone());
            }
        }

        Ok(features)
    }

    pub fn load_feature_manifest(&self, feature_name: &str) -> Result<FeatureManifest> {
        let manifest_path = self.features_path.join(feature_name).join("feature.toml");

        if !manifest_path.exists() {
            return Err(anyhow!(
                "Feature '{}' not found at {}",
                feature_name,
                manifest_path.display()
            ));
        }

        let content = fs::read_to_string(&manifest_path)?;
        let manifest: FeatureManifest = toml::from_str(&content)?;

        Ok(manifest)
    }

    #[allow(dead_code)]
    pub fn add_feature(&self, feature_name: &str, _force: bool) -> Result<()> {
        println!("🔧 Adding feature: {}", feature_name);

        // Load feature manifest
        let manifest = self.load_feature_manifest(feature_name)?;

        // Install dependencies
        self.install_dependencies(&manifest)?;

        // Install environment variables
        self.install_environment(&manifest)?;

        // Install configuration files
        self.install_configuration(&manifest)?;

        // Install resources
        self.install_resources(&manifest)?;

        // Install scripts
        self.install_scripts(&manifest)?;

        println!("✅ Feature '{}' installed successfully", feature_name);
        Ok(())
    }

    #[allow(dead_code)]
    fn install_dependencies(&self, _manifest: &FeatureManifest) -> Result<()> {
        println!("  📦 Installing dependencies...");

        // Update Cargo.toml with new dependencies
        let cargo_toml = self.project_root.join("Cargo.toml");
        if cargo_toml.exists() {
            // Load, modify, and save Cargo.toml
            // Implementation would parse and update dependencies
            println!("    ✓ Dependencies updated in Cargo.toml");
        }

        Ok(())
    }

    #[allow(dead_code)]
    fn install_environment(&self, manifest: &FeatureManifest) -> Result<()> {
        if let Some(env_config) = &manifest.environment {
            println!("  🔧 Installing environment configuration...");

            let env_file = self.project_root.join(".env");
            let mut env_content = if env_file.exists() {
                fs::read_to_string(&env_file)?
            } else {
                String::new()
            };

            // Add feature environment variables
            env_content.push_str(&format!(
                "\n# {} Feature Environment\n",
                manifest.feature.name
            ));
            for var in &env_config.variables {
                env_content.push_str(&format!("{}={}\n", var.name, var.default));
            }

            fs::write(&env_file, env_content)?;
            println!("    ✓ Environment variables added to .env");
        }

        Ok(())
    }

    #[allow(dead_code)]
    fn install_configuration(&self, manifest: &FeatureManifest) -> Result<()> {
        if let Some(config) = &manifest.configuration {
            println!("  ⚙️  Installing configuration files...");

            for file in &config.files {
                let dest_path = self.project_root.join(&file.path);
                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() {
                        fs::copy(&template_path, &dest_path)?;
                        println!("    ✓ Installed config: {}", file.path);
                    }
                }
            }
        }

        Ok(())
    }

    #[allow(dead_code)]
    fn install_resources(&self, manifest: &FeatureManifest) -> Result<()> {
        if let Some(resources) = &manifest.resources {
            println!("  📁 Installing resource files...");

            // Install public resources
            if let Some(public_resources) = &resources.public {
                for resource in public_resources {
                    let src_path = self
                        .features_path
                        .join(&manifest.feature.name)
                        .join(&resource.from);
                    let dest_path = self.project_root.join(&resource.to);

                    if let Some(parent) = dest_path.parent() {
                        fs::create_dir_all(parent)?;
                    }

                    if src_path.exists() {
                        fs::copy(&src_path, &dest_path)?;
                        println!("    ✓ Installed resource: {}", resource.to);
                    }
                }
            }
        }

        Ok(())
    }

    #[allow(dead_code)]
    fn install_scripts(&self, manifest: &FeatureManifest) -> Result<()> {
        if let Some(scripts) = &manifest.scripts {
            println!("  🔨 Installing scripts...");

            for script in scripts {
                let src_path = self
                    .features_path
                    .join(&manifest.feature.name)
                    .join(&script.from);
                let dest_path = self.project_root.join(&script.to);

                if let Some(parent) = dest_path.parent() {
                    fs::create_dir_all(parent)?;
                }

                if src_path.exists() {
                    fs::copy(&src_path, &dest_path)?;
                    println!("    ✓ Installed script: {}", script.to);
                }
            }
        }

        Ok(())
    }

    pub fn remove_feature(&self, feature_name: &str, _clean_deps: bool) -> Result<()> {
        println!("🗑️  Removing feature: {}", feature_name);

        // Load feature manifest to understand what to remove
        let _manifest = self.load_feature_manifest(feature_name)?;

        // Remove resources, configs, scripts, etc.
        // This is a complex operation that would need careful implementation

        println!("✅ Feature '{}' removed successfully", feature_name);
        Ok(())
    }
}
