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

use super::{
    value::{Array, InternalValue, Object, Value},
    Context,
};

use crate::runtime::document::DocNode;
use crate::runtime::value::QualifiedName;

/// A trait that defines the interface for accessing and manipulating values in the PEL runtime.
///
/// This trait provides methods for querying and extracting values from different
/// data structures, including objects, arrays, and documents. Implementations
/// of this trait handle the specific logic for different value types.
pub trait ValueHandler {
    /// Detaches the underlying value from its container, if possible.
    fn detach(&self) -> Option<Value>;

    /// Selects a value by its string key.
    fn select_by_key(&self, _key: &str) -> Option<Value> {
        None
    }

    /// Selects a value by its qualified name (namespace and local name).
    fn select_by_qname(&self, _qname: &QualifiedName) -> Option<Value> {
        None
    }

    /// Selects an attribute value by its string key.
    fn select_attr_by_key(&self, _key: &str) -> Option<Value> {
        None
    }

    /// Selects an attribute value by its qualified name.
    fn select_attr_by_qname(&self, _qname: &QualifiedName) -> Option<Value> {
        None
    }

    /// Performs a multi-select operation by key, which may return multiple values.
    fn multi_select_by_key(&self, _key: &str) -> Option<Value> {
        None
    }

    /// Performs a multi-select operation by qualified name.
    fn multi_select_by_qname(&self, _qname: &QualifiedName) -> Option<Value> {
        None
    }

    /// Selects a value by its numeric index.
    fn select_by_index(&self, _index: usize) -> Option<Value> {
        None
    }

    /// Performs a sub-selection by key, with potential special handling for certain value types.
    fn subselect_by_key(&self, key: &str) -> Option<Value> {
        self.select_by_key(key)
    }

    /// Returns the size of the value, if possible.
    fn size(&self) -> Option<usize> {
        None
    }
}

enum ContextualHandler<'a> {
    Any(Any<'a>),
    Array(OwnedValueHandler<'a, Array>),
    Object(OwnedValueHandler<'a, Object>),
    Reference(&'a dyn ValueHandler),
    DocNode(OwnedValueHandler<'a, DocNode>),
}

impl<'a> ContextualHandler<'a> {
    fn internal_value_handler(&'a self) -> &'a dyn ValueHandler {
        match *self {
            Self::Any(ref a) => a,
            Self::Reference(r) => r,
            Self::Array(ref a) => a,
            Self::Object(ref o) => o,
            Self::DocNode(ref m) => m,
        }
    }
}

impl ValueHandler for ContextualHandler<'_> {
    fn detach(&self) -> Option<Value> {
        self.internal_value_handler().detach()
    }

    fn select_by_key(&self, key: &str) -> Option<Value> {
        self.internal_value_handler().select_by_key(key)
    }

    fn select_by_qname(&self, qname: &QualifiedName) -> Option<Value> {
        self.internal_value_handler().select_by_qname(qname)
    }

    fn select_attr_by_key(&self, key: &str) -> Option<Value> {
        self.internal_value_handler().select_attr_by_key(key)
    }

    fn select_attr_by_qname(&self, qname: &QualifiedName) -> Option<Value> {
        self.internal_value_handler().select_attr_by_qname(qname)
    }

    fn multi_select_by_key(&self, key: &str) -> Option<Value> {
        self.internal_value_handler().multi_select_by_key(key)
    }

    fn multi_select_by_qname(&self, qname: &QualifiedName) -> Option<Value> {
        self.internal_value_handler().multi_select_by_qname(qname)
    }

    fn select_by_index(&self, index: usize) -> Option<Value> {
        self.internal_value_handler().select_by_index(index)
    }

    fn subselect_by_key(&self, key: &str) -> Option<Value> {
        self.internal_value_handler().select_by_key(key)
    }

    fn size(&self) -> Option<usize> {
        self.internal_value_handler().size()
    }
}

struct Any<'a>(&'a Value);

impl ValueHandler for Any<'_> {
    fn detach(&self) -> Option<Value> {
        Some(self.0.clone())
    }
}

struct NullValueHandler;

impl ValueHandler for NullValueHandler {
    fn detach(&self) -> Option<Value> {
        Some(Value::null())
    }

    fn select_by_key(&self, _key: &str) -> Option<Value> {
        Some(Value::null())
    }

    fn select_by_index(&self, _index: usize) -> Option<Value> {
        Some(Value::null())
    }

    fn size(&self) -> Option<usize> {
        None
    }
}

impl ValueHandler for String {
    fn detach(&self) -> Option<Value> {
        Some(Value::string(self.clone()))
    }

    fn select_by_index(&self, index: usize) -> Option<Value> {
        Some(
            self.chars()
                .nth(index)
                .map(|c| Value::string(c.to_string()))
                .unwrap_or_else(Value::null),
        )
    }

    fn size(&self) -> Option<usize> {
        Some(self.len())
    }
}

struct OwnedValueHandler<'a, T> {
    context: &'a dyn Context,
    value: &'a T,
}

impl<'a, T> OwnedValueHandler<'a, T> {
    fn new(context: &'a dyn Context, value: &'a T) -> Self {
        Self { context, value }
    }
}

impl ValueHandler for OwnedValueHandler<'_, Array> {
    fn select_by_index(&self, index: usize) -> Option<Value> {
        Some(self.value.get(index).cloned().unwrap_or_else(Value::null))
    }

    fn select_by_key(&self, key: &str) -> Option<Value> {
        let array = self
            .value
            .iter()
            .filter_map(|v| v.to_value_handler(self.context))
            .filter_map(|vh| vh.select_by_key(key))
            .collect();

        Some(Value::array(array))
    }

    fn size(&self) -> Option<usize> {
        Some(self.value.len())
    }

    fn detach(&self) -> Option<Value> {
        let array = self
            .value
            .iter()
            .filter_map(|v| v.to_value_handler(self.context))
            .filter_map(|vh| vh.detach())
            .collect();

        Some(Value::array(array))
    }
}

impl ValueHandler for OwnedValueHandler<'_, Object> {
    fn select_by_index(&self, index: usize) -> Option<Value> {
        Some(
            self.value
                .values()
                .nth(index)
                .cloned()
                .unwrap_or_else(Value::null),
        )
    }

    fn select_by_key(&self, key: &str) -> Option<Value> {
        Some(self.value.get(key).cloned().unwrap_or_else(Value::null))
    }

    fn size(&self) -> Option<usize> {
        Some(self.value.len())
    }

    fn detach(&self) -> Option<Value> {
        let object = self
            .value
            .iter()
            .filter_map(|(k, v)| v.to_value_handler(self.context).map(|ch| (k, ch)))
            .filter_map(|(k, vh)| vh.detach().map(|v| (k.clone(), v)))
            .collect();
        Some(Value::object(object))
    }
}

impl ValueHandler for OwnedValueHandler<'_, DocNode> {
    fn select_by_index(&self, index: usize) -> Option<Value> {
        self.value.child_by_index(index).map(Value::node)
    }

    fn select_by_key(&self, key: &str) -> Option<Value> {
        self.value.child_by_name(key).map(Value::node)
    }

    fn select_by_qname(&self, key: &QualifiedName) -> Option<Value> {
        self.value.child_by_qname(key).map(Value::node)
    }

    fn select_attr_by_key(&self, key: &str) -> Option<Value> {
        self.value.attr_by_name(key).map(Value::string)
    }

    fn select_attr_by_qname(&self, qname: &QualifiedName) -> Option<Value> {
        self.value.attr_by_qname(qname).map(Value::string)
    }

    fn multi_select_by_key(&self, key: &str) -> Option<Value> {
        Some(self.value.childs_by_name(key).into())
    }

    fn multi_select_by_qname(&self, qname: &QualifiedName) -> Option<Value> {
        Some(self.value.childs_by_qname(qname).into())
    }

    fn size(&self) -> Option<usize> {
        self.value.size()
    }

    fn detach(&self) -> Option<Value> {
        Some(Value::node(self.value.clone()))
    }
}

impl From<Vec<DocNode>> for Value {
    fn from(vec: Vec<DocNode>) -> Self {
        if vec.is_empty() {
            Value::null()
        } else {
            Value::array(vec.into_iter().map(Value::node).collect())
        }
    }
}

impl Value {
    fn value_handler<'a>(
        &'a self,
        internal_value: &'a InternalValue,
        context: &'a dyn Context,
    ) -> Option<impl ValueHandler + 'a> {
        match internal_value {
            InternalValue::Null => Some(ContextualHandler::Reference(&NullValueHandler)),
            InternalValue::String(s) => Some(ContextualHandler::Reference(s)),
            InternalValue::Array(array) => Some(ContextualHandler::Array(OwnedValueHandler::new(
                context, array,
            ))),
            InternalValue::Object(object) => Some(ContextualHandler::Object(
                OwnedValueHandler::new(context, object),
            )),
            InternalValue::Node(node) => Some(ContextualHandler::DocNode(OwnedValueHandler::new(
                context, node,
            ))),
            InternalValue::Reference(reference) => context
                .value_handler(*reference)
                .map(ContextualHandler::Reference),
            _ => Some(ContextualHandler::Any(Any(self))),
        }
    }

    pub(crate) fn to_value_handler<'a>(
        &'a self,
        context: &'a dyn Context,
    ) -> Option<impl ValueHandler + 'a> {
        match &self.internal {
            #[cfg(feature = "experimental_coerced_type")]
            InternalValue::CoercedType(internal, _) => self.value_handler(internal, context),
            internal => self.value_handler(internal, context),
        }
    }
}