//! Single entry point for operation dispatch — used by MCP, HTTP/REST,
//! CLI, and the save-on-NCL reconciler.
//!
//! All four surfaces converge on [`dispatch_op`]. The function resolves
//! the requested op by id against the inventory registry and runs the
//! full pipeline via [`crate::runtime::execute_op`].

use crate::context::OpContext;
use crate::error::OpError;
use crate::registry::OperationEntry;
use crate::runtime::{execute_op, PipelineResult};

/// Dispatch an operation by its catalog id.
///
/// # Errors
///
/// - [`OpError::UnknownOperation`] when no op with the requested id is
///   registered.
/// - Any pipeline error propagated from [`execute_op`].
pub fn dispatch_op(
    op_id: &str,
    inputs: &serde_json::Value,
    ctx: &mut OpContext,
) -> Result<PipelineResult, OpError> {
    let entry = inventory::iter::<OperationEntry>()
        .find(|e| e.id == op_id)
        .ok_or_else(|| OpError::UnknownOperation(op_id.to_owned()))?;
    execute_op(entry, inputs, ctx)
}

/// Snapshot of every registered operation id. Used by `describe
/// capabilities` and the MCP `tools/list` response.
pub fn registered_op_ids() -> Vec<&'static str> {
    inventory::iter::<OperationEntry>().map(|e| e.id).collect()
}
