rust_rule_engine/engine/
engine.rs

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