//! Stable explain strings for read-oriented GQL inspection.

use super::physical_plan::{PhysicalNodeScan, PhysicalPlan, ReturnSlot};

/// Render a compact physical plan explanation.
pub(crate) fn explain(plan: &PhysicalPlan) -> String {
    let returns = plan
        .returns
        .iter()
        .map(ReturnSlot::name)
        .collect::<Vec<_>>()
        .join(", ");
    let op = if plan.optional {
        "OptionalExpand"
    } else {
        "Expand"
    };
    let relationship = if plan.relationship_type_lookup.is_some() {
        let label_column = plan
            .edge_mapping
            .as_ref()
            .and_then(|mapping| mapping.label_column.as_deref())
            .unwrap_or("<unknown>");
        format!("dynamic({label_column}), hydration=edge_row")
    } else if plan.edge_mapping.is_some() {
        format!("{}, hydration=edge_row", plan.rel_type)
    } else {
        plan.rel_type.clone()
    };
    format!(
        "{op}(source={}:{}, rel={}, hops={}..{}, target={}:{}, return=[{}])",
        plan.source_var,
        plan.source_label,
        relationship,
        plan.hops.min,
        plan.hops.max,
        plan.target_var,
        plan.target_label,
        returns
    )
}

/// Render a compact node-scan plan explanation.
pub(crate) fn explain_node_scan(plan: &PhysicalNodeScan) -> String {
    let returns = plan
        .returns
        .iter()
        .map(ReturnSlot::name)
        .collect::<Vec<_>>()
        .join(", ");
    if plan.identity_lookup.is_some() {
        format!(
            "NodeLookup(node={}:{}, lookup=identity, return=[{}])",
            plan.var, plan.label, returns
        )
    } else {
        format!(
            "NodeScan(node={}:{}, return=[{}])",
            plan.var, plan.label, returns
        )
    }
}
