o7 0.1.1

O7 workflow DSL runner
Documentation
//! Wizard flow builder — produces the 5-question wizard flow.

use crate::harness::HarnessConfig;
use crate::wiz::global_config::GlobalConfig;
use crate::wiz::types::{WizQuestion, WizSection};

/// Build the 5-question wizard flow, pre-filling from existing config.
pub fn build_wiz_flow(
    existing_harnesses: Option<&HarnessConfig>,
    existing_global: Option<&GlobalConfig>,
) -> Vec<WizQuestion> {
    let first = existing_harnesses.and_then(|h| h.harness.iter().next());
    let (existing_name, existing_entry) = match first {
        Some((name, entry)) => (name.as_str(), Some(entry)),
        None => ("", None),
    };

    vec![
        WizQuestion {
            id: "harness-name".to_string(),
            prompt: r#"Harness name (e.g. "claude")"#.to_string(),
            section: WizSection::Harness,
            prefill: existing_name.to_string(),
        },
        WizQuestion {
            id: "harness-command".to_string(),
            prompt: r#"Command to run (e.g. "claude")"#.to_string(),
            section: WizSection::Harness,
            prefill: existing_entry
                .map(|e| e.command.clone())
                .unwrap_or_default(),
        },
        WizQuestion {
            id: "harness-prompt-slot".to_string(),
            prompt: r#"Prompt flag, e.g. "--prompt" (leave blank to omit)"#.to_string(),
            section: WizSection::Harness,
            prefill: existing_entry
                .and_then(|e| e.prompt_slot.clone())
                .unwrap_or_default(),
        },
        WizQuestion {
            id: "global-default-project-root".to_string(),
            prompt: "Default project root (leave blank to omit)".to_string(),
            section: WizSection::Global,
            prefill: existing_global
                .and_then(|g| g.default_project_root.clone())
                .unwrap_or_default(),
        },
        WizQuestion {
            id: "global-default-harness".to_string(),
            prompt: "Default harness name (leave blank to omit)".to_string(),
            section: WizSection::Global,
            prefill: existing_global
                .and_then(|g| g.default_harness.clone())
                .unwrap_or_default(),
        },
    ]
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::wiz::global_config::GlobalConfig;
    use std::collections::HashMap;

    fn make_harness_config(
        name: &str,
        command: &str,
        prompt_slot: Option<&str>,
    ) -> crate::harness::HarnessConfig {
        let mut harness_map = HashMap::new();
        harness_map.insert(
            name.to_string(),
            crate::harness::HarnessEntry {
                command: command.to_string(),
                prompt_slot: prompt_slot.map(|s| s.to_string()),
                args_mapping: "flags".to_string(),
                output_mode: None,
                defaults: None,
            },
        );
        crate::harness::HarnessConfig {
            harness: harness_map,
        }
    }

    #[test]
    fn test_fresh_setup_produces_five_questions() {
        let flow = build_wiz_flow(None, None);
        assert_eq!(flow.len(), 5);
    }

    #[test]
    fn test_fresh_setup_all_prefills_empty() {
        let flow = build_wiz_flow(None, None);
        for q in &flow {
            assert!(q.prefill.is_empty(), "expected empty prefill for {}", q.id);
        }
    }

    #[test]
    fn test_question_ids_in_order() {
        let flow = build_wiz_flow(None, None);
        let ids: Vec<&str> = flow.iter().map(|q| q.id.as_str()).collect();
        assert_eq!(
            ids,
            [
                "harness-name",
                "harness-command",
                "harness-prompt-slot",
                "global-default-project-root",
                "global-default-harness",
            ]
        );
    }

    #[test]
    fn test_existing_harness_prefills_name_and_command() {
        let config = make_harness_config("claude", "claude", Some("--prompt"));
        let flow = build_wiz_flow(Some(&config), None);

        let name_q = flow.iter().find(|q| q.id == "harness-name").unwrap();
        let cmd_q = flow.iter().find(|q| q.id == "harness-command").unwrap();
        let slot_q = flow.iter().find(|q| q.id == "harness-prompt-slot").unwrap();
        assert_eq!(name_q.prefill, "claude");
        assert_eq!(cmd_q.prefill, "claude");
        assert_eq!(slot_q.prefill, "--prompt");
    }

    #[test]
    fn test_existing_global_config_prefills_global_questions() {
        let global = GlobalConfig {
            default_harness: Some("claude".to_string()),
            default_project_root: Some("/tmp/proj".to_string()),
        };
        let flow = build_wiz_flow(None, Some(&global));
        let root_q = flow
            .iter()
            .find(|q| q.id == "global-default-project-root")
            .unwrap();
        let harness_q = flow
            .iter()
            .find(|q| q.id == "global-default-harness")
            .unwrap();
        assert_eq!(root_q.prefill, "/tmp/proj");
        assert_eq!(harness_q.prefill, "claude");
    }
}