//! Wizard Command - Interactive project bootstrap with deterministic factory scaffold
//!
//! `ggen wizard` creates a closed, deterministic factory scaffold with:
//! - RDF-first specification layout
//! - Deterministic generation pipeline
//! - Receipts/proofs contracts
//! - World manifest + verifier
//! - Initial SPARQL + Tera stubs
//! - Runnable ggen sync from minute zero
//!
//! ## Profiles
//!
//! - `receipts-first` (default): World manifest, receipt schemas, audit trail
//! - `c4-diagrams`: C4 L1-L4 Mermaid diagram generation
//! - `openapi-contracts`: OpenAPI spec generation
//! - `infra-k8s-gcp`: Kubernetes + GCP infrastructure manifests
//! - `lnctrl-output-contracts`: LN_CTRL output contract schemas
//!
//! ## Usage
//!
//! ```bash
//! # Interactive mode
//! ggen wizard
//!
//! # Non-interactive with profile
//! ggen wizard --profile receipts-first --yes
//!
//! # Custom output directory
//! ggen wizard --output-dir ./my-project
//! ```

#![allow(clippy::unused_unit)]

use clap_noun_verb_macros::verb;
use ggen_core::codegen::executor::{SyncExecutor, SyncOptions};
use ggen_core::codegen::FileTransaction;
use serde::{Deserialize, Serialize};
use std::fs;
use std::io::{self, Write};
use std::path::Path;

// ============================================================================
// Types
// ============================================================================

/// Available wizard profiles
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum WizardProfile {
    /// Receipts-first (default): World manifest + receipt schemas + audit trail
    #[serde(rename = "receipts-first")]
    ReceiptsFirst,
    /// C4 diagrams: L1-L4 Mermaid outputs
    #[serde(rename = "c4-diagrams")]
    C4Diagrams,
    /// OpenAPI contracts
    #[serde(rename = "openapi-contracts")]
    OpenAPIContracts,
    /// Infrastructure: K8s + GCP
    #[serde(rename = "infra-k8s-gcp")]
    InfraK8sGcp,
    /// LN_CTRL output contracts
    #[serde(rename = "lnctrl-output-contracts")]
    LnCtrlOutputContracts,
    /// LN_CTRL full profile: λn execution traces with causal chaining receipts
    #[serde(rename = "ln-ctrl")]
    LnCtrl,
    /// MCP/A2A: Model Context Protocol and Agent-to-Agent configuration
    #[serde(rename = "mcp-a2a")]
    McpA2a,
    /// Custom (advanced)
    #[serde(rename = "custom")]
    Custom,
}

impl WizardProfile {
    /// Parse profile from string
    #[allow(clippy::should_implement_trait)]
    pub fn from_str(s: &str) -> Result<Self, String> {
        match s {
            "receipts-first" => Ok(Self::ReceiptsFirst),
            "c4-diagrams" => Ok(Self::C4Diagrams),
            "openapi-contracts" => Ok(Self::OpenAPIContracts),
            "infra-k8s-gcp" => Ok(Self::InfraK8sGcp),
            "lnctrl-output-contracts" => Ok(Self::LnCtrlOutputContracts),
            "ln-ctrl" => Ok(Self::LnCtrl),
            "mcp-a2a" => Ok(Self::McpA2a),
            "custom" => Ok(Self::Custom),
            _ => Err(format!("Unknown profile: {}", s)),
        }
    }

    /// Get profile name as string
    pub fn as_str(&self) -> &'static str {
        match self {
            Self::ReceiptsFirst => "receipts-first",
            Self::C4Diagrams => "c4-diagrams",
            Self::OpenAPIContracts => "openapi-contracts",
            Self::InfraK8sGcp => "infra-k8s-gcp",
            Self::LnCtrlOutputContracts => "lnctrl-output-contracts",
            Self::LnCtrl => "ln-ctrl",
            Self::McpA2a => "mcp-a2a",
            Self::Custom => "custom",
        }
    }

    /// Get profile description
    pub fn description(&self) -> &'static str {
        match self {
            Self::ReceiptsFirst => "World manifest + receipt schemas + audit trail (default)",
            Self::C4Diagrams => "C4 L1-L4 Mermaid diagram generation",
            Self::OpenAPIContracts => "OpenAPI specification generation",
            Self::InfraK8sGcp => "Kubernetes + GCP infrastructure manifests",
            Self::LnCtrlOutputContracts => "LN_CTRL output contract schemas",
            Self::LnCtrl => {
                "LN_CTRL full profile: λn execution traces with causal chaining receipts"
            }
            Self::McpA2a => "MCP (Model Context Protocol) + A2A (Agent-to-Agent) configuration",
            Self::Custom => "Custom configuration (advanced)",
        }
    }
}

/// Project metadata
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ProjectMetadata {
    pub name: String,
    pub version: String,
    pub description: String,
    pub license: String,
    pub authors: Vec<String>,
}

impl Default for ProjectMetadata {
    fn default() -> Self {
        Self {
            name: "my-ggen-project".to_string(),
            version: "0.1.0".to_string(),
            description: "A ggen project initialized with wizard".to_string(),
            license: "MIT".to_string(),
            authors: vec!["ggen wizard".to_string()],
        }
    }
}

/// Wizard configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
#[allow(clippy::struct_excessive_bools)]
pub struct WizardConfig {
    pub profile: WizardProfile,
    pub metadata: ProjectMetadata,
    pub deterministic_output: bool,
    pub strict_template_variables: bool,
    pub shacl_validation: bool,
    pub syntax_validation: bool,
    pub audit_trail: bool,
    pub generate_world_manifest: bool,
    pub generate_world_verifier: bool,
    pub specs_dir: String,
    pub ontologies_dir: String,
    pub templates_dir: String,
    pub output_dir: String,
    pub sparql_dir: String,
}

impl Default for WizardConfig {
    fn default() -> Self {
        Self {
            profile: WizardProfile::ReceiptsFirst,
            metadata: ProjectMetadata::default(),
            deterministic_output: true,
            strict_template_variables: true,
            shacl_validation: true,
            syntax_validation: true,
            audit_trail: true,
            generate_world_manifest: true,
            generate_world_verifier: true,
            specs_dir: ".specify/specs".to_string(),
            ontologies_dir: ".specify/ontologies".to_string(),
            templates_dir: "templates".to_string(),
            output_dir: ".".to_string(),
            sparql_dir: "sparql".to_string(),
        }
    }
}

/// Wizard output
#[derive(Debug, Clone, Serialize)]
pub struct WizardOutput {
    pub status: String,
    pub project_dir: String,
    pub profile: String,
    pub files_created: Vec<String>,
    pub directories_created: Vec<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub error: Option<String>,
    pub next_steps: Vec<String>,
}

// ============================================================================
// Verb Command
// ============================================================================

/// Initialize a new ggen project with interactive wizard
///
/// The wizard guides you through project setup with profiles for common use cases.
///
/// ## Usage
///
/// ```bash
/// # Interactive mode
fn apply_metadata_overrides(
    config: &mut WizardConfig, name: Option<String>, version: Option<String>,
    description: Option<String>,
) {
    if let Some(n) = name {
        config.metadata.name = n;
    }
    if let Some(v) = version {
        config.metadata.version = v;
    }
    if let Some(d) = description {
        config.metadata.description = d;
    }
}

/// ggen wizard
///
/// # Non-interactive with defaults
/// ggen wizard --yes
///
/// # Specific profile
/// ggen wizard --profile c4-diagrams
///
/// # Custom output directory
/// ggen wizard --output-dir ./my-project
///
/// # Skip initial sync
/// ggen wizard --no-sync
/// ```
#[verb("wizard", "root")]
pub fn wizard(
    profile: Option<String>, output_dir: Option<String>, yes: Option<bool>, no_sync: Option<bool>,
    name: Option<String>, version: Option<String>, description: Option<String>,
) -> clap_noun_verb::Result<WizardOutput> {
    let output_path = output_dir.unwrap_or_else(|| ".".to_string());
    let accept_defaults = yes.unwrap_or(false);
    let skip_sync = no_sync.unwrap_or(false);

    // Parse profile if provided
    let selected_profile = if let Some(profile_str) = profile {
        WizardProfile::from_str(&profile_str).map_err(|e| {
            clap_noun_verb::NounVerbError::execution_error(format!("Invalid profile: {}", e))
        })?
    } else if accept_defaults {
        WizardProfile::ReceiptsFirst
    } else {
        // Interactive profile selection
        select_profile_interactive()?
    };

    // Create wizard config
    let mut config = if accept_defaults {
        WizardConfig {
            profile: selected_profile,
            ..Default::default()
        }
    } else {
        // Interactive configuration
        configure_interactive(selected_profile)?
    };

    // Override metadata if provided via CLI
    apply_metadata_overrides(&mut config, name, version, description);

    // Perform scaffold generation
    perform_wizard(&output_path, config, skip_sync)
}

// ============================================================================
// Interactive Functions
// ============================================================================

fn select_profile_interactive() -> clap_noun_verb::Result<WizardProfile> {
    println!("\n🧙 ggen wizard - Bootstrap your project");
    println!("\nWizard creates a deterministic factory; outputs are disposable projections.\n");
    println!("Select a profile:\n");
    println!(
        "  1. receipts-first (default) - {}",
        WizardProfile::ReceiptsFirst.description()
    );
    println!(
        "  2. c4-diagrams - {}",
        WizardProfile::C4Diagrams.description()
    );
    println!(
        "  3. openapi-contracts - {}",
        WizardProfile::OpenAPIContracts.description()
    );
    println!(
        "  4. infra-k8s-gcp - {}",
        WizardProfile::InfraK8sGcp.description()
    );
    println!(
        "  5. lnctrl-output-contracts - {}",
        WizardProfile::LnCtrlOutputContracts.description()
    );
    println!("  6. ln-ctrl - {}", WizardProfile::LnCtrl.description());
    println!("  7. mcp-a2a - {}", WizardProfile::McpA2a.description());
    println!("  8. custom - {}", WizardProfile::Custom.description());

    print!("\nEnter choice (1-8) [1]: ");
    io::stdout().flush().map_err(|e| {
        clap_noun_verb::NounVerbError::execution_error(format!("Failed to flush stdout: {}", e))
    })?;

    let mut input = String::new();
    io::stdin().read_line(&mut input).map_err(|e| {
        clap_noun_verb::NounVerbError::execution_error(format!("Failed to read input: {}", e))
    })?;

    let choice = input.trim();
    let profile = match choice {
        "" | "1" => WizardProfile::ReceiptsFirst,
        "2" => WizardProfile::C4Diagrams,
        "3" => WizardProfile::OpenAPIContracts,
        "4" => WizardProfile::InfraK8sGcp,
        "5" => WizardProfile::LnCtrlOutputContracts,
        "6" => WizardProfile::LnCtrl,
        "7" => WizardProfile::McpA2a,
        "8" => WizardProfile::Custom,
        _ => {
            return Err(clap_noun_verb::NounVerbError::execution_error(
                "Invalid choice".to_string(),
            ))
        }
    };

    println!("\n✓ Selected profile: {}", profile.as_str());
    Ok(profile)
}

fn configure_interactive(profile: WizardProfile) -> clap_noun_verb::Result<WizardConfig> {
    let mut config = WizardConfig {
        profile,
        ..Default::default()
    };

    // Project metadata
    println!("\n📋 Project Metadata");

    print!("Project name [{}]: ", config.metadata.name);
    io::stdout().flush().ok();
    let mut input = String::new();
    io::stdin().read_line(&mut input).ok();
    if !input.trim().is_empty() {
        config.metadata.name = input.trim().to_string();
    }

    print!("Version [{}]: ", config.metadata.version);
    io::stdout().flush().ok();
    input.clear();
    io::stdin().read_line(&mut input).ok();
    if !input.trim().is_empty() {
        config.metadata.version = input.trim().to_string();
    }

    print!("Description [{}]: ", config.metadata.description);
    io::stdout().flush().ok();
    input.clear();
    io::stdin().read_line(&mut input).ok();
    if !input.trim().is_empty() {
        config.metadata.description = input.trim().to_string();
    }

    println!("\n✓ Configuration complete");
    Ok(config)
}

// ============================================================================
// Scaffold Generation
// ============================================================================

fn perform_wizard(
    project_dir: &str, config: WizardConfig, skip_sync: bool,
) -> clap_noun_verb::Result<WizardOutput> {
    let base_path = Path::new(project_dir);

    // Ensure base directory exists
    fs::create_dir_all(base_path).map_err(|e| {
        clap_noun_verb::NounVerbError::execution_error(format!(
            "Failed to create project directory: {}",
            e
        ))
    })?;

    // Create file transaction for atomic operations
    let mut tx = FileTransaction::new().map_err(|e| {
        clap_noun_verb::NounVerbError::execution_error(format!(
            "Failed to initialize file transaction: {}",
            e
        ))
    })?;

    let mut directories_created = Vec::new();
    let mut files_created = Vec::new();

    // Create directory structure
    let sparql_world = format!("{}/world", config.sparql_dir);
    let sparql_receipts = format!("{}/receipts", config.sparql_dir);
    let templates_receipts = format!("{}/receipts", config.templates_dir);

    let dirs = vec![
        &config.specs_dir,
        &config.ontologies_dir,
        &sparql_world,
        &sparql_receipts,
        &templates_receipts,
        &config.output_dir,
    ];

    for dir in &dirs {
        let dir_path = base_path.join(dir);
        if !dir_path.exists() {
            fs::create_dir_all(&dir_path).map_err(|e| {
                clap_noun_verb::NounVerbError::execution_error(format!(
                    "Failed to create directory {}: {}",
                    dir, e
                ))
            })?;
            directories_created.push(dir.to_string());
        }
    }

    // Generate scaffold based on profile
    generate_scaffold(base_path, &config, &mut tx, &mut files_created)?;

    // Commit transaction
    let _receipt = tx.commit().map_err(|e| {
        clap_noun_verb::NounVerbError::execution_error(format!(
            "Failed to commit file transaction: {}",
            e
        ))
    })?;

    // Run initial sync if not skipped.
    //
    // This invokes the authoritative sync path — the SAME `SyncExecutor` the
    // `ggen sync` command drives — against the `ggen.toml` the wizard just wrote.
    // We only claim "Initial sync completed" when a real generation actually
    // succeeded. On failure we surface the error and report status "error":
    // never assert a state transition that did not happen.
    let mut next_steps = vec!["Run 'ggen sync' to generate initial outputs".to_string()];
    let mut sync_error: Option<String> = None;

    if !skip_sync {
        println!("\n⚙️  Running initial sync...");
        match run_initial_sync(base_path) {
            Ok(()) => {
                // A real sync emitted real artifacts — the claim is now TRUE.
                next_steps.insert(0, "Initial sync completed".to_string());
            }
            Err(e) => {
                // Do NOT claim "completed". Honestly report the failure and
                // tell the user how to retry the real command.
                next_steps.insert(
                    0,
                    "Initial sync did not run; run 'ggen sync' to generate outputs".to_string(),
                );
                sync_error = Some(e);
            }
        }
    }

    next_steps.push("Edit .specify/specs/project.ttl to customize your project".to_string());
    next_steps.push("Review world.manifest.json to see all outputs".to_string());
    next_steps.push("Run world.verify.mjs to validate outputs".to_string());

    let status = if sync_error.is_some() {
        "error".to_string()
    } else {
        "success".to_string()
    };

    Ok(WizardOutput {
        status,
        project_dir: project_dir.to_string(),
        profile: config.profile.as_str().to_string(),
        files_created,
        directories_created,
        error: sync_error,
        next_steps,
    })
}

/// Run the real initial sync over the freshly scaffolded `ggen.toml`.
///
/// Drives the authoritative `SyncExecutor` (the same engine behind `ggen sync`)
/// against `<base_path>/ggen.toml`. All paths inside the manifest are resolved
/// relative to the manifest's parent directory by the executor, so no working
/// directory change is required.
///
/// Returns `Ok(())` only when the executor reports `status == "success"`,
/// guaranteeing that the "Initial sync completed" claim corresponds to a real
/// state transition. Any executor error, or a non-success status, is surfaced
/// as `Err(message)` so the caller can refuse to claim completion.
fn run_initial_sync(base_path: &Path) -> std::result::Result<(), String> {
    let manifest_path = base_path.join("ggen.toml");

    let options = SyncOptions {
        manifest_path,
        ..SyncOptions::default()
    };

    let result = SyncExecutor::new(options)
        .execute()
        .map_err(|e| e.to_string())?;

    if result.status == "success" {
        Ok(())
    } else {
        Err(result
            .error
            .unwrap_or_else(|| format!("sync reported status '{}'", result.status)))
    }
}

fn generate_scaffold(
    base_path: &Path, config: &WizardConfig, tx: &mut FileTransaction,
    files_created: &mut Vec<String>,
) -> clap_noun_verb::Result<()> {
    // Generate ggen.toml
    let ggen_toml = generate_ggen_toml(config);
    let toml_path = base_path.join("ggen.toml");
    tx.write_file(&toml_path, &ggen_toml).map_err(|e| {
        clap_noun_verb::NounVerbError::execution_error(format!("Failed to write ggen.toml: {}", e))
    })?;
    files_created.push("ggen.toml".to_string());

    // Generate project.ttl
    let project_ttl = generate_project_ttl(config);
    let project_path = base_path.join(&config.specs_dir).join("project.ttl");
    tx.write_file(&project_path, &project_ttl).map_err(|e| {
        clap_noun_verb::NounVerbError::execution_error(format!(
            "Failed to write project.ttl: {}",
            e
        ))
    })?;
    files_created.push(format!("{}/project.ttl", config.specs_dir));

    // Profile-specific generation
    match config.profile {
        WizardProfile::LnCtrl | WizardProfile::LnCtrlOutputContracts => {
            generate_ln_ctrl_ontologies(base_path, config, tx, files_created)?;
            generate_ln_ctrl_sparql(base_path, config, tx, files_created)?;
            generate_ln_ctrl_templates(base_path, config, tx, files_created)?;
            generate_ln_ctrl_scripts(base_path, config, tx, files_created)?;
        }
        WizardProfile::McpA2a => {
            generate_mcp_a2a_configs(base_path, config, tx, files_created)?;
            // Also generate standard files for MCP/A2A projects
            generate_ontologies(base_path, config, tx, files_created)?;
            generate_sparql_queries(base_path, config, tx, files_created)?;
            generate_tera_templates(base_path, config, tx, files_created)?;
        }
        _ => {
            // Generate standard ontologies, queries, and templates
            generate_ontologies(base_path, config, tx, files_created)?;
            generate_sparql_queries(base_path, config, tx, files_created)?;
            generate_tera_templates(base_path, config, tx, files_created)?;
        }
    }

    // Generate README
    let readme = generate_readme(config);
    let readme_path = base_path.join("README.md");
    tx.write_file(&readme_path, &readme).map_err(|e| {
        clap_noun_verb::NounVerbError::execution_error(format!("Failed to write README.md: {}", e))
    })?;
    files_created.push("README.md".to_string());

    Ok(())
}

// ============================================================================
// Content Generators
// ============================================================================

fn generate_ggen_toml(config: &WizardConfig) -> String {
    let base_config = format!(
        r#"[project]
name = "{}"
version = "{}"
description = "{}"
authors = ["{}"]
license = "{}"

[ontology]
source = "{}/main.ttl"

[generation]
output_dir = "{}"

# World manifest generation
[[generation.rules]]
name = "world-manifest"
query = {{ file = "{}/world/outputs.sparql" }}
template = {{ file = "{}/world-manifest.tera" }}
output_file = "{}/world.manifest.json"
mode = "Overwrite"

# World verifier generation
[[generation.rules]]
name = "world-verifier"
query = {{ file = "{}/world/outputs.sparql" }}
template = {{ file = "{}/world-verify.tera" }}
output_file = "{}/world.verify.mjs"
mode = "Overwrite"

# Receipt schema generation
[[generation.rules]]
name = "receipt-schema"
query = {{ file = "{}/receipts/receipt_contract.sparql" }}
template = {{ file = "{}/receipts/receipt.schema.tera" }}
output_file = "{}/receipts/receipt.schema.json"
mode = "Overwrite"

# Verdict schema generation
[[generation.rules]]
name = "verdict-schema"
query = {{ file = "{}/receipts/receipt_contract.sparql" }}
template = {{ file = "{}/receipts/verdict.schema.tera" }}
output_file = "{}/receipts/verdict.schema.json"
mode = "Overwrite"
"#,
        config.metadata.name,
        config.metadata.version,
        config.metadata.description,
        config
            .metadata
            .authors
            .first()
            .unwrap_or(&"ggen wizard".to_string()),
        config.metadata.license,
        config.ontologies_dir,
        config.output_dir,
        config.sparql_dir,
        config.templates_dir,
        config.output_dir,
        config.sparql_dir,
        config.templates_dir,
        config.output_dir,
        config.sparql_dir,
        config.templates_dir,
        config.output_dir,
        config.sparql_dir,
        config.templates_dir,
        config.output_dir,
    );

    let ln_ctrl_rules = if matches!(config.profile, WizardProfile::LnCtrl) {
        format!(
            r#"
# ln_ctrl Receipt Schema Generation
[[generation.rules]]
name = "ln-ctrl-receipt-schema"
query = {{ file = "{}/ln_ctrl/receipt_trace.sparql" }}
template = {{ file = "{}/ln_ctrl/schemas/receipt.schema.json.tera" }}
output_file = "{}/ln_ctrl/schemas/receipt.schema.json"
mode = "Overwrite"

# ln_ctrl Golden Tests
[[generation.rules]]
name = "ln-ctrl-golden-tests"
query = {{ file = "{}/ln_ctrl/golden_tests.sparql" }}
template = {{ file = "{}/ln_ctrl/goldens/test.golden.json.tera" }}
output_file = "{}/ln_ctrl/goldens/{{{{ workflow_id }}}}.golden.json"
mode = "Overwrite"

# ln_ctrl Documentation
[[generation.rules]]
name = "ln-ctrl-docs"
query = {{ file = "{}/ln_ctrl/docs.sparql" }}
template = {{ file = "{}/ln_ctrl/docs/ln_ctrl.md.tera" }}
output_file = "{}/ln_ctrl/docs/ln_ctrl_guide.md"
mode = "Overwrite"

# ln_ctrl Kernel IR
[[generation.rules]]
name = "ln-ctrl-kernel-ir"
query = {{ file = "{}/ln_ctrl/kernel_ir.sparql" }}
template = {{ file = "{}/ln_ctrl/kernel/ir.json.tera" }}
output_file = "{}/ln_ctrl/kernel/execution.ir.json"
mode = "Overwrite"
"#,
            config.sparql_dir,
            config.templates_dir,
            config.output_dir,
            config.sparql_dir,
            config.templates_dir,
            config.output_dir,
            config.sparql_dir,
            config.templates_dir,
            config.output_dir,
            config.sparql_dir,
            config.templates_dir,
            config.output_dir,
        )
    } else {
        String::new()
    };

    let footer = format!(
        r#"
[sync]
enabled = true
on_change = "manual"
validate_after = true
conflict_mode = "fail"

[rdf]
formats = ["turtle"]
default_format = "turtle"
strict_validation = {}

[templates]
enable_caching = true
auto_reload = true

[output]
formatting = "default"
deterministic = {}
line_length = 100
indent = 2
"#,
        config.shacl_validation, config.deterministic_output,
    );

    format!("{}{}{}", base_config, ln_ctrl_rules, footer)
}

fn generate_project_ttl(config: &WizardConfig) -> String {
    format!(
        r#"@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix ggen: <https://ggen.io/marketplace/> .

ggen:Project a rdfs:Class ;
    rdfs:label "{}" ;
    rdfs:comment "{}" ;
    ggen:version "{}" ;
    ggen:profile "{}" .
"#,
        config.metadata.name,
        config.metadata.description,
        config.metadata.version,
        config.profile.as_str(),
    )
}

fn generate_ontologies(
    base_path: &Path, config: &WizardConfig, tx: &mut FileTransaction,
    files_created: &mut Vec<String>,
) -> clap_noun_verb::Result<()> {
    // Generate main.ttl
    let main_ttl = include_str!("../../templates/wizard/ontologies/main.ttl");
    let main_path = base_path.join(&config.ontologies_dir).join("main.ttl");
    tx.write_file(&main_path, main_ttl).map_err(|e| {
        clap_noun_verb::NounVerbError::execution_error(format!("Failed to write main.ttl: {}", e))
    })?;
    files_created.push(format!("{}/main.ttl", config.ontologies_dir));

    // Generate receipts.ttl
    let receipts_ttl = include_str!("../../templates/wizard/ontologies/receipts.ttl");
    let receipts_path = base_path.join(&config.ontologies_dir).join("receipts.ttl");
    tx.write_file(&receipts_path, receipts_ttl).map_err(|e| {
        clap_noun_verb::NounVerbError::execution_error(format!(
            "Failed to write receipts.ttl: {}",
            e
        ))
    })?;
    files_created.push(format!("{}/receipts.ttl", config.ontologies_dir));

    // Generate world.ttl
    let world_ttl = include_str!("../../templates/wizard/ontologies/world.ttl");
    let world_path = base_path.join(&config.ontologies_dir).join("world.ttl");
    tx.write_file(&world_path, world_ttl).map_err(|e| {
        clap_noun_verb::NounVerbError::execution_error(format!("Failed to write world.ttl: {}", e))
    })?;
    files_created.push(format!("{}/world.ttl", config.ontologies_dir));

    Ok(())
}

fn generate_sparql_queries(
    base_path: &Path, config: &WizardConfig, tx: &mut FileTransaction,
    files_created: &mut Vec<String>,
) -> clap_noun_verb::Result<()> {
    // Generate world outputs query
    let outputs_sparql = include_str!("../../templates/wizard/sparql/world/outputs.sparql");
    let outputs_path = base_path
        .join(&config.sparql_dir)
        .join("world")
        .join("outputs.sparql");
    tx.write_file(&outputs_path, outputs_sparql).map_err(|e| {
        clap_noun_verb::NounVerbError::execution_error(format!(
            "Failed to write outputs.sparql: {}",
            e
        ))
    })?;
    files_created.push(format!("{}/world/outputs.sparql", config.sparql_dir));

    // Generate receipt contract query
    let receipt_sparql =
        include_str!("../../templates/wizard/sparql/receipts/receipt_contract.sparql");
    let receipt_path = base_path
        .join(&config.sparql_dir)
        .join("receipts")
        .join("receipt_contract.sparql");
    tx.write_file(&receipt_path, receipt_sparql).map_err(|e| {
        clap_noun_verb::NounVerbError::execution_error(format!(
            "Failed to write receipt_contract.sparql: {}",
            e
        ))
    })?;
    files_created.push(format!(
        "{}/receipts/receipt_contract.sparql",
        config.sparql_dir
    ));

    Ok(())
}

fn generate_tera_templates(
    base_path: &Path, config: &WizardConfig, tx: &mut FileTransaction,
    files_created: &mut Vec<String>,
) -> clap_noun_verb::Result<()> {
    // Generate world manifest template
    let manifest_tera = include_str!("../../templates/wizard/tera/world-manifest.tera");
    let manifest_path = base_path
        .join(&config.templates_dir)
        .join("world-manifest.tera");
    tx.write_file(&manifest_path, manifest_tera).map_err(|e| {
        clap_noun_verb::NounVerbError::execution_error(format!(
            "Failed to write world-manifest.tera: {}",
            e
        ))
    })?;
    files_created.push(format!("{}/world-manifest.tera", config.templates_dir));

    // Generate world verifier template
    let verifier_tera = include_str!("../../templates/wizard/tera/world-verify.tera");
    let verifier_path = base_path
        .join(&config.templates_dir)
        .join("world-verify.tera");
    tx.write_file(&verifier_path, verifier_tera).map_err(|e| {
        clap_noun_verb::NounVerbError::execution_error(format!(
            "Failed to write world-verify.tera: {}",
            e
        ))
    })?;
    files_created.push(format!("{}/world-verify.tera", config.templates_dir));

    // Generate receipt schema template
    let receipt_schema_tera =
        include_str!("../../templates/wizard/tera/receipts/receipt.schema.tera");
    let receipt_schema_path = base_path
        .join(&config.templates_dir)
        .join("receipts")
        .join("receipt.schema.tera");
    tx.write_file(&receipt_schema_path, receipt_schema_tera)
        .map_err(|e| {
            clap_noun_verb::NounVerbError::execution_error(format!(
                "Failed to write receipt.schema.tera: {}",
                e
            ))
        })?;
    files_created.push(format!(
        "{}/receipts/receipt.schema.tera",
        config.templates_dir
    ));

    // Generate verdict schema template
    let verdict_schema_tera =
        include_str!("../../templates/wizard/tera/receipts/verdict.schema.tera");
    let verdict_schema_path = base_path
        .join(&config.templates_dir)
        .join("receipts")
        .join("verdict.schema.tera");
    tx.write_file(&verdict_schema_path, verdict_schema_tera)
        .map_err(|e| {
            clap_noun_verb::NounVerbError::execution_error(format!(
                "Failed to write verdict.schema.tera: {}",
                e
            ))
        })?;
    files_created.push(format!(
        "{}/receipts/verdict.schema.tera",
        config.templates_dir
    ));

    Ok(())
}

fn generate_ln_ctrl_ontologies(
    base_path: &Path, config: &WizardConfig, tx: &mut FileTransaction,
    files_created: &mut Vec<String>,
) -> clap_noun_verb::Result<()> {
    // Generate ln_ctrl_receipts.ttl
    let ln_ctrl_receipts_ttl =
        include_str!("../../templates/wizard/ln_ctrl/ontologies/ln_ctrl_receipts.ttl");
    let receipts_path = base_path
        .join(&config.ontologies_dir)
        .join("ln_ctrl_receipts.ttl");
    tx.write_file(&receipts_path, ln_ctrl_receipts_ttl)
        .map_err(|e| {
            clap_noun_verb::NounVerbError::execution_error(format!(
                "Failed to write ln_ctrl_receipts.ttl: {}",
                e
            ))
        })?;
    files_created.push(format!("{}/ln_ctrl_receipts.ttl", config.ontologies_dir));

    Ok(())
}

fn generate_ln_ctrl_sparql(
    base_path: &Path, config: &WizardConfig, tx: &mut FileTransaction,
    files_created: &mut Vec<String>,
) -> clap_noun_verb::Result<()> {
    // Create ln_ctrl SPARQL directory
    let ln_ctrl_sparql_dir = base_path.join(&config.sparql_dir).join("ln_ctrl");
    fs::create_dir_all(&ln_ctrl_sparql_dir).map_err(|e| {
        clap_noun_verb::NounVerbError::execution_error(format!(
            "Failed to create ln_ctrl SPARQL directory: {}",
            e
        ))
    })?;

    // Generate receipt_trace.sparql
    let receipt_trace_sparql = r"PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
PREFIX ln_ctrl: <https://ggen.io/ontology/ln_ctrl#>

SELECT ?receipt ?timestamp ?operation ?workflow_id ?step_index
       ?causal_parent ?hash_chain ?redex_type ?redex_expression
       ?frontier_size ?frontier_hash ?budget_steps ?budget_memory
WHERE {
  ?receipt a ln_ctrl:Receipt ;
           ln_ctrl:timestamp ?timestamp ;
           ln_ctrl:operation ?operation ;
           ln_ctrl:workflow_id ?workflow_id ;
           ln_ctrl:step_index ?step_index ;
           ln_ctrl:hash_chain ?hash_chain .

  OPTIONAL { ?receipt ln_ctrl:causal_parent ?causal_parent }

  ?receipt ln_ctrl:redex_executed ?redex .
  ?redex ln_ctrl:redex_type ?redex_type ;
         ln_ctrl:redex_expression ?redex_expression .

  ?receipt ln_ctrl:frontier_after ?frontier .
  ?frontier ln_ctrl:frontier_size ?frontier_size ;
            ln_ctrl:frontier_hash ?frontier_hash .

  ?receipt ln_ctrl:budget_remaining ?budget .
  ?budget ln_ctrl:budget_steps ?budget_steps ;
          ln_ctrl:budget_memory ?budget_memory .
}
ORDER BY ?workflow_id ?step_index
";
    let receipt_trace_path = ln_ctrl_sparql_dir.join("receipt_trace.sparql");
    tx.write_file(&receipt_trace_path, receipt_trace_sparql)
        .map_err(|e| {
            clap_noun_verb::NounVerbError::execution_error(format!(
                "Failed to write receipt_trace.sparql: {}",
                e
            ))
        })?;
    files_created.push(format!(
        "{}/ln_ctrl/receipt_trace.sparql",
        config.sparql_dir
    ));

    // Generate golden_tests.sparql
    let golden_tests_sparql = r"PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
PREFIX ln_ctrl: <https://ggen.io/ontology/ln_ctrl#>

SELECT ?workflow_id ?receipts_count ?total_steps ?final_frontier_hash
WHERE {
  {
    SELECT ?workflow_id (COUNT(?receipt) AS ?receipts_count) (MAX(?step_index) AS ?total_steps)
    WHERE {
      ?receipt a ln_ctrl:Receipt ;
               ln_ctrl:workflow_id ?workflow_id ;
               ln_ctrl:step_index ?step_index .
    }
    GROUP BY ?workflow_id
  }

  ?final_receipt a ln_ctrl:Receipt ;
                 ln_ctrl:workflow_id ?workflow_id ;
                 ln_ctrl:step_index ?total_steps ;
                 ln_ctrl:frontier_after ?frontier .
  ?frontier ln_ctrl:frontier_hash ?final_frontier_hash .
}
ORDER BY ?workflow_id
";
    let golden_tests_path = ln_ctrl_sparql_dir.join("golden_tests.sparql");
    tx.write_file(&golden_tests_path, golden_tests_sparql)
        .map_err(|e| {
            clap_noun_verb::NounVerbError::execution_error(format!(
                "Failed to write golden_tests.sparql: {}",
                e
            ))
        })?;
    files_created.push(format!("{}/ln_ctrl/golden_tests.sparql", config.sparql_dir));

    // Generate docs.sparql
    let docs_sparql = r#"PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
PREFIX ln_ctrl: <https://ggen.io/ontology/ln_ctrl#>

SELECT ?class ?label ?comment ?property ?property_label ?property_comment
WHERE {
  {
    ?class a rdfs:Class ;
           rdfs:label ?label ;
           rdfs:comment ?comment .
    FILTER(STRSTARTS(STR(?class), "https://ggen.io/ontology/ln_ctrl#"))
  }
  UNION
  {
    ?property a rdf:Property ;
              rdfs:label ?property_label ;
              rdfs:comment ?property_comment .
    FILTER(STRSTARTS(STR(?property), "https://ggen.io/ontology/ln_ctrl#"))
  }
}
ORDER BY ?class ?property
"#;
    let docs_path = ln_ctrl_sparql_dir.join("docs.sparql");
    tx.write_file(&docs_path, docs_sparql).map_err(|e| {
        clap_noun_verb::NounVerbError::execution_error(format!(
            "Failed to write docs.sparql: {}",
            e
        ))
    })?;
    files_created.push(format!("{}/ln_ctrl/docs.sparql", config.sparql_dir));

    // Generate kernel_ir.sparql
    let kernel_ir_sparql = r#"PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
PREFIX ln_ctrl: <https://ggen.io/ontology/ln_ctrl#>

SELECT ?receipt ?timestamp ?operation ?redex_type ?redex_expression
       ?frontier_terms ?effects ?budget_steps
WHERE {
  ?receipt a ln_ctrl:Receipt ;
           ln_ctrl:timestamp ?timestamp ;
           ln_ctrl:operation ?operation ;
           ln_ctrl:redex_executed ?redex ;
           ln_ctrl:frontier_after ?frontier ;
           ln_ctrl:budget_remaining ?budget .

  ?redex ln_ctrl:redex_type ?redex_type ;
         ln_ctrl:redex_expression ?redex_expression .

  ?frontier ln_ctrl:frontier_terms ?frontier_terms .

  ?budget ln_ctrl:budget_steps ?budget_steps .

  OPTIONAL {
    ?receipt ln_ctrl:effects_performed ?effect .
    ?effect ln_ctrl:effect_type ?effect_type ;
            ln_ctrl:effect_data ?effect_data .
    BIND(CONCAT(?effect_type, ":", ?effect_data) AS ?effects)
  }
}
ORDER BY ?timestamp
"#;
    let kernel_ir_path = ln_ctrl_sparql_dir.join("kernel_ir.sparql");
    tx.write_file(&kernel_ir_path, kernel_ir_sparql)
        .map_err(|e| {
            clap_noun_verb::NounVerbError::execution_error(format!(
                "Failed to write kernel_ir.sparql: {}",
                e
            ))
        })?;
    files_created.push(format!("{}/ln_ctrl/kernel_ir.sparql", config.sparql_dir));

    Ok(())
}

fn generate_ln_ctrl_templates(
    base_path: &Path, config: &WizardConfig, tx: &mut FileTransaction,
    files_created: &mut Vec<String>,
) -> clap_noun_verb::Result<()> {
    // Create ln_ctrl template directories
    let ln_ctrl_schemas_dir = base_path
        .join(&config.templates_dir)
        .join("ln_ctrl")
        .join("schemas");
    fs::create_dir_all(&ln_ctrl_schemas_dir).map_err(|e| {
        clap_noun_verb::NounVerbError::execution_error(format!(
            "Failed to create ln_ctrl schemas directory: {}",
            e
        ))
    })?;

    let ln_ctrl_goldens_dir = base_path
        .join(&config.templates_dir)
        .join("ln_ctrl")
        .join("goldens");
    fs::create_dir_all(&ln_ctrl_goldens_dir).map_err(|e| {
        clap_noun_verb::NounVerbError::execution_error(format!(
            "Failed to create ln_ctrl goldens directory: {}",
            e
        ))
    })?;

    let ln_ctrl_docs_dir = base_path
        .join(&config.templates_dir)
        .join("ln_ctrl")
        .join("docs");
    fs::create_dir_all(&ln_ctrl_docs_dir).map_err(|e| {
        clap_noun_verb::NounVerbError::execution_error(format!(
            "Failed to create ln_ctrl docs directory: {}",
            e
        ))
    })?;

    let ln_ctrl_kernel_dir = base_path
        .join(&config.templates_dir)
        .join("ln_ctrl")
        .join("kernel");
    fs::create_dir_all(&ln_ctrl_kernel_dir).map_err(|e| {
        clap_noun_verb::NounVerbError::execution_error(format!(
            "Failed to create ln_ctrl kernel directory: {}",
            e
        ))
    })?;

    // Generate receipt schema template
    let receipt_schema_tera =
        include_str!("../../templates/wizard/ln_ctrl/templates/schemas/receipt.schema.json.tera");
    let receipt_schema_path = ln_ctrl_schemas_dir.join("receipt.schema.json.tera");
    tx.write_file(&receipt_schema_path, receipt_schema_tera)
        .map_err(|e| {
            clap_noun_verb::NounVerbError::execution_error(format!(
                "Failed to write receipt.schema.json.tera: {}",
                e
            ))
        })?;
    files_created.push(format!(
        "{}/ln_ctrl/schemas/receipt.schema.json.tera",
        config.templates_dir
    ));

    // Generate golden test template
    let golden_test_tera = r#"{
  "workflow_id": "{{ workflow_id }}",
  "total_receipts": {{ receipts_count }},
  "total_steps": {{ total_steps }},
  "final_frontier_hash": "{{ final_frontier_hash }}",
  "generated_at": "{{ now() | date(format='%Y-%m-%dT%H:%M:%SZ') }}",
  "deterministic": true
}
"#;
    let golden_test_path = ln_ctrl_goldens_dir.join("test.golden.json.tera");
    tx.write_file(&golden_test_path, golden_test_tera)
        .map_err(|e| {
            clap_noun_verb::NounVerbError::execution_error(format!(
                "Failed to write test.golden.json.tera: {}",
                e
            ))
        })?;
    files_created.push(format!(
        "{}/ln_ctrl/goldens/test.golden.json.tera",
        config.templates_dir
    ));

    // Generate docs template
    let docs_tera = r"# ln_ctrl Ontology Documentation

Generated: {{ now() | date(format='%Y-%m-%d %H:%M:%S UTC') }}

## Classes

{% for result in results -%}
{% if result.class -%}
### {{ result.label }}

**URI**: `{{ result.class }}`

{{ result.comment }}

{% endif -%}
{% endfor %}

## Properties

{% for result in results -%}
{% if result.property -%}
### {{ result.property_label }}

**URI**: `{{ result.property }}`

{{ result.property_comment }}

{% endif -%}
{% endfor %}

---

*This documentation is generated from the ln_ctrl RDF ontology.*
";
    let docs_path = ln_ctrl_docs_dir.join("ln_ctrl.md.tera");
    tx.write_file(&docs_path, docs_tera).map_err(|e| {
        clap_noun_verb::NounVerbError::execution_error(format!(
            "Failed to write ln_ctrl.md.tera: {}",
            e
        ))
    })?;
    files_created.push(format!(
        "{}/ln_ctrl/docs/ln_ctrl.md.tera",
        config.templates_dir
    ));

    // Generate kernel IR template
    let kernel_ir_tera = r#"{
  "version": "1.0",
  "generated_at": "{{ now() | date(format='%Y-%m-%dT%H:%M:%SZ') }}",
  "execution_trace": [
    {% for result in results -%}
    {
      "receipt_id": "{{ result.receipt }}",
      "timestamp": "{{ result.timestamp }}",
      "operation": "{{ result.operation }}",
      "redex": {
        "type": "{{ result.redex_type }}",
        "expression": "{{ result.redex_expression }}"
      },
      "frontier_terms": {{ result.frontier_terms | json_encode }},
      "effects": [{{ result.effects | default(value="") }}],
      "budget_remaining": {
        "steps": {{ result.budget_steps }}
      }
    }{% if not loop.last %},{% endif %}
    {% endfor -%}
  ]
}
"#;
    let kernel_ir_path = ln_ctrl_kernel_dir.join("ir.json.tera");
    tx.write_file(&kernel_ir_path, kernel_ir_tera)
        .map_err(|e| {
            clap_noun_verb::NounVerbError::execution_error(format!(
                "Failed to write ir.json.tera: {}",
                e
            ))
        })?;
    files_created.push(format!(
        "{}/ln_ctrl/kernel/ir.json.tera",
        config.templates_dir
    ));

    Ok(())
}

fn generate_ln_ctrl_scripts(
    base_path: &Path, config: &WizardConfig, tx: &mut FileTransaction,
    files_created: &mut Vec<String>,
) -> clap_noun_verb::Result<()> {
    let scripts_dir = base_path.join("scripts");

    // Generate validate.sh — runs quality gates
    let validate_sh = format!(
        "#!/usr/bin/env bash\n\
        set -euo pipefail\n\
        # Quality gate validation for {}\n\
        echo \"Running quality gates for {}...\"\n\
        cargo make check && cargo make lint && cargo make test\n\
        echo \"All gates passed\"\n",
        config.metadata.name, config.metadata.name
    );
    tx.write_file(scripts_dir.join("validate.sh"), &validate_sh)
        .map_err(|e| {
            clap_noun_verb::NounVerbError::execution_error(format!(
                "Failed to write validate.sh: {}",
                e
            ))
        })?;
    files_created.push("scripts/validate.sh".to_string());

    // Generate ci.sh — CI pipeline stub
    let ci_sh = format!(
        "#!/usr/bin/env bash\n\
        set -euo pipefail\n\
        # CI pipeline for {}\n\
        cargo make pre-commit\n\
        cargo make audit\n",
        config.metadata.name
    );
    tx.write_file(scripts_dir.join("ci.sh"), &ci_sh)
        .map_err(|e| {
            clap_noun_verb::NounVerbError::execution_error(format!("Failed to write ci.sh: {}", e))
        })?;
    files_created.push("scripts/ci.sh".to_string());

    Ok(())
}

fn generate_mcp_a2a_configs(
    base_path: &Path, config: &WizardConfig, tx: &mut FileTransaction,
    files_created: &mut Vec<String>,
) -> clap_noun_verb::Result<()> {
    // Constants for config file names
    const PROJECT_MCP_CONFIG: &str = ".mcp.json";
    const PROJECT_A2A_CONFIG: &str = "a2a.toml";

    // Generate .mcp.json configuration
    let mcp_json = format!(
        r#"{{
  "mcpServers": {{
    "ggen": {{
      "command": "ggen",
      "args": ["mcp", "start-server", "--transport", "stdio"],
      "env": {{
        "GGEN_LOG_LEVEL": "info"
      }}
    }}
  }},
  "description": "MCP servers for ggen project",
  "version": "1.0.0",
  "metadata": {{
    "project": "{}",
    "profile": "mcp-a2a"
  }}
}}
"#,
        config.metadata.name
    );

    let mcp_path = base_path.join(PROJECT_MCP_CONFIG);
    tx.write_file(&mcp_path, &mcp_json).map_err(|e| {
        clap_noun_verb::NounVerbError::execution_error(format!("Failed to write .mcp.json: {}", e))
    })?;
    files_created.push(PROJECT_MCP_CONFIG.to_string());

    // Generate a2a.toml configuration
    let a2a_toml = format!(
        r#"[server]
host = "127.0.0.1"
port = 8080
timeout = 30
max_connections = 100

[metadata]
version = "{}"
environment = "development"

[[agents]]
name = "{}"
agent_type = "mcp-bridge"
enabled = true
description = "{}"

[workflows]
# Define your A2A workflows here
"#,
        config.metadata.version, config.metadata.name, config.metadata.description
    );

    let a2a_path = base_path.join(PROJECT_A2A_CONFIG);
    tx.write_file(&a2a_path, &a2a_toml).map_err(|e| {
        clap_noun_verb::NounVerbError::execution_error(format!("Failed to write a2a.toml: {}", e))
    })?;
    files_created.push(PROJECT_A2A_CONFIG.to_string());

    Ok(())
}

fn generate_readme(config: &WizardConfig) -> String {
    format!(
        r"# {}

{}

Generated by `ggen wizard` with profile: **{}**

## Quick Start

```bash
# Generate all outputs
ggen sync

# Validate outputs
node world.verify.mjs

# View world manifest
cat world.manifest.json
```

## Project Structure

```
.
├── ggen.toml                           # ggen configuration
├── README.md                            # This file
├── {}/                    # RDF specifications
│   └── project.ttl                     # Project metadata
├── {}/                # RDF ontologies
│   ├── main.ttl                        # Main ontology
│   ├── receipts.ttl                    # Receipt schemas
│   └── world.ttl                       # World manifest definition
├── {}/                         # SPARQL queries
│   ├── world/
│   │   └── outputs.sparql              # Query for world outputs
│   └── receipts/
│       └── receipt_contract.sparql     # Query for receipt contracts
├── {}/                     # Tera templates
│   ├── world-manifest.tera             # World manifest template
│   ├── world-verify.tera               # World verifier template
│   └── receipts/
│       ├── receipt.schema.tera         # Receipt schema template
│       └── verdict.schema.tera         # Verdict schema template
└── {}/                       # Generated outputs
    ├── world.manifest.json             # World manifest
    ├── world.verify.mjs                # World verifier
    └── receipts/
        ├── receipt.schema.json         # Receipt schema
        └── verdict.schema.json         # Verdict schema
```

## Commands

```bash
# Generate code from ontology
ggen sync

# Dry-run: preview changes without writing
ggen sync --dry-run

# Watch mode: regenerate on file changes
ggen sync --watch

# Validate without generating
ggen sync --validate-only
```

## Determinism

This project is configured for deterministic output:

- ✅ Stable ordering enforced in SPARQL queries
- ✅ Canonical JSON/YAML rendering
- ✅ Strict template variables (no silent missing)
- ✅ SHACL validation enabled
- ✅ World manifest tracks all outputs with hashes

## Learn More

- [ggen Documentation](https://docs.ggen.io)
- [RDF/Turtle Syntax](https://www.w3.org/TR/turtle/)
- [SPARQL Query Language](https://www.w3.org/TR/sparql11-query/)
- [Tera Template Language](https://keats.github.io/tera/)

---

*Profile: {} | Version: {}*
",
        config.metadata.name,
        config.metadata.description,
        config.profile.as_str(),
        config.specs_dir,
        config.ontologies_dir,
        config.sparql_dir,
        config.templates_dir,
        config.output_dir,
        config.profile.as_str(),
        config.metadata.version,
    )
}

// ============================================================================
// Tests
// ============================================================================

#[cfg(test)]
mod tests {
    use super::*;
    use tempfile::tempdir;

    #[test]
    fn test_wizard_profile_parsing() {
        assert_eq!(
            WizardProfile::from_str("receipts-first").unwrap(),
            WizardProfile::ReceiptsFirst
        );
        assert_eq!(
            WizardProfile::from_str("c4-diagrams").unwrap(),
            WizardProfile::C4Diagrams
        );
        assert_eq!(
            WizardProfile::from_str("ln-ctrl").unwrap(),
            WizardProfile::LnCtrl
        );
        assert!(WizardProfile::from_str("invalid").is_err());
    }

    #[test]
    fn test_wizard_default_config() {
        let config = WizardConfig::default();
        assert_eq!(config.profile, WizardProfile::ReceiptsFirst);
        assert!(config.deterministic_output);
        assert!(config.strict_template_variables);
        assert!(config.shacl_validation);
        assert!(config.generate_world_manifest);
        assert!(config.generate_world_verifier);
    }

    #[test]
    fn test_wizard_scaffold_creation() {
        let temp_dir = tempdir().expect("Failed to create temp dir");
        let project_path = temp_dir.path().to_str().expect("Invalid path");

        let config = WizardConfig::default();
        let result = perform_wizard(project_path, config, true).expect("Wizard should succeed");

        assert_eq!(result.status, "success");
        assert_eq!(result.profile, "receipts-first");
        assert!(!result.files_created.is_empty());
        assert!(!result.directories_created.is_empty());
        assert!(result.error.is_none());

        // Verify files exist
        let base = temp_dir.path();
        assert!(base.join("ggen.toml").exists());
        assert!(base.join("README.md").exists());
        assert!(base.join(".specify/specs/project.ttl").exists());
    }

    #[test]
    fn test_generate_ggen_toml() {
        let config = WizardConfig::default();
        let toml = generate_ggen_toml(&config);

        assert!(toml.contains("[project]"));
        assert!(toml.contains("name = \"my-ggen-project\""));
        assert!(toml.contains("world-manifest"));
        assert!(toml.contains("receipt-schema"));
    }

    #[test]
    fn test_generate_project_ttl() {
        let config = WizardConfig::default();
        let ttl = generate_project_ttl(&config);

        assert!(ttl.contains("@prefix ggen:"));
        assert!(ttl.contains("ggen:Project"));
        assert!(ttl.contains("receipts-first"));
    }

    #[test]
    fn test_wizard_output_serialization() {
        let output = WizardOutput {
            status: "success".to_string(),
            project_dir: "/tmp/test".to_string(),
            profile: "receipts-first".to_string(),
            files_created: vec!["ggen.toml".to_string()],
            directories_created: vec![".specify".to_string()],
            error: None,
            next_steps: vec!["Run ggen sync".to_string()],
        };

        let json = serde_json::to_string(&output).expect("Should serialize");
        assert!(json.contains("\"status\":\"success\""));
        assert!(json.contains("\"profile\":\"receipts-first\""));
    }

    #[test]
    fn test_ln_ctrl_profile_parsing() {
        assert_eq!(
            WizardProfile::from_str("ln-ctrl").unwrap(),
            WizardProfile::LnCtrl
        );
        assert_eq!(WizardProfile::LnCtrl.as_str(), "ln-ctrl");
        assert!(WizardProfile::LnCtrl.description().contains("λn execution"));
    }

    #[test]
    fn test_ln_ctrl_scaffold_creation() {
        let temp_dir = tempdir().expect("Failed to create temp dir");
        let project_path = temp_dir.path().to_str().expect("Invalid path");

        let config = WizardConfig {
            profile: WizardProfile::LnCtrl,
            ..Default::default()
        };
        let result = perform_wizard(project_path, config, true).expect("Wizard should succeed");

        assert_eq!(result.status, "success");
        assert_eq!(result.profile, "ln-ctrl");
        assert!(!result.files_created.is_empty());
        assert!(result.error.is_none());

        // Verify ln_ctrl-specific files exist
        let base = temp_dir.path();
        assert!(base.join("ggen.toml").exists());
        assert!(base
            .join(".specify/ontologies/ln_ctrl_receipts.ttl")
            .exists());
    }

    #[test]
    fn test_ln_ctrl_ggen_toml_generation() {
        let config = WizardConfig {
            profile: WizardProfile::LnCtrl,
            ..Default::default()
        };
        let toml = generate_ggen_toml(&config);

        assert!(toml.contains("[project]"));
        assert!(toml.contains("ln-ctrl-receipt-schema"));
        assert!(toml.contains("ln-ctrl-golden-tests"));
        assert!(toml.contains("ln-ctrl-docs"));
        assert!(toml.contains("ln-ctrl-kernel-ir"));
    }

    #[test]
    fn test_mcp_a2a_profile_parsing() {
        assert_eq!(
            WizardProfile::from_str("mcp-a2a").unwrap(),
            WizardProfile::McpA2a
        );
        assert_eq!(WizardProfile::McpA2a.as_str(), "mcp-a2a");
        assert!(WizardProfile::McpA2a.description().contains("MCP"));
        assert!(WizardProfile::McpA2a.description().contains("A2A"));
    }

    /// Positive: with sync ENABLED (skip_sync = false), the wizard must run the
    /// real `SyncExecutor` against the scaffolded `ggen.toml` and produce real
    /// generated output. We assert the observable filesystem state (the world
    /// manifest the manifest declares) actually exists — proving a real state
    /// transition happened, not a printed claim.
    #[test]
    fn test_wizard_initial_sync_produces_real_output() {
        let temp_dir = tempdir().expect("Failed to create temp dir");
        let project_path = temp_dir.path().to_str().expect("Invalid path");

        let config = WizardConfig::default();
        // skip_sync = false → the real sync must run.
        let result =
            perform_wizard(project_path, config, false).expect("Wizard should not hard-error");

        // The claim and the world must agree: either sync succeeded AND the
        // declared output artifact exists, or sync failed AND we did NOT claim
        // completion. There is no honest third state.
        let base = temp_dir.path();
        let claimed_completed = result
            .next_steps
            .iter()
            .any(|s| s == "Initial sync completed");
        let manifest_output_exists = base.join("world.manifest.json").exists();

        if claimed_completed {
            // INVARIANT: a "completed" claim REQUIRES a real generated artifact.
            assert_eq!(
                result.status, "success",
                "claimed sync completion but status was not success"
            );
            assert!(
                manifest_output_exists,
                "claimed 'Initial sync completed' but world.manifest.json was never generated — \
                 this is the decorative-completion lie"
            );
            assert!(result.error.is_none(), "success must carry no error");
        } else {
            // If sync did not complete, status must be error and an error must
            // be surfaced — never a silent fake-success.
            assert_eq!(
                result.status, "error",
                "sync did not complete yet status was not 'error'"
            );
            assert!(
                result.error.is_some(),
                "sync did not complete but no error was surfaced"
            );
        }
    }

    /// Negative / sabotage: this is the loud-fail guard. The output must NEVER
    /// contain a "completed" sync claim unless a sync actually occurred.
    ///
    /// With skip_sync = true the wizard runs NO sync, so the phrase "Initial
    /// sync completed" must be absent. If a future change re-introduces the
    /// decorative-completion lie (pushing the claim without running sync), this
    /// test fails loudly.
    #[test]
    fn test_wizard_skip_sync_never_claims_completion() {
        let temp_dir = tempdir().expect("Failed to create temp dir");
        let project_path = temp_dir.path().to_str().expect("Invalid path");

        let config = WizardConfig::default();
        // skip_sync = true → NO sync runs.
        let result = perform_wizard(project_path, config, true).expect("Wizard should succeed");

        // No sync ran, so no generated artifact may exist...
        let base = temp_dir.path();
        assert!(
            !base.join("world.manifest.json").exists(),
            "no sync was requested yet a sync artifact appeared"
        );

        // ...and the output must NOT claim a sync was completed.
        assert!(
            !result.next_steps.iter().any(|s| s.contains("completed")),
            "skip_sync was set but output falsely claims a sync 'completed': {:?}",
            result.next_steps
        );
    }

    #[test]
    fn test_mcp_a2a_scaffold_creation() {
        let temp_dir = tempdir().expect("Failed to create temp dir");
        let project_path = temp_dir.path().to_str().expect("Invalid path");

        let config = WizardConfig {
            profile: WizardProfile::McpA2a,
            ..Default::default()
        };
        let result = perform_wizard(project_path, config, true).expect("Wizard should succeed");

        assert_eq!(result.status, "success");
        assert_eq!(result.profile, "mcp-a2a");
        assert!(!result.files_created.is_empty());
        assert!(result.error.is_none());

        // Verify MCP/A2A files exist
        let base = temp_dir.path();
        assert!(base.join(".mcp.json").exists(), ".mcp.json should exist");
        assert!(base.join("a2a.toml").exists(), "a2a.toml should exist");
    }
}
