//! Parse + validate the suite-level `compositions:` block.
//!
//! Each entry deserializes into a [`crate::composition::CompositionSpec`]
//! and is immediately validated by
//! [`crate::composition::ResolvedComposition::resolve`]. A validation
//! error (duplicate node, unknown need, undeclared reference, cycle,
//! missing output node) becomes a [`ParseError::BadComposition`] so
//! the loader speaks one error shape for the whole block.

use serde_yaml::Value as YamlValue;

use super::ParseError;

/// Parse the optional `compositions:` block off the YAML document.
///
/// Returns an empty vector when the key is absent so an existing
/// suite that does not declare compositions is unchanged.
pub(super) fn parse(
    value: &YamlValue,
) -> Result<Vec<crate::composition::ResolvedComposition>, ParseError> {
    let YamlValue::Mapping(map) = value else {
        return Ok(Vec::new());
    };
    let Some(YamlValue::Sequence(entries)) = map.get(YamlValue::String("compositions".into()))
    else {
        return Ok(Vec::new());
    };
    let mut out = Vec::with_capacity(entries.len());
    for (index, entry) in entries.iter().enumerate() {
        let spec: crate::composition::CompositionSpec = serde_yaml::from_value(entry.clone())
            .map_err(|e| ParseError::BadComposition {
                path: format!("compositions[{index}]"),
                detail: e.to_string(),
            })?;
        let (resolved, _warnings) = crate::composition::ResolvedComposition::resolve(&spec)
            .map_err(|e| ParseError::BadComposition {
                path: format!("compositions[{index}]"),
                detail: e.to_string(),
            })?;
        // Parse the optional `eval:` block alongside resolution so
        // the typed metric vector lands on the resolved composition.
        // The same parser the agent loader uses keeps the rubric /
        // mcp_task_completion / mcp_use / argument_correctness /
        // plan_quality surface in one place.
        let eval = super::eval_parse::parse_eval_metrics(entry, &format!("compositions[{index}]"))?;
        out.push(resolved.with_eval(eval));
    }
    Ok(out)
}
