rust_rule_engine/engine/
engine.rs

1use crate::engine::{
2    agenda::{ActivationGroupManager, AgendaManager},
3    analytics::RuleAnalytics,
4    facts::Facts,
5    knowledge_base::KnowledgeBase,
6};
7use crate::errors::{Result, RuleEngineError};
8use crate::types::{ActionType, Value};
9use chrono::{DateTime, Utc};
10use std::collections::HashMap;
11use std::time::{Duration, Instant};
12
13/// Type for custom function implementations
14pub type CustomFunction = Box<dyn Fn(&[Value], &Facts) -> Result<Value> + Send + Sync>;
15
16/// Type for custom action handlers
17pub type ActionHandler = Box<dyn Fn(&HashMap<String, Value>, &Facts) -> Result<()> + Send + Sync>;
18
19/// Configuration options for the rule engine
20#[derive(Debug, Clone)]
21pub struct EngineConfig {
22    /// Maximum number of execution cycles
23    pub max_cycles: usize,
24    /// Execution timeout
25    pub timeout: Option<Duration>,
26    /// Enable performance statistics collection
27    pub enable_stats: bool,
28    /// Enable debug mode with verbose logging
29    pub debug_mode: bool,
30}
31
32impl Default for EngineConfig {
33    fn default() -> Self {
34        Self {
35            max_cycles: 100,
36            timeout: Some(Duration::from_secs(30)),
37            enable_stats: true,
38            debug_mode: false,
39        }
40    }
41}
42
43/// Result of rule engine execution
44#[derive(Debug, Clone)]
45pub struct GruleExecutionResult {
46    /// Number of execution cycles
47    pub cycle_count: usize,
48    /// Number of rules evaluated
49    pub rules_evaluated: usize,
50    /// Number of rules that fired
51    pub rules_fired: usize,
52    /// Total execution time
53    pub execution_time: Duration,
54}
55
56/// Rust Rule Engine - High-performance rule execution engine
57pub struct RustRuleEngine {
58    knowledge_base: KnowledgeBase,
59    config: EngineConfig,
60    custom_functions: HashMap<String, CustomFunction>,
61    action_handlers: HashMap<String, ActionHandler>,
62    analytics: Option<RuleAnalytics>,
63    agenda_manager: AgendaManager,
64    activation_group_manager: ActivationGroupManager,
65    /// Track rules that have fired globally (for no-loop support)
66    fired_rules_global: std::collections::HashSet<String>,
67}
68
69impl RustRuleEngine {
70    /// Create a new RustRuleEngine with default configuration
71    pub fn new(knowledge_base: KnowledgeBase) -> Self {
72        Self {
73            knowledge_base,
74            config: EngineConfig::default(),
75            custom_functions: HashMap::new(),
76            action_handlers: HashMap::new(),
77            analytics: None,
78            agenda_manager: AgendaManager::new(),
79            activation_group_manager: ActivationGroupManager::new(),
80            fired_rules_global: std::collections::HashSet::new(),
81        }
82    }
83
84    /// Create a new RustRuleEngine with custom configuration
85    pub fn with_config(knowledge_base: KnowledgeBase, config: EngineConfig) -> Self {
86        Self {
87            knowledge_base,
88            config,
89            custom_functions: HashMap::new(),
90            action_handlers: HashMap::new(),
91            analytics: None,
92            agenda_manager: AgendaManager::new(),
93            activation_group_manager: ActivationGroupManager::new(),
94            fired_rules_global: std::collections::HashSet::new(),
95        }
96    }
97
98    /// Register a custom function
99    pub fn register_function<F>(&mut self, name: &str, func: F)
100    where
101        F: Fn(&[Value], &Facts) -> Result<Value> + Send + Sync + 'static,
102    {
103        self.custom_functions
104            .insert(name.to_string(), Box::new(func));
105    }
106
107    /// Register a custom action handler
108    pub fn register_action_handler<F>(&mut self, action_type: &str, handler: F)
109    where
110        F: Fn(&HashMap<String, Value>, &Facts) -> Result<()> + Send + Sync + 'static,
111    {
112        self.action_handlers
113            .insert(action_type.to_string(), Box::new(handler));
114    }
115
116    /// Enable analytics with custom configuration
117    pub fn enable_analytics(&mut self, analytics: RuleAnalytics) {
118        self.analytics = Some(analytics);
119    }
120
121    /// Reset global no-loop tracking (useful for testing or when facts change significantly)
122    pub fn reset_no_loop_tracking(&mut self) {
123        self.fired_rules_global.clear();
124    }
125
126    /// Disable analytics
127    pub fn disable_analytics(&mut self) {
128        self.analytics = None;
129    }
130
131    /// Get reference to analytics data
132    pub fn analytics(&self) -> Option<&RuleAnalytics> {
133        self.analytics.as_ref()
134    }
135
136    /// Check if a custom function is registered
137    pub fn has_function(&self, name: &str) -> bool {
138        self.custom_functions.contains_key(name)
139    }
140
141    /// Check if a custom action handler is registered
142    pub fn has_action_handler(&self, action_type: &str) -> bool {
143        self.action_handlers.contains_key(action_type)
144    }
145
146    /// Get reference to the knowledge base
147    pub fn knowledge_base(&self) -> &KnowledgeBase {
148        &self.knowledge_base
149    }
150
151    /// Get mutable reference to the knowledge base
152    pub fn knowledge_base_mut(&mut self) -> &mut KnowledgeBase {
153        &mut self.knowledge_base
154    }
155
156    /// Set focus to a specific agenda group
157    pub fn set_agenda_focus(&mut self, group: &str) {
158        self.agenda_manager.set_focus(group);
159    }
160
161    /// Get the currently active agenda group
162    pub fn get_active_agenda_group(&self) -> &str {
163        self.agenda_manager.get_active_group()
164    }
165
166    /// Pop the agenda focus stack
167    pub fn pop_agenda_focus(&mut self) -> Option<String> {
168        self.agenda_manager.pop_focus()
169    }
170
171    /// Clear all agenda focus and return to MAIN
172    pub fn clear_agenda_focus(&mut self) {
173        self.agenda_manager.clear_focus();
174    }
175
176    /// Get all agenda groups that have rules
177    pub fn get_agenda_groups(&self) -> Vec<String> {
178        self.agenda_manager
179            .get_agenda_groups(&self.knowledge_base.get_rules())
180    }
181
182    /// Get all activation groups that have rules
183    pub fn get_activation_groups(&self) -> Vec<String> {
184        self.activation_group_manager
185            .get_activation_groups(&self.knowledge_base.get_rules())
186    }
187
188    /// Execute all rules in the knowledge base against the given facts
189    pub fn execute(&mut self, facts: &Facts) -> Result<GruleExecutionResult> {
190        self.execute_at_time(facts, Utc::now())
191    }
192
193    /// Execute all rules at a specific timestamp (for date-effective/expires testing)
194    pub fn execute_at_time(
195        &mut self,
196        facts: &Facts,
197        timestamp: DateTime<Utc>,
198    ) -> Result<GruleExecutionResult> {
199        let start_time = Instant::now();
200        let mut cycle_count = 0;
201        let mut rules_evaluated = 0;
202        let mut rules_fired = 0;
203
204        if self.config.debug_mode {
205            println!(
206                "🚀 Starting rule execution with {} rules (agenda group: {})",
207                self.knowledge_base.get_rules().len(),
208                self.agenda_manager.get_active_group()
209            );
210        }
211
212        for cycle in 0..self.config.max_cycles {
213            cycle_count = cycle + 1;
214            let mut any_rule_fired = false;
215            let mut fired_rules_in_cycle = std::collections::HashSet::new();
216
217            // Reset activation groups for each cycle
218            self.activation_group_manager.reset_cycle();
219
220            // Check for timeout
221            if let Some(timeout) = self.config.timeout {
222                if start_time.elapsed() > timeout {
223                    return Err(RuleEngineError::EvaluationError {
224                        message: "Execution timeout exceeded".to_string(),
225                    });
226                }
227            }
228
229            // Get rules sorted by salience (highest first)
230            let mut rules = self.knowledge_base.get_rules().clone();
231            rules.sort_by(|a, b| b.salience.cmp(&a.salience));
232
233            // Filter rules by agenda group
234            let rules: Vec<_> = rules
235                .iter()
236                .filter(|rule| self.agenda_manager.should_evaluate_rule(rule))
237                .collect();
238
239            for rule in &rules {
240                if !rule.enabled {
241                    continue;
242                }
243
244                // Check date effective/expires
245                if !rule.is_active_at(timestamp) {
246                    continue;
247                }
248
249                // Check agenda group constraints (lock-on-active)
250                if !self.agenda_manager.can_fire_rule(rule) {
251                    continue;
252                }
253
254                // Check activation group constraints
255                if !self.activation_group_manager.can_fire(rule) {
256                    continue;
257                }
258
259                // Check no-loop: if rule has no_loop=true and already fired globally, skip
260                if rule.no_loop && self.fired_rules_global.contains(&rule.name) {
261                    continue;
262                }
263
264                rules_evaluated += 1;
265                let rule_start = Instant::now();
266
267                if self.config.debug_mode {
268                    println!("📝 Evaluating rule: {}", rule.name);
269                }
270
271                // Evaluate rule conditions
272                if self.evaluate_conditions(&rule.conditions, facts)? {
273                    if self.config.debug_mode {
274                        println!(
275                            "🔥 Rule '{}' fired (salience: {})",
276                            rule.name, rule.salience
277                        );
278                    }
279
280                    // Execute actions
281                    for action in &rule.actions {
282                        self.execute_action(action, facts)?;
283                    }
284
285                    let rule_duration = rule_start.elapsed();
286
287                    // Record analytics if enabled
288                    if let Some(analytics) = &mut self.analytics {
289                        analytics.record_execution(&rule.name, rule_duration, true, true, None, 0);
290                    }
291
292                    rules_fired += 1;
293                    any_rule_fired = true;
294
295                    // Track that this rule fired in this cycle (for cycle counting)
296                    fired_rules_in_cycle.insert(rule.name.clone());
297
298                    // Track that this rule fired globally (for no-loop support)
299                    if rule.no_loop {
300                        self.fired_rules_global.insert(rule.name.clone());
301                    }
302
303                    // Mark rule as fired for agenda and activation group management
304                    self.agenda_manager.mark_rule_fired(rule);
305                    self.activation_group_manager.mark_fired(rule);
306                } else {
307                    let rule_duration = rule_start.elapsed();
308
309                    // Record analytics for failed rules too
310                    if let Some(analytics) = &mut self.analytics {
311                        analytics.record_execution(
312                            &rule.name,
313                            rule_duration,
314                            false,
315                            false,
316                            None,
317                            0,
318                        );
319                    }
320                }
321            }
322
323            // If no rules fired in this cycle, we're done
324            if !any_rule_fired {
325                break;
326            }
327        }
328
329        let execution_time = start_time.elapsed();
330
331        Ok(GruleExecutionResult {
332            cycle_count,
333            rules_evaluated,
334            rules_fired,
335            execution_time,
336        })
337    }
338
339    /// Evaluate conditions against facts
340    fn evaluate_conditions(
341        &self,
342        conditions: &crate::engine::rule::ConditionGroup,
343        facts: &Facts,
344    ) -> Result<bool> {
345        use crate::engine::pattern_matcher::PatternMatcher;
346        use crate::engine::rule::ConditionGroup;
347
348        match conditions {
349            ConditionGroup::Single(condition) => self.evaluate_single_condition(condition, facts),
350            ConditionGroup::Compound {
351                left,
352                operator,
353                right,
354            } => {
355                let left_result = self.evaluate_conditions(left, facts)?;
356                let right_result = self.evaluate_conditions(right, facts)?;
357
358                match operator {
359                    crate::types::LogicalOperator::And => Ok(left_result && right_result),
360                    crate::types::LogicalOperator::Or => Ok(left_result || right_result),
361                    crate::types::LogicalOperator::Not => Err(RuleEngineError::EvaluationError {
362                        message: "NOT operator should not appear in compound conditions"
363                            .to_string(),
364                    }),
365                }
366            }
367            ConditionGroup::Not(condition) => {
368                let result = self.evaluate_conditions(condition, facts)?;
369                Ok(!result)
370            }
371            // Pattern matching conditions
372            ConditionGroup::Exists(condition) => {
373                Ok(PatternMatcher::evaluate_exists(condition, facts))
374            }
375            ConditionGroup::Forall(condition) => {
376                Ok(PatternMatcher::evaluate_forall(condition, facts))
377            }
378        }
379    }
380
381    /// Evaluate a single condition
382    fn evaluate_single_condition(
383        &self,
384        condition: &crate::engine::rule::Condition,
385        facts: &Facts,
386    ) -> Result<bool> {
387        // Get the field value from facts
388        let field_value = facts.get_nested(&condition.field);
389
390        if field_value.is_none() {
391            return Ok(false); // Field not found, condition fails
392        }
393
394        let field_value = field_value.unwrap();
395
396        // Compare using the operator
397        Ok(condition.operator.evaluate(&field_value, &condition.value))
398    }
399
400    /// Execute an action
401    fn execute_action(&self, action: &ActionType, facts: &Facts) -> Result<()> {
402        match action {
403            ActionType::Set { field, value } => {
404                facts.set_nested(field, value.clone())?;
405                if self.config.debug_mode {
406                    println!("  ✅ Set {field} = {value:?}");
407                }
408            }
409            ActionType::Log { message } => {
410                println!("📋 LOG: {}", message);
411            }
412            ActionType::Call { function, args } => {
413                let result = self.execute_function_call(function, args, facts)?;
414                if self.config.debug_mode {
415                    println!("  📞 Called {function}({args:?}) -> {result}");
416                }
417            }
418            ActionType::MethodCall {
419                object,
420                method,
421                args,
422            } => {
423                let result = self.execute_method_call(object, method, args, facts)?;
424                if self.config.debug_mode {
425                    println!("  🔧 Called {object}.{method}({args:?}) -> {result}");
426                }
427            }
428            ActionType::Update { object } => {
429                if self.config.debug_mode {
430                    println!("  🔄 Updated {object}");
431                }
432                // Update action is mainly for working memory management
433                // In this implementation, it's mostly a no-op since we update in place
434            }
435            ActionType::Custom {
436                action_type,
437                params,
438            } => {
439                if let Some(handler) = self.action_handlers.get(action_type) {
440                    if self.config.debug_mode {
441                        println!("  🎯 Executing custom action: {action_type} with params: {params:?}");
442                    }
443                    
444                    // Resolve parameter values from facts
445                    let resolved_params = self.resolve_action_parameters(params, facts)?;
446                    
447                    // Execute the registered handler
448                    handler(&resolved_params, facts)?;
449                } else {
450                    if self.config.debug_mode {
451                        println!("  ⚠️ No handler registered for custom action: {action_type}");
452                        println!("     Available handlers: {:?}", self.action_handlers.keys().collect::<Vec<_>>());
453                    }
454                    
455                    // Return error if no handler found
456                    return Err(RuleEngineError::EvaluationError {
457                        message: format!(
458                            "No action handler registered for '{action_type}'. Use engine.register_action_handler() to add custom action handlers."
459                        ),
460                    });
461                }
462            }
463        }
464        Ok(())
465    }
466
467    /// Execute function call
468    fn execute_function_call(
469        &self,
470        function: &str,
471        args: &[Value],
472        facts: &Facts,
473    ) -> Result<String> {
474        let function_lower = function.to_lowercase();
475
476        // Handle built-in utility functions
477        match function_lower.as_str() {
478            "log" | "print" | "println" => self.handle_log_function(args),
479            "update" | "refresh" => self.handle_update_function(args),
480            "now" | "timestamp" => self.handle_timestamp_function(),
481            "random" => self.handle_random_function(args),
482            "format" | "sprintf" => self.handle_format_function(args),
483            "length" | "size" | "count" => self.handle_length_function(args),
484            "sum" | "add" => self.handle_sum_function(args),
485            "max" | "maximum" => self.handle_max_function(args),
486            "min" | "minimum" => self.handle_min_function(args),
487            "avg" | "average" => self.handle_average_function(args),
488            "round" => self.handle_round_function(args),
489            "floor" => self.handle_floor_function(args),
490            "ceil" | "ceiling" => self.handle_ceil_function(args),
491            "abs" | "absolute" => self.handle_abs_function(args),
492            "contains" | "includes" => self.handle_contains_function(args),
493            "startswith" | "begins_with" => self.handle_starts_with_function(args),
494            "endswith" | "ends_with" => self.handle_ends_with_function(args),
495            "lowercase" | "tolower" => self.handle_lowercase_function(args),
496            "uppercase" | "toupper" => self.handle_uppercase_function(args),
497            "trim" | "strip" => self.handle_trim_function(args),
498            "split" => self.handle_split_function(args),
499            "join" => self.handle_join_function(args),
500            _ => {
501                // Try to call custom user-defined function
502                self.handle_custom_function(function, args, facts)
503            }
504        }
505    }
506
507    /// Handle logging functions (log, print, println)
508    fn handle_log_function(&self, args: &[Value]) -> Result<String> {
509        let message = if args.is_empty() {
510            "".to_string()
511        } else if args.len() == 1 {
512            args[0].to_string()
513        } else {
514            args.iter()
515                .map(|v| v.to_string())
516                .collect::<Vec<_>>()
517                .join(" ")
518        };
519
520        println!("📋 {}", message);
521        Ok(message)
522    }
523
524    /// Handle update/refresh functions
525    fn handle_update_function(&self, args: &[Value]) -> Result<String> {
526        if let Some(arg) = args.first() {
527            Ok(format!("Updated: {}", arg.to_string()))
528        } else {
529            Ok("Updated".to_string())
530        }
531    }
532
533    /// Handle timestamp function
534    fn handle_timestamp_function(&self) -> Result<String> {
535        use std::time::{SystemTime, UNIX_EPOCH};
536        let timestamp = SystemTime::now()
537            .duration_since(UNIX_EPOCH)
538            .map_err(|e| RuleEngineError::EvaluationError {
539                message: format!("Failed to get timestamp: {}", e),
540            })?
541            .as_secs();
542        Ok(timestamp.to_string())
543    }
544
545    /// Handle random function
546    fn handle_random_function(&self, args: &[Value]) -> Result<String> {
547        use std::collections::hash_map::DefaultHasher;
548        use std::hash::{Hash, Hasher};
549
550        // Simple pseudo-random based on current time (for deterministic behavior in tests)
551        let mut hasher = DefaultHasher::new();
552        std::time::SystemTime::now().hash(&mut hasher);
553        let random_value = hasher.finish();
554
555        if args.is_empty() {
556            Ok((random_value % 100).to_string()) // 0-99
557        } else if let Some(Value::Number(max)) = args.first() {
558            let max_val = *max as u64;
559            Ok((random_value % max_val).to_string())
560        } else {
561            Ok(random_value.to_string())
562        }
563    }
564
565    /// Handle format function (simple sprintf-like)
566    fn handle_format_function(&self, args: &[Value]) -> Result<String> {
567        if args.is_empty() {
568            return Ok("".to_string());
569        }
570
571        let template = args[0].to_string();
572        let values: Vec<String> = args[1..].iter().map(|v| v.to_string()).collect();
573
574        // Simple placeholder replacement: {0}, {1}, etc.
575        let mut result = template;
576        for (i, value) in values.iter().enumerate() {
577            result = result.replace(&format!("{{{}}}", i), value);
578        }
579
580        Ok(result)
581    }
582
583    /// Handle length/size functions
584    fn handle_length_function(&self, args: &[Value]) -> Result<String> {
585        if let Some(arg) = args.first() {
586            match arg {
587                Value::String(s) => Ok(s.len().to_string()),
588                Value::Array(arr) => Ok(arr.len().to_string()),
589                Value::Object(obj) => Ok(obj.len().to_string()),
590                _ => Ok("1".to_string()), // Single value has length 1
591            }
592        } else {
593            Ok("0".to_string())
594        }
595    }
596
597    /// Handle sum function
598    fn handle_sum_function(&self, args: &[Value]) -> Result<String> {
599        let sum = args.iter().fold(0.0, |acc, val| match val {
600            Value::Number(n) => acc + n,
601            Value::Integer(i) => acc + (*i as f64),
602            _ => acc,
603        });
604        Ok(sum.to_string())
605    }
606
607    /// Handle max function
608    fn handle_max_function(&self, args: &[Value]) -> Result<String> {
609        let max = args.iter().fold(f64::NEG_INFINITY, |acc, val| match val {
610            Value::Number(n) => acc.max(*n),
611            Value::Integer(i) => acc.max(*i as f64),
612            _ => acc,
613        });
614        Ok(max.to_string())
615    }
616
617    /// Handle min function
618    fn handle_min_function(&self, args: &[Value]) -> Result<String> {
619        let min = args.iter().fold(f64::INFINITY, |acc, val| match val {
620            Value::Number(n) => acc.min(*n),
621            Value::Integer(i) => acc.min(*i as f64),
622            _ => acc,
623        });
624        Ok(min.to_string())
625    }
626
627    /// Handle average function
628    fn handle_average_function(&self, args: &[Value]) -> Result<String> {
629        if args.is_empty() {
630            return Ok("0".to_string());
631        }
632
633        let (sum, count) = args.iter().fold((0.0, 0), |(sum, count), val| match val {
634            Value::Number(n) => (sum + n, count + 1),
635            Value::Integer(i) => (sum + (*i as f64), count + 1),
636            _ => (sum, count),
637        });
638
639        if count > 0 {
640            Ok((sum / count as f64).to_string())
641        } else {
642            Ok("0".to_string())
643        }
644    }
645
646    /// Handle mathematical functions
647    fn handle_round_function(&self, args: &[Value]) -> Result<String> {
648        if let Some(Value::Number(n)) = args.first() {
649            Ok(n.round().to_string())
650        } else if let Some(Value::Integer(i)) = args.first() {
651            Ok(i.to_string())
652        } else {
653            Err(RuleEngineError::EvaluationError {
654                message: "round() requires a numeric argument".to_string(),
655            })
656        }
657    }
658
659    fn handle_floor_function(&self, args: &[Value]) -> Result<String> {
660        if let Some(Value::Number(n)) = args.first() {
661            Ok(n.floor().to_string())
662        } else if let Some(Value::Integer(i)) = args.first() {
663            Ok(i.to_string())
664        } else {
665            Err(RuleEngineError::EvaluationError {
666                message: "floor() requires a numeric argument".to_string(),
667            })
668        }
669    }
670
671    fn handle_ceil_function(&self, args: &[Value]) -> Result<String> {
672        if let Some(Value::Number(n)) = args.first() {
673            Ok(n.ceil().to_string())
674        } else if let Some(Value::Integer(i)) = args.first() {
675            Ok(i.to_string())
676        } else {
677            Err(RuleEngineError::EvaluationError {
678                message: "ceil() requires a numeric argument".to_string(),
679            })
680        }
681    }
682
683    fn handle_abs_function(&self, args: &[Value]) -> Result<String> {
684        if let Some(Value::Number(n)) = args.first() {
685            Ok(n.abs().to_string())
686        } else if let Some(Value::Integer(i)) = args.first() {
687            Ok(i.abs().to_string())
688        } else {
689            Err(RuleEngineError::EvaluationError {
690                message: "abs() requires a numeric argument".to_string(),
691            })
692        }
693    }
694
695    /// Handle string functions
696    fn handle_contains_function(&self, args: &[Value]) -> Result<String> {
697        if args.len() >= 2 {
698            let haystack = args[0].to_string();
699            let needle = args[1].to_string();
700            Ok(haystack.contains(&needle).to_string())
701        } else {
702            Err(RuleEngineError::EvaluationError {
703                message: "contains() requires 2 arguments".to_string(),
704            })
705        }
706    }
707
708    fn handle_starts_with_function(&self, args: &[Value]) -> Result<String> {
709        if args.len() >= 2 {
710            let text = args[0].to_string();
711            let prefix = args[1].to_string();
712            Ok(text.starts_with(&prefix).to_string())
713        } else {
714            Err(RuleEngineError::EvaluationError {
715                message: "startswith() requires 2 arguments".to_string(),
716            })
717        }
718    }
719
720    fn handle_ends_with_function(&self, args: &[Value]) -> Result<String> {
721        if args.len() >= 2 {
722            let text = args[0].to_string();
723            let suffix = args[1].to_string();
724            Ok(text.ends_with(&suffix).to_string())
725        } else {
726            Err(RuleEngineError::EvaluationError {
727                message: "endswith() requires 2 arguments".to_string(),
728            })
729        }
730    }
731
732    fn handle_lowercase_function(&self, args: &[Value]) -> Result<String> {
733        if let Some(arg) = args.first() {
734            Ok(arg.to_string().to_lowercase())
735        } else {
736            Err(RuleEngineError::EvaluationError {
737                message: "lowercase() requires 1 argument".to_string(),
738            })
739        }
740    }
741
742    fn handle_uppercase_function(&self, args: &[Value]) -> Result<String> {
743        if let Some(arg) = args.first() {
744            Ok(arg.to_string().to_uppercase())
745        } else {
746            Err(RuleEngineError::EvaluationError {
747                message: "uppercase() requires 1 argument".to_string(),
748            })
749        }
750    }
751
752    fn handle_trim_function(&self, args: &[Value]) -> Result<String> {
753        if let Some(arg) = args.first() {
754            Ok(arg.to_string().trim().to_string())
755        } else {
756            Err(RuleEngineError::EvaluationError {
757                message: "trim() requires 1 argument".to_string(),
758            })
759        }
760    }
761
762    fn handle_split_function(&self, args: &[Value]) -> Result<String> {
763        if args.len() >= 2 {
764            let text = args[0].to_string();
765            let delimiter = args[1].to_string();
766            let parts: Vec<String> = text.split(&delimiter).map(|s| s.to_string()).collect();
767            Ok(format!("{:?}", parts)) // Return as debug string for now
768        } else {
769            Err(RuleEngineError::EvaluationError {
770                message: "split() requires 2 arguments".to_string(),
771            })
772        }
773    }
774
775    fn handle_join_function(&self, args: &[Value]) -> Result<String> {
776        if args.len() >= 2 {
777            let delimiter = args[0].to_string();
778            let parts: Vec<String> = args[1..].iter().map(|v| v.to_string()).collect();
779            Ok(parts.join(&delimiter))
780        } else {
781            Err(RuleEngineError::EvaluationError {
782                message: "join() requires at least 2 arguments".to_string(),
783            })
784        }
785    }
786
787    /// Handle custom user-defined functions
788    fn handle_custom_function(
789        &self,
790        function: &str,
791        args: &[Value],
792        facts: &Facts,
793    ) -> Result<String> {
794        // Check if we have a registered custom function
795        if let Some(custom_func) = self.custom_functions.get(function) {
796            if self.config.debug_mode {
797                println!("🎯 Calling registered function: {}({:?})", function, args);
798            }
799
800            match custom_func(args, facts) {
801                Ok(result) => Ok(result.to_string()),
802                Err(e) => Err(e),
803            }
804        } else {
805            // Function not found - return error or placeholder
806            if self.config.debug_mode {
807                println!("⚠️ Custom function '{}' not registered", function);
808            }
809
810            Err(RuleEngineError::EvaluationError {
811                message: format!("Function '{}' is not registered. Use engine.register_function() to add custom functions.", function),
812            })
813        }
814    }
815
816    /// Execute method call on object
817    fn execute_method_call(
818        &self,
819        object_name: &str,
820        method: &str,
821        args: &[Value],
822        facts: &Facts,
823    ) -> Result<String> {
824        // Get the object from facts
825        let Some(object_value) = facts.get(object_name) else {
826            return Err(RuleEngineError::EvaluationError {
827                message: format!("Object '{}' not found in facts", object_name),
828            });
829        };
830
831        let method_lower = method.to_lowercase();
832
833        // Handle setter methods (set + property name)
834        if method_lower.starts_with("set") && args.len() == 1 {
835            return self.handle_setter_method(object_name, method, &args[0], object_value, facts);
836        }
837
838        // Handle getter methods (get + property name)
839        if method_lower.starts_with("get") && args.is_empty() {
840            return self.handle_getter_method(object_name, method, &object_value);
841        }
842
843        // Handle built-in methods
844        match method_lower.as_str() {
845            "tostring" => Ok(object_value.to_string()),
846            "update" => {
847                facts.add_value(object_name, object_value)?;
848                Ok(format!("Updated {}", object_name))
849            }
850            "reset" => self.handle_reset_method(object_name, object_value, facts),
851            _ => self.handle_property_access_or_fallback(
852                object_name,
853                method,
854                args.len(),
855                &object_value,
856            ),
857        }
858    }
859
860    /// Handle setter method calls (setXxx)
861    fn handle_setter_method(
862        &self,
863        object_name: &str,
864        method: &str,
865        new_value: &Value,
866        mut object_value: Value,
867        facts: &Facts,
868    ) -> Result<String> {
869        let property_name = Self::extract_property_name_from_setter(method);
870
871        match object_value {
872            Value::Object(ref mut obj) => {
873                obj.insert(property_name.clone(), new_value.clone());
874                facts.add_value(object_name, object_value)?;
875                Ok(format!(
876                    "Set {} to {}",
877                    property_name,
878                    new_value.to_string()
879                ))
880            }
881            _ => Err(RuleEngineError::EvaluationError {
882                message: format!("Cannot call setter on non-object type: {}", object_name),
883            }),
884        }
885    }
886
887    /// Handle getter method calls (getXxx)
888    fn handle_getter_method(
889        &self,
890        object_name: &str,
891        method: &str,
892        object_value: &Value,
893    ) -> Result<String> {
894        let property_name = Self::extract_property_name_from_getter(method);
895
896        match object_value {
897            Value::Object(obj) => {
898                if let Some(value) = obj.get(&property_name) {
899                    Ok(value.to_string())
900                } else {
901                    Err(RuleEngineError::EvaluationError {
902                        message: format!(
903                            "Property '{}' not found on object '{}'",
904                            property_name, object_name
905                        ),
906                    })
907                }
908            }
909            _ => Err(RuleEngineError::EvaluationError {
910                message: format!("Cannot call getter on non-object type: {}", object_name),
911            }),
912        }
913    }
914
915    /// Handle reset method call
916    fn handle_reset_method(
917        &self,
918        object_name: &str,
919        mut object_value: Value,
920        facts: &Facts,
921    ) -> Result<String> {
922        match object_value {
923            Value::Object(ref mut obj) => {
924                obj.clear();
925                facts.add_value(object_name, object_value)?;
926                Ok(format!("Reset {}", object_name))
927            }
928            _ => Err(RuleEngineError::EvaluationError {
929                message: format!("Cannot reset non-object type: {}", object_name),
930            }),
931        }
932    }
933
934    /// Handle property access or fallback to generic method call
935    fn handle_property_access_or_fallback(
936        &self,
937        object_name: &str,
938        method: &str,
939        arg_count: usize,
940        object_value: &Value,
941    ) -> Result<String> {
942        if let Value::Object(obj) = object_value {
943            // Try exact property name match
944            if let Some(value) = obj.get(method) {
945                return Ok(value.to_string());
946            }
947
948            // Try capitalized property name
949            let capitalized_method = Self::capitalize_first_letter(method);
950            if let Some(value) = obj.get(&capitalized_method) {
951                return Ok(value.to_string());
952            }
953        }
954
955        // Fallback to generic response
956        Ok(format!(
957            "Called {}.{} with {} args",
958            object_name, method, arg_count
959        ))
960    }
961
962    /// Extract property name from setter method (setXxx -> Xxx)
963    fn extract_property_name_from_setter(method: &str) -> String {
964        let property_name = &method[3..]; // Remove "set" prefix
965        Self::capitalize_first_letter(property_name)
966    }
967
968    /// Extract property name from getter method (getXxx -> Xxx)
969    fn extract_property_name_from_getter(method: &str) -> String {
970        let property_name = &method[3..]; // Remove "get" prefix
971        Self::capitalize_first_letter(property_name)
972    }
973
974    /// Helper function to capitalize first letter of a string
975    fn capitalize_first_letter(s: &str) -> String {
976        if s.is_empty() {
977            return String::new();
978        }
979        let mut chars = s.chars();
980        match chars.next() {
981            None => String::new(),
982            Some(first) => first.to_uppercase().collect::<String>() + chars.as_str(),
983        }
984    }
985
986    /// Resolve action parameters by replacing fact references with actual values
987    fn resolve_action_parameters(
988        &self,
989        params: &HashMap<String, Value>,
990        facts: &Facts,
991    ) -> Result<HashMap<String, Value>> {
992        let mut resolved = HashMap::new();
993
994        for (key, value) in params {
995            let resolved_value = match value {
996                Value::String(s) => {
997                    // Check if string looks like a fact reference (contains dot)
998                    if s.contains('.') {
999                        // Try to get the value from facts
1000                        if let Some(fact_value) = facts.get_nested(s) {
1001                            fact_value
1002                        } else {
1003                            // If not found, keep original string
1004                            value.clone()
1005                        }
1006                    } else {
1007                        value.clone()
1008                    }
1009                }
1010                _ => value.clone(),
1011            };
1012            resolved.insert(key.clone(), resolved_value);
1013        }
1014
1015        Ok(resolved)
1016    }
1017}