//! Form Composer generator: creates master form configuration from fragments.

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

/// Generates master form configuration that includes all fragments.
pub struct FormComposerGenerator;

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

        let mut form_content = String::new();

        // Generate form metadata
        form_content.push_str(&Self::generate_form_metadata(spec)?);

        // Generate includes for all feature fragments
        form_content.push_str(&Self::generate_includes(spec)?);

        // Write master config-form.toml
        let form_file = output_dir.join("config-form.toml");
        std::fs::write(&form_file, form_content).map_err(|e| {
            crate::error::ProvisioningGenError::Other(format!(
                "Failed to write config-form.toml: {}",
                e
            ))
        })?;

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

    /// Generate form metadata section.
    fn generate_form_metadata(spec: &ProjectSpec) -> Result<String> {
        let mut metadata = String::new();

        metadata.push_str("# Master Form Configuration\n");
        metadata.push_str(&format!("# Auto-generated for project: {}\n", spec.name));
        metadata.push_str(
            "# This form composes all feature fragments into a complete configuration workflow\n\n",
        );

        metadata.push_str("[form]\n");
        metadata.push_str(&format!("name = \"{}-config\"\n", spec.name));
        metadata.push_str(&format!(
            "title = \"{} Configuration\"\n",
            Self::capitalize_first(&spec.name)
        ));
        metadata.push_str(&format!(
            "description = \"Complete configuration wizard for {}\"\n",
            spec.name
        ));
        metadata.push_str("backend = \"cli\"  # Use CLI backend by default (supports TUI, Web)\n");
        metadata.push_str("multi_step = true  # Enable multi-step form with sections\n");
        metadata.push('\n');

        Ok(metadata)
    }

    /// Generate includes section for all feature fragments.
    fn generate_includes(spec: &ProjectSpec) -> Result<String> {
        let mut includes = String::new();

        includes.push_str("# Feature Fragment Includes\n");
        includes.push_str(
            "# Each feature fragment is conditionally included based on configuration\n\n",
        );

        for feature in &spec.domain_features {
            includes.push_str(&Self::generate_feature_include(spec, feature)?);
            includes.push('\n');
        }

        Ok(includes)
    }

    /// Generate include entry for a single feature fragment.
    fn generate_feature_include(
        _spec: &ProjectSpec,
        feature: &crate::models::DomainFeature,
    ) -> Result<String> {
        let mut include = String::new();

        include.push_str("[[includes]]\n");
        include.push_str(&format!(
            "path = \"fragments/{}-section.toml\"\n",
            feature.name
        ));

        // Add description/label
        if let Some(desc) = &feature.description {
            include.push_str(&format!("label = \"{}\"\n", desc));
        } else {
            include.push_str(&format!(
                "label = \"{} Configuration\"\n",
                Self::capitalize_first(&feature.name)
            ));
        }

        // Add conditional logic if feature has optional flag
        // For now, we'll include all features by default
        // In the future, this could be based on a "required" field in DomainFeature
        if !feature.fields.is_empty() {
            include.push_str("required = true\n");
        }

        Ok(include)
    }

    /// 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(),
        }
    }
}
