//! Compiled walk plan for the `json` reader.
//!
//! A flat `Vec<Segment>` is compiled into a linked `NodePlan` tree that
//! mirrors the walker's recursion shape: at each object level we know which
//! key to descend; at each array level we iterate every element; at the
//! leaf we capture the current JSON subtree as a `Variant`.

use std::sync::Arc;

use super::path::Segment;

/// Children are `Arc<NodePlan>` so walker frames can cheaply clone the
/// subtree they're currently inside without deep-copying. NodePlan is
/// immutable after compile, so shared ownership is safe and cheap.
#[derive(Debug, Clone)]
pub enum NodePlan {
    /// Descend into the object field `key` and continue with `child`.
    Object { key: Arc<str>, child: Arc<NodePlan> },
    /// Iterate every element of the current array and continue with `child`
    /// for each one.
    Array { child: Arc<NodePlan> },
    /// Emit the current JSON subtree as one `Variant` row.
    Leaf,
}

impl NodePlan {
    /// Compile a validated `Vec<Segment>` into a [`NodePlan`] tree.
    /// Empty input compiles to [`NodePlan::Leaf`], producing one
    /// Variant row per top-level JSON document.
    pub fn compile(segments: &[Segment]) -> Self {
        let mut plan = NodePlan::Leaf;
        for seg in segments.iter().rev() {
            plan = match seg {
                Segment::Field(name) => NodePlan::Object {
                    key: Arc::from(name.as_str()),
                    child: Arc::new(plan),
                },
                Segment::ArrayElements => NodePlan::Array {
                    child: Arc::new(plan),
                },
            };
        }
        plan
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn compile_empty_segments_is_leaf() {
        let plan = NodePlan::compile(&[]);
        assert!(matches!(plan, NodePlan::Leaf));
    }

    #[test]
    fn compile_field_only_no_explode() {
        // `$.event` — extract the `event` field, no array iteration.
        let segs = vec![Segment::Field("event".into())];
        let plan = NodePlan::compile(&segs);
        match plan {
            NodePlan::Object { key, child } => {
                assert_eq!(&*key, "event");
                assert!(matches!(*child, NodePlan::Leaf));
            }
            _ => panic!("expected Object → Leaf"),
        }
    }

    #[test]
    fn compile_cloudtrail() {
        let segs = vec![Segment::Field("Records".into()), Segment::ArrayElements];
        let plan = NodePlan::compile(&segs);
        match plan {
            NodePlan::Object { key, child } => {
                assert_eq!(&*key, "Records");
                assert!(matches!(*child, NodePlan::Array { .. }));
            }
            _ => panic!("expected Object root"),
        }
    }

    #[test]
    fn compile_multi_level() {
        let segs = vec![
            Segment::Field("resourceLogs".into()),
            Segment::ArrayElements,
            Segment::Field("scopeLogs".into()),
            Segment::ArrayElements,
            Segment::Field("logRecords".into()),
            Segment::ArrayElements,
        ];
        let plan = NodePlan::compile(&segs);
        // Walk the chain.
        let mut cur = &plan;
        for expected_key in ["resourceLogs", "scopeLogs", "logRecords"] {
            match cur {
                NodePlan::Object { key, child } => {
                    assert_eq!(&**key, expected_key);
                    match child.as_ref() {
                        NodePlan::Array { child } => cur = child.as_ref(),
                        _ => panic!("expected Array after {}", expected_key),
                    }
                }
                _ => panic!("expected Object node at {}", expected_key),
            }
        }
        assert!(matches!(cur, NodePlan::Leaf));
    }
}
