use std::collections::BTreeMap;

use eidos_kernel::schema::{Edge, Graph, Node};
use serde::Serialize;

use crate::glyph::diagnostics::GlyphDiagnostics;

/// Language/file coverage captured during a Glyph compile. This is intentionally factual:
/// `language_counts` is the number of supported files actually sent to a parser, while
/// `files_ignored_by_extension` counts regular files that Glyph walked but did not support.
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize)]
pub struct CodeCoverage {
    pub files_walked: usize,
    pub files_scanned: usize,
    pub files_ignored_by_extension: BTreeMap<String, usize>,
    #[serde(default)]
    pub files_excluded_by_scope: usize,
    #[serde(default)]
    pub source_scope_excluded_paths: Vec<String>,
    pub files_walked_paths: Vec<String>,
    pub files_ignored_paths: Vec<String>,
    /// Files excluded because the root declared an extraction-language contract that does not
    /// include their language (label -> count). Empty when no languages were declared.
    #[serde(default)]
    pub files_excluded_by_language: BTreeMap<String, usize>,
    #[serde(default)]
    pub files_excluded_by_language_paths: Vec<String>,
    pub language_counts: BTreeMap<String, usize>,
    pub node_counts_by_kind: BTreeMap<String, usize>,
    pub edge_counts_by_relation: BTreeMap<String, usize>,
}

/// The compiled code graph plus the coverage accounting behind it.
#[derive(Debug, Clone, PartialEq, Serialize)]
pub struct CodeCompileReport {
    pub graph: Graph,
    pub coverage: CodeCoverage,
    pub diagnostics: GlyphDiagnostics,
}

pub(crate) fn count_nodes_by_kind(nodes: &[Node]) -> BTreeMap<String, usize> {
    let mut counts = BTreeMap::new();
    for n in nodes {
        *counts
            .entry(format!("{:?}", n.kind).to_ascii_lowercase())
            .or_insert(0) += 1;
    }
    counts
}

pub(crate) fn count_edges_by_relation(edges: &[Edge]) -> BTreeMap<String, usize> {
    let mut counts = BTreeMap::new();
    for e in edges {
        *counts.entry(e.relation.clone()).or_insert(0) += 1;
    }
    counts
}
