//! Span-read — the code analogue of the docs `read_doc`.
//!
//! Where `read_doc` returns a whole markdown file, a code node's read returns just the SYMBOL
//! body (the lines its `Span` covers) plus a `{path, start_line, end_line}` provenance receipt.
//! Every byte returned traces to an exact source location — the law restated for reading code.

use eidos_kernel::schema::Graph;
use serde::Serialize;

/// The body text behind a code node's span plus its provenance receipt.
#[derive(Serialize, Debug, Clone, PartialEq, Eq)]
pub struct SpanRead {
    pub id: String,
    pub path: String,
    pub start_line: usize,
    pub end_line: usize,
    pub content: String,
}

/// Read the source body a node's span points at. `None` when the id is unknown, the node has no
/// span (a doc node), or the file/line range can't be read — the caller reports "not found".
pub fn read_span(graph: &Graph, id: &str) -> Option<SpanRead> {
    let node = graph.nodes.iter().find(|n| n.id == id)?;
    let span = node.span.as_ref()?;
    let text = std::fs::read_to_string(&span.path).ok()?;
    let lines: Vec<&str> = text.lines().collect();
    // 1-indexed, inclusive range — guard against a span that doesn't fit the current file.
    if span.start_line == 0 || span.start_line > span.end_line || span.end_line > lines.len() {
        return None;
    }
    let content = lines[span.start_line - 1..span.end_line].join("\n");
    Some(SpanRead {
        id: id.to_string(),
        path: span.path.clone(),
        start_line: span.start_line,
        end_line: span.end_line,
        content,
    })
}
