Documentation
// Copyright (c) 2026, Salesforce, Inc.,
// All rights reserved.
// For full license text, see the LICENSE.txt file

use pel::runtime::value::{DocumentBuilder, QualifiedName, Value as PelValue};
use std::rc::Rc;

use log::debug;
use roxmltree::{Document, Node, NodeType};

pub fn xml_to_pel(body: String) -> PelValue {
    let body = Rc::new(body);
    match Document::parse(body.as_ref()) {
        Ok(doc) => doc
            .root()
            .first_child()
            .and_then(|node| transverse(&node, Rc::clone(&body), None))
            .map(|d| d.build())
            .unwrap_or_else(PelValue::null),
        Err(err) => {
            debug!("Unexpected error parsing xml body: {err}");
            PelValue::null()
        }
    }
}

fn filter_nodes(node: &Node) -> bool {
    match node.node_type() {
        NodeType::Element => true,
        NodeType::Text => node
            .text()
            .map(|t| !t.trim().is_empty())
            .unwrap_or_default(),
        _ => false,
    }
}

fn transverse(
    node: &Node,
    origin: Rc<String>,
    parent: Option<DocumentBuilder>,
) -> Option<DocumentBuilder> {
    match node.node_type() {
        NodeType::Element => {
            let default_namespace = node.default_namespace().unwrap_or_default();
            let namespace = node.tag_name().namespace().unwrap_or(default_namespace);
            let prefix = node
                .namespaces()
                .find(|n| n.uri().eq(namespace))
                .and_then(|n| n.name());
            let name = QualifiedName::prefixed(
                prefix.unwrap_or_default(),
                namespace,
                node.tag_name().name(),
            );
            let mut builder = match parent {
                None => DocumentBuilder::root(name, Rc::clone(&origin), node.range()),
                Some(b) => b.with_element(name, node.range()),
            };

            for attr in node.attributes() {
                let namespace = attr.namespace().unwrap_or(default_namespace);
                let prefix = node
                    .namespaces()
                    .find(|n| n.uri().eq(namespace))
                    .and_then(|n| n.name());
                let attr_name =
                    QualifiedName::prefixed(prefix.unwrap_or_default(), namespace, attr.name());
                builder = builder.with_attribute(attr_name, attr.value())
            }

            for child in node.children().filter(filter_nodes) {
                builder = transverse(&child, Rc::clone(&origin), Some(builder))
                    .expect("Builder already initialized.");
            }

            builder = builder.finish_element();

            Some(builder)
        }
        NodeType::Text => parent.map(|builder| builder.with_text(node.text().unwrap_or_default())),
        _ => parent,
    }
}