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

use pel::expression::{
    Apply, Body, DefaultOperator, Expression, IfElse, Operation, Selection, Symbol, UnaryOperation,
};
use pel::runtime::value::Value;
use pel::runtime::{Binding, Context, ValueHandler};
use pel::Reference;
use std::collections::hash_map::Entry;
use std::collections::HashMap;

#[derive(Default)]
pub struct TransientContext {
    vars: HashMap<Reference, OwnedValueHandler>,
}

impl TransientContext {
    pub fn detach_pending(&mut self, expression: &Expression, context: &dyn Context) {
        for reference in expression.references() {
            if let Entry::Vacant(entry) = self.vars.entry(reference) {
                if let Some(value) = context
                    .value_handler(reference)
                    .and_then(ValueHandler::detach)
                {
                    entry.insert(OwnedValueHandler::new(value));
                }
            }
        }
    }
}

impl Context for TransientContext {
    fn resolve(&self, _symbol: &Symbol) -> Binding {
        Binding::Unknown
    }

    fn value_handler(&self, reference: Reference) -> Option<&dyn ValueHandler> {
        self.vars.get(&reference).map(|v| v as &dyn ValueHandler)
    }
}

struct OwnedValueHandler {
    value: Value,
}

impl OwnedValueHandler {
    pub fn new(value: Value) -> Self {
        Self { value }
    }
}

impl ValueHandler for OwnedValueHandler {
    fn detach(&self) -> Option<Value> {
        Some(self.value.clone())
    }

    fn select_by_key(&self, key: &str) -> Option<Value> {
        if let Some(obj) = self.value.as_object() {
            return obj.get(key).cloned();
        };

        Some(Value::null())
    }

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

        if let Some(array) = self.value.as_slice() {
            return Some(array.get(index).cloned().unwrap_or_else(Value::null));
        };

        if let Some(obj) = self.value.as_object() {
            return Some(obj.values().nth(index).cloned().unwrap_or_else(Value::null));
        };

        Some(Value::null())
    }

    fn size(&self) -> Option<usize> {
        if let Some(string) = self.value.as_str() {
            return Some(string.len());
        };

        if let Some(array) = self.value.as_slice() {
            return Some(array.len());
        };

        if let Some(obj) = self.value.as_object() {
            return Some(obj.len());
        };

        None
    }
}

trait ReferenceCollector {
    fn references(&self) -> Vec<Reference>;
}

impl ReferenceCollector for Value {
    fn references(&self) -> Vec<Reference> {
        self.as_reference().map(|e| vec![e]).unwrap_or_default()
    }
}

impl ReferenceCollector for Selection {
    fn references(&self) -> Vec<Reference> {
        let mut result = Vec::new();
        result.append(&mut self.target.references());
        result.append(&mut self.selector.references());
        result
    }
}

impl ReferenceCollector for Vec<Expression> {
    fn references(&self) -> Vec<Reference> {
        let mut result = Vec::new();
        self.iter()
            .for_each(|exp| result.append(&mut exp.references()));
        result
    }
}

impl ReferenceCollector for Apply {
    fn references(&self) -> Vec<Reference> {
        let mut result = Vec::new();
        result.append(&mut self.function.references());
        result.append(&mut self.arguments.references());
        result
    }
}

impl ReferenceCollector for DefaultOperator {
    fn references(&self) -> Vec<Reference> {
        let mut result = Vec::new();
        result.append(&mut self.left.references());
        result.append(&mut self.right.references());
        result
    }
}

impl ReferenceCollector for IfElse {
    fn references(&self) -> Vec<Reference> {
        let mut result = Vec::new();
        result.append(&mut self.condition.references());
        result.append(&mut self.true_branch.references());
        result.append(&mut self.false_branch.references());
        result
    }
}

impl ReferenceCollector for UnaryOperation {
    fn references(&self) -> Vec<Reference> {
        self.operand.references()
    }
}

impl ReferenceCollector for Operation {
    fn references(&self) -> Vec<Reference> {
        let mut result = Vec::new();
        result.append(&mut self.left.references());
        result.append(&mut self.right.references());
        result
    }
}

impl ReferenceCollector for Expression {
    fn references(&self) -> Vec<Reference> {
        match &self.body {
            Body::Value(value) => value.references(),
            Body::Selection(selection) => selection.references(),
            Body::Array(array) => array.references(),
            Body::Apply(apply) => apply.references(),
            Body::DefaultOperator(default) => default.references(),
            Body::IfElse(ifelse) => ifelse.references(),
            Body::UnaryOperation(unary) => unary.references(),
            Body::Operation(operation) => operation.references(),
            _ => vec![],
        }
    }
}