//! Script generator: produces bash and nushell orchestration scripts.

use crate::error::Result;
use crate::models::ProjectSpec;
use crate::template::TemplateLoader;
use std::path::Path;
use tera::Context;

/// Generates orchestration scripts for provisioning.
pub struct ScriptGenerator;

impl ScriptGenerator {
    /// Generate bash and nushell scripts for provisioning orchestration.
    pub fn generate(
        spec: &ProjectSpec,
        output_dir: impl AsRef<Path>,
        template_loader: &TemplateLoader,
    ) -> Result<()> {
        let output_dir = output_dir.as_ref();
        tracing::info!(
            "Generating orchestration scripts for project: {}",
            spec.name
        );

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

        // Prepare template context
        let mut context = Context::new();
        context.insert("project_name", &spec.name);
        context.insert("features", &spec.domain_features);

        // Generate main configuration scripts
        Self::generate_configure_scripts(spec, &scripts_dir, template_loader, &context)?;

        // Generate nickel conversion scripts
        Self::generate_conversion_scripts(spec, &scripts_dir, template_loader, &context)?;

        // Generate validation scripts
        Self::generate_validation_scripts(spec, &scripts_dir, &context)?;

        // Generate utility scripts
        Self::generate_utility_scripts(spec, &scripts_dir, &context)?;

        // Generate README
        Self::generate_scripts_readme(spec, &scripts_dir)?;

        tracing::info!("Successfully generated orchestration scripts");
        Ok(())
    }

    /// Generate main configure scripts (configure.sh / configure.nu).
    fn generate_configure_scripts(
        _spec: &ProjectSpec,
        scripts_dir: &Path,
        template_loader: &TemplateLoader,
        context: &Context,
    ) -> Result<()> {
        // Generate configure.sh from template
        let configure_sh = template_loader.render("scripts/configure.sh.tera", context)?;
        let configure_sh_path = scripts_dir.join("configure.sh");
        std::fs::write(&configure_sh_path, configure_sh).map_err(|e| {
            crate::error::ProvisioningGenError::Other(format!(
                "Failed to write configure.sh: {}",
                e
            ))
        })?;
        Self::make_executable(&configure_sh_path);

        // Generate configure.nu from template
        let configure_nu = template_loader.render("scripts/configure.nu.tera", context)?;
        let configure_nu_path = scripts_dir.join("configure.nu");
        std::fs::write(&configure_nu_path, configure_nu).map_err(|e| {
            crate::error::ProvisioningGenError::Other(format!(
                "Failed to write configure.nu: {}",
                e
            ))
        })?;
        Self::make_executable(&configure_nu_path);

        tracing::debug!("Generated configure scripts");
        Ok(())
    }

    /// Generate nickel conversion scripts (nickel-to-X, X-to-nickel).
    fn generate_conversion_scripts(
        spec: &ProjectSpec,
        scripts_dir: &Path,
        template_loader: &TemplateLoader,
        context: &Context,
    ) -> Result<()> {
        let conversions = vec![
            (
                "nickel-to-json",
                "scripts/nickel-to-json.sh.tera",
                "scripts/nickel-to-json.nu.tera",
            ),
            (
                "nickel-to-yaml",
                "scripts/nickel-to-yaml.sh.tera",
                "scripts/nickel-to-yaml.nu.tera",
            ),
            (
                "json-to-nickel",
                "scripts/json-to-nickel.sh.tera",
                "scripts/json-to-nickel.nu.tera",
            ),
        ];

        for (name, sh_template, nu_template) in conversions {
            // Generate bash version
            let sh_content = template_loader.render(sh_template, context)?;
            let sh_path = scripts_dir.join(format!("{}.sh", name));
            std::fs::write(&sh_path, sh_content).map_err(|e| {
                crate::error::ProvisioningGenError::Other(format!(
                    "Failed to write {}.sh: {}",
                    name, e
                ))
            })?;
            Self::make_executable(&sh_path);

            // Generate nushell version
            let nu_content = template_loader.render(nu_template, context)?;
            let nu_path = scripts_dir.join(format!("{}.nu", name));
            std::fs::write(&nu_path, nu_content).map_err(|e| {
                crate::error::ProvisioningGenError::Other(format!(
                    "Failed to write {}.nu: {}",
                    name, e
                ))
            })?;
            Self::make_executable(&nu_path);
        }

        // Generate additional converters (yaml-to-nickel, toml-to-nickel)
        Self::generate_yaml_to_nickel(spec, scripts_dir)?;
        Self::generate_toml_to_nickel(spec, scripts_dir)?;
        Self::generate_nickel_to_toml(spec, scripts_dir)?;

        tracing::debug!("Generated conversion scripts");
        Ok(())
    }

    /// Generate validation scripts (validate-nickel.sh, apply-validators.sh).
    fn generate_validation_scripts(
        spec: &ProjectSpec,
        scripts_dir: &Path,
        _context: &Context,
    ) -> Result<()> {
        // validate-nickel.sh
        let validate_sh = format!(
            "#!/bin/bash\n\
             # Validate Nickel configuration for {}\n\
             set -euo pipefail\n\n\
             NICKEL_FILE=\"${{1:-config.ncl}}\"\n\
             \n\
             if [[ ! -f \"$NICKEL_FILE\" ]]; then\n\
               echo \"Error: Nickel file not found: $NICKEL_FILE\" >&2\n\
               exit 1\n\
             fi\n\
             \n\
             echo \"Validating $NICKEL_FILE...\"\n\
             nickel typecheck \"$NICKEL_FILE\" || {{\n\
               echo \"Error: Type checking failed\" >&2\n\
               exit 1\n\
             }}\n\
             \n\
             nickel eval --raw \"$NICKEL_FILE\" > /dev/null || {{\n\
               echo \"Error: Evaluation failed\" >&2\n\
               exit 1\n\
             }}\n\
             \n\
             echo \"Validation successful\"\n",
            spec.name
        );
        let validate_sh_path = scripts_dir.join("validate-nickel.sh");
        std::fs::write(&validate_sh_path, validate_sh).map_err(|e| {
            crate::error::ProvisioningGenError::Other(format!(
                "Failed to write validate-nickel.sh: {}",
                e
            ))
        })?;
        Self::make_executable(&validate_sh_path);

        // validate-nickel.nu
        let validate_nu = format!(
            "#!/usr/bin/env nu\n\
             # Validate Nickel configuration for {} (nushell version)\n\n\
             def main [nickel_file: path = \"config.ncl\"] {{\n\
               if not ($nickel_file | path exists) {{\n\
                 error make {{\n\
                   msg: $\"Nickel file not found: ($nickel_file)\"\n\
                 }}\n\
               }}\n\
               \n\
               print $\"Validating ($nickel_file)...\"\n\
               \n\
               # Type check\n\
               let typecheck = (nickel typecheck $nickel_file | complete)\n\
               if $typecheck.exit_code != 0 {{\n\
                 error make {{\n\
                   msg: \"Type checking failed\"\n\
                 }}\n\
               }}\n\
               \n\
               # Evaluate\n\
               let eval = (nickel eval --raw $nickel_file | complete)\n\
               if $eval.exit_code != 0 {{\n\
                 error make {{\n\
                   msg: \"Evaluation failed\"\n\
                 }}\n\
               }}\n\
               \n\
               print \"Validation successful\"\n\
             }}\n",
            spec.name
        );
        let validate_nu_path = scripts_dir.join("validate-nickel.nu");
        std::fs::write(&validate_nu_path, validate_nu).map_err(|e| {
            crate::error::ProvisioningGenError::Other(format!(
                "Failed to write validate-nickel.nu: {}",
                e
            ))
        })?;
        Self::make_executable(&validate_nu_path);

        // apply-validators.sh
        let apply_validators_sh = format!(
            "#!/bin/bash\n\
             # Apply Nickel validators to configuration for {}\n\
             set -euo pipefail\n\n\
             CONFIG_FILE=\"${{1:-config.ncl}}\"\n\
             VALIDATORS_DIR=\"${{VALIDATORS_DIR:-validators}}\"\n\
             \n\
             echo \"Applying validators from $VALIDATORS_DIR to $CONFIG_FILE...\"\n\
             \n\
             # Import validators and merge with config\n\
             nickel eval --raw <<EOF\n\
             let validators = import \"$VALIDATORS_DIR/config.ncl\" in\n\
             let config = import \"$CONFIG_FILE\" in\n\
             validators.validate config\n\
             EOF\n\
             \n\
             echo \"Validators applied successfully\"\n",
            spec.name
        );
        let apply_validators_sh_path = scripts_dir.join("apply-validators.sh");
        std::fs::write(&apply_validators_sh_path, apply_validators_sh).map_err(|e| {
            crate::error::ProvisioningGenError::Other(format!(
                "Failed to write apply-validators.sh: {}",
                e
            ))
        })?;
        Self::make_executable(&apply_validators_sh_path);

        // apply-validators.nu
        let apply_validators_nu = format!(
            "#!/usr/bin/env nu\n\
             # Apply Nickel validators to configuration for {} (nushell version)\n\n\
             def main [\n\
               config_file: path = \"config.ncl\",\n\
               --validators_dir: path = \"validators\"\n\
             ] {{\n\
               print $\"Applying validators from ($validators_dir) to ($config_file)...\"\n\
               \n\
               let nickel_code = $\"let validators = import \\\"($validators_dir)/config.ncl\\\" in\\nlet config = import \\\"($config_file)\\\" in\\nvalidators.validate config\"\n\
               \n\
               let result = ($nickel_code | nickel eval --raw | complete)\n\
               if $result.exit_code != 0 {{\n\
                 error make {{\n\
                   msg: $\"Validation failed: ($result.stderr)\"\n\
                 }}\n\
               }}\n\
               \n\
               print \"Validators applied successfully\"\n\
             }}\n",
            spec.name
        );
        let apply_validators_nu_path = scripts_dir.join("apply-validators.nu");
        std::fs::write(&apply_validators_nu_path, apply_validators_nu).map_err(|e| {
            crate::error::ProvisioningGenError::Other(format!(
                "Failed to write apply-validators.nu: {}",
                e
            ))
        })?;
        Self::make_executable(&apply_validators_nu_path);

        tracing::debug!("Generated validation scripts");
        Ok(())
    }

    /// Generate utility scripts (merge-configs.sh, export-final.sh).
    fn generate_utility_scripts(
        spec: &ProjectSpec,
        scripts_dir: &Path,
        _context: &Context,
    ) -> Result<()> {
        // merge-configs.sh
        let merge_configs_sh = format!(
            "#!/bin/bash\n\
             # Merge multiple Nickel configurations for {}\n\
             set -euo pipefail\n\n\
             if [[ $# -lt 2 ]]; then\n\
               echo \"Usage: $0 <base.ncl> <override.ncl> [output.ncl]\" >&2\n\
               exit 1\n\
             fi\n\
             \n\
             BASE_FILE=\"$1\"\n\
             OVERRIDE_FILE=\"$2\"\n\
             OUTPUT_FILE=\"${{3:-merged.ncl}}\"\n\
             \n\
             echo \"Merging $OVERRIDE_FILE into $BASE_FILE -> $OUTPUT_FILE\"\n\
             \n\
             nickel eval --raw <<EOF > \"$OUTPUT_FILE\"\n\
             let base = import \"$BASE_FILE\" in\n\
             let override = import \"$OVERRIDE_FILE\" in\n\
             std.record.merge base override\n\
             EOF\n\
             \n\
             echo \"Merge complete: $OUTPUT_FILE\"\n",
            spec.name
        );
        let merge_configs_sh_path = scripts_dir.join("merge-configs.sh");
        std::fs::write(&merge_configs_sh_path, merge_configs_sh).map_err(|e| {
            crate::error::ProvisioningGenError::Other(format!(
                "Failed to write merge-configs.sh: {}",
                e
            ))
        })?;
        Self::make_executable(&merge_configs_sh_path);

        // merge-configs.nu
        let merge_configs_nu = format!(
            "#!/usr/bin/env nu\n\
             # Merge multiple Nickel configurations for {} (nushell version)\n\n\
             def main [\n\
               base_file: path,\n\
               override_file: path,\n\
               output_file: path = \"merged.ncl\"\n\
             ] {{\n\
               print $\"Merging ($override_file) into ($base_file) -> ($output_file)\"\n\
               \n\
               let nickel_code = $\"let base = import \\\"($base_file)\\\" in\\nlet override = import \\\"($override_file)\\\" in\\nstd.record.merge base override\"\n\
               \n\
               $nickel_code | nickel eval --raw | save -f $output_file\n\
               \n\
               print $\"Merge complete: ($output_file)\"\n\
             }}\n",
            spec.name
        );
        let merge_configs_nu_path = scripts_dir.join("merge-configs.nu");
        std::fs::write(&merge_configs_nu_path, merge_configs_nu).map_err(|e| {
            crate::error::ProvisioningGenError::Other(format!(
                "Failed to write merge-configs.nu: {}",
                e
            ))
        })?;
        Self::make_executable(&merge_configs_nu_path);

        // export-final.sh
        let export_final_sh = format!(
            "#!/bin/bash\n\
             # Export final configuration for deployment for {}\n\
             set -euo pipefail\n\n\
             NICKEL_FILE=\"${{1:-config.ncl}}\"\n\
             FORMAT=\"${{2:-json}}\"\n\
             OUTPUT_FILE=\"${{3:-config.$FORMAT}}\"\n\
             \n\
             echo \"Exporting $NICKEL_FILE to $OUTPUT_FILE ($FORMAT format)\"\n\
             \n\
             case \"$FORMAT\" in\n\
               json)\n\
                 nickel export --format json \"$NICKEL_FILE\" > \"$OUTPUT_FILE\"\n\
                 ;;\n\
               yaml)\n\
                 nickel export --format yaml \"$NICKEL_FILE\" > \"$OUTPUT_FILE\"\n\
                 ;;\n\
               toml)\n\
                 nickel export --format toml \"$NICKEL_FILE\" > \"$OUTPUT_FILE\"\n\
                 ;;\n\
               *)\n\
                 echo \"Error: Unsupported format: $FORMAT\" >&2\n\
                 echo \"Supported: json, yaml, toml\" >&2\n\
                 exit 1\n\
                 ;;\n\
             esac\n\
             \n\
             echo \"Export complete: $OUTPUT_FILE\"\n",
            spec.name
        );
        let export_final_sh_path = scripts_dir.join("export-final.sh");
        std::fs::write(&export_final_sh_path, export_final_sh).map_err(|e| {
            crate::error::ProvisioningGenError::Other(format!(
                "Failed to write export-final.sh: {}",
                e
            ))
        })?;
        Self::make_executable(&export_final_sh_path);

        // export-final.nu
        let export_final_nu = format!(
            "#!/usr/bin/env nu\n\
             # Export final configuration for deployment for {} (nushell version)\n\n\
             def main [\n\
               nickel_file: path = \"config.ncl\",\n\
               format: string = \"json\",\n\
               output_file?: path\n\
             ] {{\n\
               let out = if ($output_file | is-empty) {{ $\"config.($format)\" }} else {{ $output_file }}\n\
               \n\
               print $\"Exporting ($nickel_file) to ($out) (($format) format)\"\n\
               \n\
               match $format {{\n\
                 \"json\" => {{ nickel export --format json $nickel_file | save -f $out }},\n\
                 \"yaml\" => {{ nickel export --format yaml $nickel_file | save -f $out }},\n\
                 \"toml\" => {{ nickel export --format toml $nickel_file | save -f $out }},\n\
                 _ => {{\n\
                   error make {{\n\
                     msg: $\"Unsupported format: ($format). Supported: json, yaml, toml\"\n\
                   }}\n\
                 }}\n\
               }}\n\
               \n\
               print $\"Export complete: ($out)\"\n\
             }}\n",
            spec.name
        );
        let export_final_nu_path = scripts_dir.join("export-final.nu");
        std::fs::write(&export_final_nu_path, export_final_nu).map_err(|e| {
            crate::error::ProvisioningGenError::Other(format!(
                "Failed to write export-final.nu: {}",
                e
            ))
        })?;
        Self::make_executable(&export_final_nu_path);

        tracing::debug!("Generated utility scripts");
        Ok(())
    }

    /// Generate yaml-to-nickel converter.
    fn generate_yaml_to_nickel(_spec: &ProjectSpec, scripts_dir: &Path) -> Result<()> {
        let yaml_to_nickel_sh = "#!/bin/bash\n\
            # Convert YAML to Nickel\n\
            set -euo pipefail\n\n\
            YAML_FILE=\"${1}\"\n\
            NICKEL_FILE=\"${2:-${YAML_FILE%.yaml}.ncl}\"\n\
            \n\
            if [[ ! -f \"$YAML_FILE\" ]]; then\n\
              echo \"Error: YAML file not found: $YAML_FILE\" >&2\n\
              exit 1\n\
            fi\n\
            \n\
            # Convert via JSON intermediary\n\
            TEMP_JSON=\"$(mktemp)\"\n\
            yq -o json \"$YAML_FILE\" > \"$TEMP_JSON\"\n\
            ./json-to-nickel.sh \"$TEMP_JSON\" \"$NICKEL_FILE\"\n\
            rm -f \"$TEMP_JSON\"\n";

        let yaml_to_nickel_sh_path = scripts_dir.join("yaml-to-nickel.sh");
        std::fs::write(&yaml_to_nickel_sh_path, yaml_to_nickel_sh).map_err(|e| {
            crate::error::ProvisioningGenError::Other(format!(
                "Failed to write yaml-to-nickel.sh: {}",
                e
            ))
        })?;
        Self::make_executable(&yaml_to_nickel_sh_path);

        let yaml_to_nickel_nu = "#!/usr/bin/env nu\n\
            # Convert YAML to Nickel (nushell version)\n\n\
            def main [yaml_file: path, nickel_file?: path] {{\n\
              let out = if ($nickel_file | is-empty) {{ ($yaml_file | path parse | update extension ncl | path join) }} else {{ $nickel_file }}\n\
              \n\
              if not ($yaml_file | path exists) {{\n\
                error make {{ msg: $\"YAML file not found: ($yaml_file)\" }}\n\
              }}\n\
              \n\
              # Convert via JSON intermediary\n\
              let json_content = (open $yaml_file | to json)\n\
              $json_content | save -f temp.json\n\
              nu json-to-nickel.nu temp.json $out\n\
              rm temp.json\n\
            }}\n";

        let yaml_to_nickel_nu_path = scripts_dir.join("yaml-to-nickel.nu");
        std::fs::write(&yaml_to_nickel_nu_path, yaml_to_nickel_nu).map_err(|e| {
            crate::error::ProvisioningGenError::Other(format!(
                "Failed to write yaml-to-nickel.nu: {}",
                e
            ))
        })?;
        Self::make_executable(&yaml_to_nickel_nu_path);

        Ok(())
    }

    /// Generate toml-to-nickel converter.
    fn generate_toml_to_nickel(_spec: &ProjectSpec, scripts_dir: &Path) -> Result<()> {
        let toml_to_nickel_sh = "#!/bin/bash\n\
            # Convert TOML to Nickel\n\
            set -euo pipefail\n\n\
            TOML_FILE=\"${1}\"\n\
            NICKEL_FILE=\"${2:-${TOML_FILE%.toml}.ncl}\"\n\
            \n\
            if [[ ! -f \"$TOML_FILE\" ]]; then\n\
              echo \"Error: TOML file not found: $TOML_FILE\" >&2\n\
              exit 1\n\
            fi\n\
            \n\
            # Convert via JSON intermediary\n\
            TEMP_JSON=\"$(mktemp)\"\n\
            cat \"$TOML_FILE\" | toml2json > \"$TEMP_JSON\"\n\
            ./json-to-nickel.sh \"$TEMP_JSON\" \"$NICKEL_FILE\"\n\
            rm -f \"$TEMP_JSON\"\n";

        let toml_to_nickel_sh_path = scripts_dir.join("toml-to-nickel.sh");
        std::fs::write(&toml_to_nickel_sh_path, toml_to_nickel_sh).map_err(|e| {
            crate::error::ProvisioningGenError::Other(format!(
                "Failed to write toml-to-nickel.sh: {}",
                e
            ))
        })?;
        Self::make_executable(&toml_to_nickel_sh_path);

        let toml_to_nickel_nu = "#!/usr/bin/env nu\n\
            # Convert TOML to Nickel (nushell version)\n\n\
            def main [toml_file: path, nickel_file?: path] {{\n\
              let out = if ($nickel_file | is-empty) {{ ($toml_file | path parse | update extension ncl | path join) }} else {{ $nickel_file }}\n\
              \n\
              if not ($toml_file | path exists) {{\n\
                error make {{ msg: $\"TOML file not found: ($toml_file)\" }}\n\
              }}\n\
              \n\
              # Convert via JSON intermediary\n\
              let json_content = (open $toml_file | to json)\n\
              $json_content | save -f temp.json\n\
              nu json-to-nickel.nu temp.json $out\n\
              rm temp.json\n\
            }}\n";

        let toml_to_nickel_nu_path = scripts_dir.join("toml-to-nickel.nu");
        std::fs::write(&toml_to_nickel_nu_path, toml_to_nickel_nu).map_err(|e| {
            crate::error::ProvisioningGenError::Other(format!(
                "Failed to write toml-to-nickel.nu: {}",
                e
            ))
        })?;
        Self::make_executable(&toml_to_nickel_nu_path);

        Ok(())
    }

    /// Generate nickel-to-toml converter.
    fn generate_nickel_to_toml(_spec: &ProjectSpec, scripts_dir: &Path) -> Result<()> {
        let nickel_to_toml_sh = "#!/bin/bash\n\
            # Convert Nickel to TOML\n\
            set -euo pipefail\n\n\
            NICKEL_FILE=\"${1}\"\n\
            TOML_FILE=\"${2:-${NICKEL_FILE%.ncl}.toml}\"\n\
            \n\
            if [[ ! -f \"$NICKEL_FILE\" ]]; then\n\
              echo \"Error: Nickel file not found: $NICKEL_FILE\" >&2\n\
              exit 1\n\
            fi\n\
            \n\
            nickel export --format toml \"$NICKEL_FILE\" > \"$TOML_FILE\"\n\
            echo \"Converted $NICKEL_FILE to $TOML_FILE\"\n";

        let nickel_to_toml_sh_path = scripts_dir.join("nickel-to-toml.sh");
        std::fs::write(&nickel_to_toml_sh_path, nickel_to_toml_sh).map_err(|e| {
            crate::error::ProvisioningGenError::Other(format!(
                "Failed to write nickel-to-toml.sh: {}",
                e
            ))
        })?;
        Self::make_executable(&nickel_to_toml_sh_path);

        let nickel_to_toml_nu = "#!/usr/bin/env nu\n\
            # Convert Nickel to TOML (nushell version)\n\n\
            def main [nickel_file: path, toml_file?: path] {{\n\
              let out = if ($toml_file | is-empty) {{ ($nickel_file | path parse | update extension toml | path join) }} else {{ $toml_file }}\n\
              \n\
              if not ($nickel_file | path exists) {{\n\
                error make {{ msg: $\"Nickel file not found: ($nickel_file)\" }}\n\
              }}\n\
              \n\
              nickel export --format toml $nickel_file | save -f $out\n\
              print $\"Converted ($nickel_file) to ($out)\"\n\
            }}\n";

        let nickel_to_toml_nu_path = scripts_dir.join("nickel-to-toml.nu");
        std::fs::write(&nickel_to_toml_nu_path, nickel_to_toml_nu).map_err(|e| {
            crate::error::ProvisioningGenError::Other(format!(
                "Failed to write nickel-to-toml.nu: {}",
                e
            ))
        })?;
        Self::make_executable(&nickel_to_toml_nu_path);

        Ok(())
    }

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

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

        content.push_str(
            "This directory contains bash and nushell scripts for provisioning orchestration.\n\n\
             ## Overview\n\n\
             Scripts implement the nickel-roundtrip workflow: TypeDialog → JSON → Nickel → Validation → Export.\n\n\
             ## Script Categories\n\n\
             ### Main Configuration\n\n\
             - `configure.sh` / `configure.nu` - Main nickel-roundtrip orchestration script\n\n\
             ### Format Conversion\n\n\
             **From Nickel:**\n\
             - `nickel-to-json.sh` / `.nu` - Convert Nickel to JSON\n\
             - `nickel-to-yaml.sh` / `.nu` - Convert Nickel to YAML\n\
             - `nickel-to-toml.sh` / `.nu` - Convert Nickel to TOML\n\n\
             **To Nickel:**\n\
             - `json-to-nickel.sh` / `.nu` - Convert JSON to Nickel\n\
             - `yaml-to-nickel.sh` / `.nu` - Convert YAML to Nickel\n\
             - `toml-to-nickel.sh` / `.nu` - Convert TOML to Nickel\n\n\
             ### Validation\n\n\
             - `validate-nickel.sh` / `.nu` - Type-check and evaluate Nickel files\n\
             - `apply-validators.sh` / `.nu` - Apply custom validators to configuration\n\n\
             ### Utilities\n\n\
             - `merge-configs.sh` / `.nu` - Merge multiple Nickel configurations\n\
             - `export-final.sh` / `.nu` - Export final configuration for deployment\n\n\
             ## Nickel-Roundtrip Workflow\n\n\
             Complete configuration workflow:\n\n\
             ```bash\n\
             # 1. Collect configuration via TypeDialog\n\
             typedialog --form fragments/complete.toml --output user-input.json\n\
             \n\
             # 2. Convert JSON to Nickel\n\
             ./scripts/json-to-nickel.sh user-input.json config.ncl\n\
             \n\
             # 3. Validate against schemas\n\
             ./scripts/validate-nickel.sh config.ncl\n\
             \n\
             # 4. Apply custom validators\n\
             ./scripts/apply-validators.sh config.ncl\n\
             \n\
             # 5. Merge with defaults\n\
             ./scripts/merge-configs.sh defaults/config.ncl config.ncl final.ncl\n\
             \n\
             # 6. Export to deployment format\n\
             ./scripts/export-final.sh final.ncl yaml deployment.yaml\n\
             ```\n\n\
             Or use the main orchestration script:\n\n\
             ```bash\n\
             ./scripts/configure.sh\n\
             ```\n\n\
             ## Nushell Versions\n\n\
             All scripts have `.nu` equivalents with better error handling:\n\n\
             ```bash\n\
             nu scripts/configure.nu\n\
             ```\n\n\
             ## Requirements\n\n\
             - **nickel** - Nickel language runtime\n\
             - **typedialog** - For interactive forms (CLI backend)\n\
             - **jq** - JSON processing (for bash scripts)\n\
             - **yq** - YAML processing (for YAML conversions)\n\
             - **toml2json** - TOML to JSON conversion\n\
             - **nushell** - For `.nu` script versions (optional)\n\n\
             ## Error Handling\n\n\
             All scripts use `set -euo pipefail` (bash) or structured error handling (nushell) to fail fast on errors.\n\n\
             ## Permissions\n\n\
             Scripts are automatically marked executable. Manual override:\n\n\
             ```bash\n\
             chmod +x scripts/*.sh scripts/*.nu\n\
             ```\n\n",
        );

        let readme_file = scripts_dir.join("README.md");
        std::fs::write(&readme_file, content).map_err(|e| {
            crate::error::ProvisioningGenError::Other(format!(
                "Failed to write scripts README: {}",
                e
            ))
        })?;

        Ok(())
    }

    /// Make a script file executable on Unix systems.
    #[cfg(unix)]
    fn make_executable(path: &Path) {
        use std::os::unix::fs::PermissionsExt;
        if let Ok(metadata) = std::fs::metadata(path) {
            let mut perms = metadata.permissions();
            perms.set_mode(0o755);
            std::fs::set_permissions(path, perms).ok();
        }
    }

    /// No-op on non-Unix systems.
    #[cfg(not(unix))]
    fn make_executable(_path: &Path) {}
}
