//! Values generator: creates runtime values configuration for deployment environments.

use crate::error::Result;
use crate::models::ProjectSpec;
use std::path::Path;

/// Generates runtime values files for environment-specific configuration.
pub struct ValuesGenerator;

impl ValuesGenerator {
    /// Generate values configuration files.
    pub fn generate(spec: &ProjectSpec, output_dir: impl AsRef<Path>) -> Result<()> {
        let output_dir = output_dir.as_ref();
        tracing::info!("Generating values configuration for project: {}", spec.name);

        // Ensure values directory exists
        let values_dir = output_dir.join("values");
        std::fs::create_dir_all(&values_dir).map_err(|e| {
            crate::error::ProvisioningGenError::Other(format!(
                "Failed to create values directory: {}",
                e
            ))
        })?;

        // Generate main values file
        Self::generate_values_config(spec, &values_dir)?;

        // Generate environment-specific values
        Self::generate_environment_values(spec, &values_dir, "development")?;
        Self::generate_environment_values(spec, &values_dir, "staging")?;
        Self::generate_environment_values(spec, &values_dir, "production")?;

        // Generate README
        Self::generate_values_readme(spec, &values_dir)?;

        tracing::info!("Successfully generated values configuration");
        Ok(())
    }

    /// Generate main values/config.ncl file.
    fn generate_values_config(spec: &ProjectSpec, values_dir: &Path) -> Result<()> {
        let mut content = String::new();

        content.push_str(&format!(
            "# Runtime values configuration for {}\n\
             # This file separates runtime values from configuration structure\n\n",
            spec.name
        ));

        content.push_str("# Import defaults\n");
        content.push_str("let defaults = import \"../defaults/config.ncl\" in\n\n");

        content.push_str("# Runtime values (override these per environment)\n");
        content.push_str("{\n");

        for feature in &spec.domain_features {
            content.push_str(&format!("  {} = {{\n", feature.name));

            for field in &feature.fields {
                if field.sensitive {
                    content.push_str(&format!(
                        "    {} = \"OVERRIDE_IN_ENVIRONMENT\",  # Sensitive\n",
                        field.name
                    ));
                } else if let Some(default) = &field.default {
                    content.push_str(&format!("    {} = {},\n", field.name, default));
                } else {
                    content.push_str(&format!(
                        "    {} = defaults.{}.{},\n",
                        field.name, feature.name, field.name
                    ));
                }
            }

            content.push_str("  },\n");
        }

        content.push_str("}\n");

        std::fs::write(values_dir.join("config.ncl"), content).map_err(|e| {
            crate::error::ProvisioningGenError::Other(format!(
                "Failed to write values/config.ncl: {}",
                e
            ))
        })?;

        Ok(())
    }

    /// Generate environment-specific values file.
    fn generate_environment_values(
        spec: &ProjectSpec,
        values_dir: &Path,
        environment: &str,
    ) -> Result<()> {
        let mut content = String::new();

        content.push_str(&format!(
            "# {} environment values for {}\n\n",
            Self::capitalize_first(environment),
            spec.name
        ));

        content.push_str("# Import base values\n");
        content.push_str("let base = import \"./config.ncl\" in\n\n");

        content.push_str(&format!(
            "# {} overrides\n",
            Self::capitalize_first(environment)
        ));
        content.push_str("std.record.merge base {\n");

        // Add environment-specific examples
        match environment {
            "development" => {
                content.push_str("  # Development-specific values\n");
                content.push_str("  # Example: lower resource limits, verbose logging\n");
            }
            "staging" => {
                content.push_str("  # Staging-specific values\n");
                content.push_str("  # Example: production-like but with test data\n");
            }
            "production" => {
                content.push_str("  # Production-specific values\n");
                content.push_str("  # Example: high availability, strict security\n");
            }
            _ => {}
        }

        content.push_str("}\n");

        std::fs::write(values_dir.join(format!("{}.ncl", environment)), content).map_err(|e| {
            crate::error::ProvisioningGenError::Other(format!(
                "Failed to write {}.ncl: {}",
                environment, e
            ))
        })?;

        Ok(())
    }

    /// Generate README.md for values directory.
    fn generate_values_readme(spec: &ProjectSpec, values_dir: &Path) -> Result<()> {
        let mut content = String::new();

        content.push_str(&format!("# Runtime Values - {}\n\n", spec.name));

        content.push_str(
            "This directory contains runtime values separated from configuration structure.\n\n\
             ## Overview\n\n\
             Values files allow environment-specific overrides while keeping the core \
             configuration structure unchanged.\n\n\
             ## Files\n\n\
             - `config.ncl` - Base runtime values (references defaults)\n\
             - `development.ncl` - Development environment overrides\n\
             - `staging.ncl` - Staging environment overrides\n\
             - `production.ncl` - Production environment overrides\n\n\
             ## Usage\n\n\
             ### Development\n\n\
             ```bash\n\
             nickel export values/development.ncl > config.json\n\
             ```\n\n\
             ### Production\n\n\
             ```bash\n\
             nickel export values/production.ncl > config.json\n\
             ```\n\n\
             ## Environment Variables\n\n\
             Sensitive values should be provided via environment variables:\n\n\
             ```nickel\n\
             {\n\
               database = {\n\
                 password = std.env.DATABASE_PASSWORD,\n\
               },\n\
             }\n\
             ```\n\n\
             ## Merging Strategy\n\n\
             1. **Defaults** (`defaults/config.ncl`) - Sensible defaults for all fields\n\
             2. **Base values** (`values/config.ncl`) - Runtime values common to all environments\n\
             3. **Environment values** (`values/{env}.ncl`) - Environment-specific overrides\n\n\
             ```nickel\n\
             let defaults = import \"defaults/config.ncl\" in\n\
             let values = import \"values/config.ncl\" in\n\
             let env_values = import \"values/production.ncl\" in\n\
             std.record.merge_all [defaults, values, env_values]\n\
             ```\n\n\
             ## Security\n\n\
             - Never commit sensitive values (passwords, API keys) to version control\n\
             - Use environment variables or secret management systems\n\
             - Mark sensitive fields in `values/config.ncl` with `# Sensitive` comment\n\n",
        );

        std::fs::write(values_dir.join("README.md"), content).map_err(|e| {
            crate::error::ProvisioningGenError::Other(format!(
                "Failed to write values README: {}",
                e
            ))
        })?;

        Ok(())
    }

    /// Capitalize first letter of a string.
    fn capitalize_first(s: &str) -> String {
        let mut chars = s.chars();
        match chars.next() {
            None => String::new(),
            Some(first) => first.to_uppercase().collect::<String>() + chars.as_str(),
        }
    }
}
