rust_rule_engine/engine/
engine.rs

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