pmat 3.11.0

PMAT - Zero-config AI context generation and code quality toolkit (CLI, MCP, HTTP)
// Mermaid diagram generation and utility functions for canonical queries.
// This file is include!()'d into canonical_query.rs and shares its scope.

fn generate_styled_architecture_diagram(
    components: &[Component],
    edges: &[ComponentEdge],
    metrics: &FxHashMap<String, ComponentMetrics>,
) -> Result<String> {
    let mut diagram = String::from("graph TD\n");

    render_component_nodes(&mut diagram, components, metrics);
    render_component_edges(&mut diagram, edges);

    // Add class definitions
    diagram.push('\n');
    diagram.push_str("    classDef high-complexity fill:#ff9999,stroke:#ff0000,stroke-width:3px\n");
    diagram
        .push_str("    classDef medium-complexity fill:#ffcc99,stroke:#ff8800,stroke-width:2px\n");
    diagram.push_str("    classDef low-complexity fill:#99ff99,stroke:#00aa00,stroke-width:1px\n");
    diagram
        .push_str("    classDef unknown-complexity fill:#cccccc,stroke:#888888,stroke-width:1px\n");

    Ok(diagram)
}

fn render_component_nodes(
    diagram: &mut String,
    components: &[Component],
    metrics: &FxHashMap<String, ComponentMetrics>,
) {
    for component in components {
        let complexity_class = if let Some(m) = metrics.get(&component.id) {
            match m.avg_complexity {
                x if x > 15.0 => "high-complexity",
                x if x > 10.0 => "medium-complexity",
                _ => "low-complexity",
            }
        } else {
            "unknown-complexity"
        };

        diagram.push_str(&format!("    {}[\"{}\"]\n", component.id, component.label));
        diagram.push_str(&format!(
            "    class {} {}\n",
            component.id, complexity_class
        ));
    }
}

fn render_component_edges(diagram: &mut String, edges: &[ComponentEdge]) {
    for edge in edges {
        let edge_class = match edge.weight {
            w if w > 10 => "strong-coupling",
            w if w > 5 => "medium-coupling",
            _ => "weak-coupling",
        };

        let arrow_style = match edge.edge_type {
            ComponentEdgeType::Import => "-->",
            ComponentEdgeType::Call => "-.->",
            ComponentEdgeType::Inheritance => "==>>",
            ComponentEdgeType::Composition => "--o",
        };

        diagram.push_str(&format!("    {} {} {}\n", edge.from, arrow_style, edge.to));
        diagram.push_str(&format!(
            "    linkStyle {} stroke:{}\n",
            edges.len(),
            match edge_class {
                "strong-coupling" => "#ff0000",
                "medium-coupling" => "#ff8800",
                _ => "#888888",
            }
        ));
    }
}

// Helper functions

fn sanitize_component_id(name: &str) -> String {
    name.chars()
        .map(|c| if c.is_alphanumeric() { c } else { '_' })
        .collect::<String>()
        .trim_matches('_')
        .to_string()
}

fn humanize_component_name(name: &str) -> String {
    name.split('_')
        .map(|part| {
            let mut chars = part.chars();
            match chars.next() {
                None => String::new(),
                Some(first) => first.to_uppercase().collect::<String>() + chars.as_str(),
            }
        })
        .collect::<Vec<String>>()
        .join(" ")
}

fn collect_component_nodes(
    dag: &crate::models::dag::DependencyGraph,
    module_name: &str,
) -> Vec<String> {
    dag.nodes
        .iter()
        .filter(|(node_id, _)| node_id.starts_with(module_name))
        .map(|(node_id, _)| node_id.clone())
        .collect()
}

fn merge_coupled_components(
    _components: &mut [Component],
    _dag: &crate::models::dag::DependencyGraph,
) {
    // TRACKED: Implement coupling analysis and merge highly coupled components
    // For now, this is a placeholder
}

fn calculate_graph_diameter(_components: &[Component], _edges: &[ComponentEdge]) -> usize {
    // TRACKED: Implement graph diameter calculation
    // For now, return a placeholder value
    5
}

impl Clone for ComponentEdgeType {
    fn clone(&self) -> Self {
        match self {
            ComponentEdgeType::Import => ComponentEdgeType::Import,
            ComponentEdgeType::Call => ComponentEdgeType::Call,
            ComponentEdgeType::Inheritance => ComponentEdgeType::Inheritance,
            ComponentEdgeType::Composition => ComponentEdgeType::Composition,
        }
    }
}

impl std::hash::Hash for ComponentEdgeType {
    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
        std::mem::discriminant(self).hash(state);
    }
}

impl PartialEq for ComponentEdgeType {
    fn eq(&self, other: &Self) -> bool {
        std::mem::discriminant(self) == std::mem::discriminant(other)
    }
}

impl Eq for ComponentEdgeType {}