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                } else {
688                    false
689                }
690            }
691            ConditionExpression::FunctionCall { name, args } => {
692                // Function call condition
693                if self.config.debug_mode {
694                    println!(
695                        "    🔎 Evaluating function condition: {}({:?}) {} {:?}",
696                        name,
697                        args,
698                        format!("{:?}", condition.operator).to_lowercase(),
699                        condition.value
700                    );
701                }
702
703                if let Some(function) = self.custom_functions.get(name) {
704                    // Resolve arguments from facts
705                    let arg_values: Vec<Value> = args
706                        .iter()
707                        .map(|arg| {
708                            facts
709                                .get_nested(arg)
710                                .or_else(|| facts.get(arg))
711                                .unwrap_or(Value::String(arg.clone()))
712                        })
713                        .collect();
714
715                    // Call the function
716                    match function(&arg_values, facts) {
717                        Ok(result_value) => {
718                            if self.config.debug_mode {
719                                println!("      Function result: {:?}", result_value);
720                            }
721                            condition.operator.evaluate(&result_value, &condition.value)
722                        }
723                        Err(e) => {
724                            if self.config.debug_mode {
725                                println!("      Function error: {}", e);
726                            }
727                            false
728                        }
729                    }
730                } else {
731                    if self.config.debug_mode {
732                        println!("      Function '{}' not found", name);
733                    }
734                    false
735                }
736            }
737            ConditionExpression::Test { name, args } => {
738                // Test CE condition - expects boolean result
739                if self.config.debug_mode {
740                    println!("    🧪 Evaluating test CE: test({}({:?}))", name, args);
741                }
742
743                if let Some(function) = self.custom_functions.get(name) {
744                    // Resolve arguments from facts
745                    let arg_values: Vec<Value> = args
746                        .iter()
747                        .map(|arg| {
748                            let resolved = facts
749                                .get_nested(arg)
750                                .or_else(|| facts.get(arg))
751                                .unwrap_or(Value::String(arg.clone()));
752                            if self.config.debug_mode {
753                                println!("      Resolving arg '{}' -> {:?}", arg, resolved);
754                            }
755                            resolved
756                        })
757                        .collect();
758
759                    // Call the function
760                    match function(&arg_values, facts) {
761                        Ok(result_value) => {
762                            if self.config.debug_mode {
763                                println!("      Test result: {:?}", result_value);
764                            }
765                            // Test CE expects boolean result directly
766                            match result_value {
767                                Value::Boolean(b) => b,
768                                Value::Integer(i) => i != 0,
769                                Value::Number(f) => f != 0.0,
770                                Value::String(s) => !s.is_empty(),
771                                _ => false,
772                            }
773                        }
774                        Err(e) => {
775                            if self.config.debug_mode {
776                                println!("      Test function error: {}", e);
777                            }
778                            false
779                        }
780                    }
781                } else {
782                    if self.config.debug_mode {
783                        println!("      Test function '{}' not found", name);
784                    }
785                    false
786                }
787            }
788        };
789
790        if self.config.debug_mode {
791            println!("      Result: {}", result);
792        }
793
794        Ok(result)
795    }
796
797    /// Execute an action
798    fn execute_action(&mut self, action: &ActionType, facts: &Facts) -> Result<()> {
799        match action {
800            ActionType::Set { field, value } => {
801                // Try nested first, then fall back to flat key setting
802                if let Err(_) = facts.set_nested(field, value.clone()) {
803                    // If nested fails, use flat key
804                    facts.set(field, value.clone());
805                }
806                if self.config.debug_mode {
807                    println!("  ✅ Set {field} = {value:?}");
808                }
809            }
810            ActionType::Log { message } => {
811                println!("📋 LOG: {}", message);
812            }
813            ActionType::Call { function, args } => {
814                let result = self.execute_function_call(function, args, facts)?;
815                if self.config.debug_mode {
816                    println!("  📞 Called {function}({args:?}) -> {result}");
817                }
818            }
819            ActionType::MethodCall {
820                object,
821                method,
822                args,
823            } => {
824                let result = self.execute_method_call(object, method, args, facts)?;
825                if self.config.debug_mode {
826                    println!("  🔧 Called {object}.{method}({args:?}) -> {result}");
827                }
828            }
829            ActionType::Update { object } => {
830                if self.config.debug_mode {
831                    println!("  🔄 Updated {object}");
832                }
833                // Update action is mainly for working memory management
834                // In this implementation, it's mostly a no-op since we update in place
835            }
836            ActionType::Custom {
837                action_type,
838                params,
839            } => {
840                if let Some(handler) = self.action_handlers.get(action_type) {
841                    if self.config.debug_mode {
842                        println!(
843                            "  🎯 Executing custom action: {action_type} with params: {params:?}"
844                        );
845                    }
846
847                    // Resolve parameter values from facts
848                    let resolved_params = self.resolve_action_parameters(params, facts)?;
849
850                    // Execute the registered handler
851                    handler(&resolved_params, facts)?;
852                } else {
853                    if self.config.debug_mode {
854                        println!("  ⚠️ No handler registered for custom action: {action_type}");
855                        println!(
856                            "     Available handlers: {:?}",
857                            self.action_handlers.keys().collect::<Vec<_>>()
858                        );
859                    }
860
861                    // Return error if no handler found
862                    return Err(RuleEngineError::EvaluationError {
863                        message: format!(
864                            "No action handler registered for '{action_type}'. Use engine.register_action_handler() to add custom action handlers."
865                        ),
866                    });
867                }
868            }
869            // 🔄 Workflow Actions
870            ActionType::ActivateAgendaGroup { group } => {
871                if self.config.debug_mode {
872                    println!("  🎯 Activating agenda group: {}", group);
873                }
874                // Sync with both workflow engine and agenda manager immediately
875                self.workflow_engine.activate_agenda_group(group.clone());
876                self.agenda_manager.set_focus(group);
877            }
878            ActionType::ScheduleRule {
879                rule_name,
880                delay_ms,
881            } => {
882                if self.config.debug_mode {
883                    println!(
884                        "  ⏰ Scheduling rule '{}' to execute in {}ms",
885                        rule_name, delay_ms
886                    );
887                }
888                self.workflow_engine
889                    .schedule_rule(rule_name.clone(), *delay_ms, None);
890            }
891            ActionType::CompleteWorkflow { workflow_name } => {
892                if self.config.debug_mode {
893                    println!("  ✅ Completing workflow: {}", workflow_name);
894                }
895                self.workflow_engine
896                    .complete_workflow(workflow_name.clone());
897            }
898            ActionType::SetWorkflowData { key, value } => {
899                if self.config.debug_mode {
900                    println!("  💾 Setting workflow data: {} = {:?}", key, value);
901                }
902                // For now, we'll use a default workflow ID. Later this could be enhanced
903                // to track current workflow context
904                let workflow_id = "default_workflow";
905                self.workflow_engine
906                    .set_workflow_data(workflow_id, key.clone(), value.clone());
907            }
908        }
909        Ok(())
910    }
911
912    /// Execute function call
913    fn execute_function_call(
914        &self,
915        function: &str,
916        args: &[Value],
917        facts: &Facts,
918    ) -> Result<String> {
919        let function_lower = function.to_lowercase();
920
921        // Handle built-in utility functions
922        match function_lower.as_str() {
923            "log" | "print" | "println" => self.handle_log_function(args),
924            "update" | "refresh" => self.handle_update_function(args),
925            "now" | "timestamp" => self.handle_timestamp_function(),
926            "random" => self.handle_random_function(args),
927            "format" | "sprintf" => self.handle_format_function(args),
928            "length" | "size" | "count" => self.handle_length_function(args),
929            "sum" | "add" => self.handle_sum_function(args),
930            "max" | "maximum" => self.handle_max_function(args),
931            "min" | "minimum" => self.handle_min_function(args),
932            "avg" | "average" => self.handle_average_function(args),
933            "round" => self.handle_round_function(args),
934            "floor" => self.handle_floor_function(args),
935            "ceil" | "ceiling" => self.handle_ceil_function(args),
936            "abs" | "absolute" => self.handle_abs_function(args),
937            "contains" | "includes" => self.handle_contains_function(args),
938            "startswith" | "begins_with" => self.handle_starts_with_function(args),
939            "endswith" | "ends_with" => self.handle_ends_with_function(args),
940            "lowercase" | "tolower" => self.handle_lowercase_function(args),
941            "uppercase" | "toupper" => self.handle_uppercase_function(args),
942            "trim" | "strip" => self.handle_trim_function(args),
943            "split" => self.handle_split_function(args),
944            "join" => self.handle_join_function(args),
945            _ => {
946                // Try to call custom user-defined function
947                self.handle_custom_function(function, args, facts)
948            }
949        }
950    }
951
952    /// Handle logging functions (log, print, println)
953    fn handle_log_function(&self, args: &[Value]) -> Result<String> {
954        let message = if args.is_empty() {
955            "".to_string()
956        } else if args.len() == 1 {
957            args[0].to_string()
958        } else {
959            args.iter()
960                .map(|v| v.to_string())
961                .collect::<Vec<_>>()
962                .join(" ")
963        };
964
965        println!("📋 {}", message);
966        Ok(message)
967    }
968
969    /// Handle update/refresh functions
970    fn handle_update_function(&self, args: &[Value]) -> Result<String> {
971        if let Some(arg) = args.first() {
972            Ok(format!("Updated: {}", arg.to_string()))
973        } else {
974            Ok("Updated".to_string())
975        }
976    }
977
978    /// Handle timestamp function
979    fn handle_timestamp_function(&self) -> Result<String> {
980        use std::time::{SystemTime, UNIX_EPOCH};
981        let timestamp = SystemTime::now()
982            .duration_since(UNIX_EPOCH)
983            .map_err(|e| RuleEngineError::EvaluationError {
984                message: format!("Failed to get timestamp: {}", e),
985            })?
986            .as_secs();
987        Ok(timestamp.to_string())
988    }
989
990    /// Handle random function
991    fn handle_random_function(&self, args: &[Value]) -> Result<String> {
992        use std::collections::hash_map::DefaultHasher;
993        use std::hash::{Hash, Hasher};
994
995        // Simple pseudo-random based on current time (for deterministic behavior in tests)
996        let mut hasher = DefaultHasher::new();
997        std::time::SystemTime::now().hash(&mut hasher);
998        let random_value = hasher.finish();
999
1000        if args.is_empty() {
1001            Ok((random_value % 100).to_string()) // 0-99
1002        } else if let Some(Value::Number(max)) = args.first() {
1003            let max_val = *max as u64;
1004            Ok((random_value % max_val).to_string())
1005        } else {
1006            Ok(random_value.to_string())
1007        }
1008    }
1009
1010    /// Handle format function (simple sprintf-like)
1011    fn handle_format_function(&self, args: &[Value]) -> Result<String> {
1012        if args.is_empty() {
1013            return Ok("".to_string());
1014        }
1015
1016        let template = args[0].to_string();
1017        let values: Vec<String> = args[1..].iter().map(|v| v.to_string()).collect();
1018
1019        // Simple placeholder replacement: {0}, {1}, etc.
1020        let mut result = template;
1021        for (i, value) in values.iter().enumerate() {
1022            result = result.replace(&format!("{{{}}}", i), value);
1023        }
1024
1025        Ok(result)
1026    }
1027
1028    /// Handle length/size functions
1029    fn handle_length_function(&self, args: &[Value]) -> Result<String> {
1030        if let Some(arg) = args.first() {
1031            match arg {
1032                Value::String(s) => Ok(s.len().to_string()),
1033                Value::Array(arr) => Ok(arr.len().to_string()),
1034                Value::Object(obj) => Ok(obj.len().to_string()),
1035                _ => Ok("1".to_string()), // Single value has length 1
1036            }
1037        } else {
1038            Ok("0".to_string())
1039        }
1040    }
1041
1042    /// Handle sum function
1043    fn handle_sum_function(&self, args: &[Value]) -> Result<String> {
1044        let sum = args.iter().fold(0.0, |acc, val| match val {
1045            Value::Number(n) => acc + n,
1046            Value::Integer(i) => acc + (*i as f64),
1047            _ => acc,
1048        });
1049        Ok(sum.to_string())
1050    }
1051
1052    /// Handle max function
1053    fn handle_max_function(&self, args: &[Value]) -> Result<String> {
1054        let max = args.iter().fold(f64::NEG_INFINITY, |acc, val| match val {
1055            Value::Number(n) => acc.max(*n),
1056            Value::Integer(i) => acc.max(*i as f64),
1057            _ => acc,
1058        });
1059        Ok(max.to_string())
1060    }
1061
1062    /// Handle min function
1063    fn handle_min_function(&self, args: &[Value]) -> Result<String> {
1064        let min = args.iter().fold(f64::INFINITY, |acc, val| match val {
1065            Value::Number(n) => acc.min(*n),
1066            Value::Integer(i) => acc.min(*i as f64),
1067            _ => acc,
1068        });
1069        Ok(min.to_string())
1070    }
1071
1072    /// Handle average function
1073    fn handle_average_function(&self, args: &[Value]) -> Result<String> {
1074        if args.is_empty() {
1075            return Ok("0".to_string());
1076        }
1077
1078        let (sum, count) = args.iter().fold((0.0, 0), |(sum, count), val| match val {
1079            Value::Number(n) => (sum + n, count + 1),
1080            Value::Integer(i) => (sum + (*i as f64), count + 1),
1081            _ => (sum, count),
1082        });
1083
1084        if count > 0 {
1085            Ok((sum / count as f64).to_string())
1086        } else {
1087            Ok("0".to_string())
1088        }
1089    }
1090
1091    /// Handle mathematical functions
1092    fn handle_round_function(&self, args: &[Value]) -> Result<String> {
1093        if let Some(Value::Number(n)) = args.first() {
1094            Ok(n.round().to_string())
1095        } else if let Some(Value::Integer(i)) = args.first() {
1096            Ok(i.to_string())
1097        } else {
1098            Err(RuleEngineError::EvaluationError {
1099                message: "round() requires a numeric argument".to_string(),
1100            })
1101        }
1102    }
1103
1104    fn handle_floor_function(&self, args: &[Value]) -> Result<String> {
1105        if let Some(Value::Number(n)) = args.first() {
1106            Ok(n.floor().to_string())
1107        } else if let Some(Value::Integer(i)) = args.first() {
1108            Ok(i.to_string())
1109        } else {
1110            Err(RuleEngineError::EvaluationError {
1111                message: "floor() requires a numeric argument".to_string(),
1112            })
1113        }
1114    }
1115
1116    fn handle_ceil_function(&self, args: &[Value]) -> Result<String> {
1117        if let Some(Value::Number(n)) = args.first() {
1118            Ok(n.ceil().to_string())
1119        } else if let Some(Value::Integer(i)) = args.first() {
1120            Ok(i.to_string())
1121        } else {
1122            Err(RuleEngineError::EvaluationError {
1123                message: "ceil() requires a numeric argument".to_string(),
1124            })
1125        }
1126    }
1127
1128    fn handle_abs_function(&self, args: &[Value]) -> Result<String> {
1129        if let Some(Value::Number(n)) = args.first() {
1130            Ok(n.abs().to_string())
1131        } else if let Some(Value::Integer(i)) = args.first() {
1132            Ok(i.abs().to_string())
1133        } else {
1134            Err(RuleEngineError::EvaluationError {
1135                message: "abs() requires a numeric argument".to_string(),
1136            })
1137        }
1138    }
1139
1140    /// Handle string functions
1141    fn handle_contains_function(&self, args: &[Value]) -> Result<String> {
1142        if args.len() >= 2 {
1143            let haystack = args[0].to_string();
1144            let needle = args[1].to_string();
1145            Ok(haystack.contains(&needle).to_string())
1146        } else {
1147            Err(RuleEngineError::EvaluationError {
1148                message: "contains() requires 2 arguments".to_string(),
1149            })
1150        }
1151    }
1152
1153    fn handle_starts_with_function(&self, args: &[Value]) -> Result<String> {
1154        if args.len() >= 2 {
1155            let text = args[0].to_string();
1156            let prefix = args[1].to_string();
1157            Ok(text.starts_with(&prefix).to_string())
1158        } else {
1159            Err(RuleEngineError::EvaluationError {
1160                message: "startswith() requires 2 arguments".to_string(),
1161            })
1162        }
1163    }
1164
1165    fn handle_ends_with_function(&self, args: &[Value]) -> Result<String> {
1166        if args.len() >= 2 {
1167            let text = args[0].to_string();
1168            let suffix = args[1].to_string();
1169            Ok(text.ends_with(&suffix).to_string())
1170        } else {
1171            Err(RuleEngineError::EvaluationError {
1172                message: "endswith() requires 2 arguments".to_string(),
1173            })
1174        }
1175    }
1176
1177    fn handle_lowercase_function(&self, args: &[Value]) -> Result<String> {
1178        if let Some(arg) = args.first() {
1179            Ok(arg.to_string().to_lowercase())
1180        } else {
1181            Err(RuleEngineError::EvaluationError {
1182                message: "lowercase() requires 1 argument".to_string(),
1183            })
1184        }
1185    }
1186
1187    fn handle_uppercase_function(&self, args: &[Value]) -> Result<String> {
1188        if let Some(arg) = args.first() {
1189            Ok(arg.to_string().to_uppercase())
1190        } else {
1191            Err(RuleEngineError::EvaluationError {
1192                message: "uppercase() requires 1 argument".to_string(),
1193            })
1194        }
1195    }
1196
1197    fn handle_trim_function(&self, args: &[Value]) -> Result<String> {
1198        if let Some(arg) = args.first() {
1199            Ok(arg.to_string().trim().to_string())
1200        } else {
1201            Err(RuleEngineError::EvaluationError {
1202                message: "trim() requires 1 argument".to_string(),
1203            })
1204        }
1205    }
1206
1207    fn handle_split_function(&self, args: &[Value]) -> Result<String> {
1208        if args.len() >= 2 {
1209            let text = args[0].to_string();
1210            let delimiter = args[1].to_string();
1211            let parts: Vec<String> = text.split(&delimiter).map(|s| s.to_string()).collect();
1212            Ok(format!("{:?}", parts)) // Return as debug string for now
1213        } else {
1214            Err(RuleEngineError::EvaluationError {
1215                message: "split() requires 2 arguments".to_string(),
1216            })
1217        }
1218    }
1219
1220    fn handle_join_function(&self, args: &[Value]) -> Result<String> {
1221        if args.len() >= 2 {
1222            let delimiter = args[0].to_string();
1223            let parts: Vec<String> = args[1..].iter().map(|v| v.to_string()).collect();
1224            Ok(parts.join(&delimiter))
1225        } else {
1226            Err(RuleEngineError::EvaluationError {
1227                message: "join() requires at least 2 arguments".to_string(),
1228            })
1229        }
1230    }
1231
1232    /// Handle custom user-defined functions
1233    fn handle_custom_function(
1234        &self,
1235        function: &str,
1236        args: &[Value],
1237        facts: &Facts,
1238    ) -> Result<String> {
1239        // Check if we have a registered custom function
1240        if let Some(custom_func) = self.custom_functions.get(function) {
1241            if self.config.debug_mode {
1242                println!("🎯 Calling registered function: {}({:?})", function, args);
1243            }
1244
1245            match custom_func(args, facts) {
1246                Ok(result) => Ok(result.to_string()),
1247                Err(e) => Err(e),
1248            }
1249        } else {
1250            // Function not found - return error or placeholder
1251            if self.config.debug_mode {
1252                println!("⚠️ Custom function '{}' not registered", function);
1253            }
1254
1255            Err(RuleEngineError::EvaluationError {
1256                message: format!("Function '{}' is not registered. Use engine.register_function() to add custom functions.", function),
1257            })
1258        }
1259    }
1260
1261    /// Execute method call on object
1262    fn execute_method_call(
1263        &self,
1264        object_name: &str,
1265        method: &str,
1266        args: &[Value],
1267        facts: &Facts,
1268    ) -> Result<String> {
1269        // Get the object from facts
1270        let Some(object_value) = facts.get(object_name) else {
1271            return Err(RuleEngineError::EvaluationError {
1272                message: format!("Object '{}' not found in facts", object_name),
1273            });
1274        };
1275
1276        let method_lower = method.to_lowercase();
1277
1278        // Handle setter methods (set + property name)
1279        if method_lower.starts_with("set") && args.len() == 1 {
1280            return self.handle_setter_method(object_name, method, &args[0], object_value, facts);
1281        }
1282
1283        // Handle getter methods (get + property name)
1284        if method_lower.starts_with("get") && args.is_empty() {
1285            return self.handle_getter_method(object_name, method, &object_value);
1286        }
1287
1288        // Handle built-in methods
1289        match method_lower.as_str() {
1290            "tostring" => Ok(object_value.to_string()),
1291            "update" => {
1292                facts.add_value(object_name, object_value)?;
1293                Ok(format!("Updated {}", object_name))
1294            }
1295            "reset" => self.handle_reset_method(object_name, object_value, facts),
1296            _ => self.handle_property_access_or_fallback(
1297                object_name,
1298                method,
1299                args.len(),
1300                &object_value,
1301            ),
1302        }
1303    }
1304
1305    /// Handle setter method calls (setXxx)
1306    fn handle_setter_method(
1307        &self,
1308        object_name: &str,
1309        method: &str,
1310        new_value: &Value,
1311        mut object_value: Value,
1312        facts: &Facts,
1313    ) -> Result<String> {
1314        let property_name = Self::extract_property_name_from_setter(method);
1315
1316        match object_value {
1317            Value::Object(ref mut obj) => {
1318                obj.insert(property_name.clone(), new_value.clone());
1319                facts.add_value(object_name, object_value)?;
1320                Ok(format!(
1321                    "Set {} to {}",
1322                    property_name,
1323                    new_value.to_string()
1324                ))
1325            }
1326            _ => Err(RuleEngineError::EvaluationError {
1327                message: format!("Cannot call setter on non-object type: {}", object_name),
1328            }),
1329        }
1330    }
1331
1332    /// Handle getter method calls (getXxx)
1333    fn handle_getter_method(
1334        &self,
1335        object_name: &str,
1336        method: &str,
1337        object_value: &Value,
1338    ) -> Result<String> {
1339        let property_name = Self::extract_property_name_from_getter(method);
1340
1341        match object_value {
1342            Value::Object(obj) => {
1343                if let Some(value) = obj.get(&property_name) {
1344                    Ok(value.to_string())
1345                } else {
1346                    Err(RuleEngineError::EvaluationError {
1347                        message: format!(
1348                            "Property '{}' not found on object '{}'",
1349                            property_name, object_name
1350                        ),
1351                    })
1352                }
1353            }
1354            _ => Err(RuleEngineError::EvaluationError {
1355                message: format!("Cannot call getter on non-object type: {}", object_name),
1356            }),
1357        }
1358    }
1359
1360    /// Handle reset method call
1361    fn handle_reset_method(
1362        &self,
1363        object_name: &str,
1364        mut object_value: Value,
1365        facts: &Facts,
1366    ) -> Result<String> {
1367        match object_value {
1368            Value::Object(ref mut obj) => {
1369                obj.clear();
1370                facts.add_value(object_name, object_value)?;
1371                Ok(format!("Reset {}", object_name))
1372            }
1373            _ => Err(RuleEngineError::EvaluationError {
1374                message: format!("Cannot reset non-object type: {}", object_name),
1375            }),
1376        }
1377    }
1378
1379    /// Handle property access or fallback to generic method call
1380    fn handle_property_access_or_fallback(
1381        &self,
1382        object_name: &str,
1383        method: &str,
1384        arg_count: usize,
1385        object_value: &Value,
1386    ) -> Result<String> {
1387        if let Value::Object(obj) = object_value {
1388            // Try exact property name match
1389            if let Some(value) = obj.get(method) {
1390                return Ok(value.to_string());
1391            }
1392
1393            // Try capitalized property name
1394            let capitalized_method = Self::capitalize_first_letter(method);
1395            if let Some(value) = obj.get(&capitalized_method) {
1396                return Ok(value.to_string());
1397            }
1398        }
1399
1400        // Fallback to generic response
1401        Ok(format!(
1402            "Called {}.{} with {} args",
1403            object_name, method, arg_count
1404        ))
1405    }
1406
1407    /// Extract property name from setter method (setXxx -> Xxx)
1408    fn extract_property_name_from_setter(method: &str) -> String {
1409        let property_name = &method[3..]; // Remove "set" prefix
1410        Self::capitalize_first_letter(property_name)
1411    }
1412
1413    /// Extract property name from getter method (getXxx -> Xxx)
1414    fn extract_property_name_from_getter(method: &str) -> String {
1415        let property_name = &method[3..]; // Remove "get" prefix
1416        Self::capitalize_first_letter(property_name)
1417    }
1418
1419    /// Helper function to capitalize first letter of a string
1420    fn capitalize_first_letter(s: &str) -> String {
1421        if s.is_empty() {
1422            return String::new();
1423        }
1424        let mut chars = s.chars();
1425        match chars.next() {
1426            None => String::new(),
1427            Some(first) => first.to_uppercase().collect::<String>() + chars.as_str(),
1428        }
1429    }
1430
1431    /// Resolve action parameters by replacing fact references with actual values
1432    fn resolve_action_parameters(
1433        &self,
1434        params: &HashMap<String, Value>,
1435        facts: &Facts,
1436    ) -> Result<HashMap<String, Value>> {
1437        let mut resolved = HashMap::new();
1438
1439        for (key, value) in params {
1440            let resolved_value = match value {
1441                Value::String(s) => {
1442                    // Check if string looks like a fact reference (contains dot)
1443                    if s.contains('.') {
1444                        // Try to get the value from facts
1445                        if let Some(fact_value) = facts.get_nested(s) {
1446                            fact_value
1447                        } else {
1448                            // If not found, keep original string
1449                            value.clone()
1450                        }
1451                    } else {
1452                        value.clone()
1453                    }
1454                }
1455                _ => value.clone(),
1456            };
1457            resolved.insert(key.clone(), resolved_value);
1458        }
1459
1460        Ok(resolved)
1461    }
1462
1463    // 🔌 Plugin System Methods
1464
1465    /// Load a plugin into the engine
1466    pub fn load_plugin(
1467        &mut self,
1468        plugin: std::sync::Arc<dyn crate::engine::plugin::RulePlugin>,
1469    ) -> Result<()> {
1470        // First register the plugin actions with this engine
1471        plugin.register_actions(self)?;
1472        plugin.register_functions(self)?;
1473
1474        // Then store it in the plugin manager
1475        self.plugin_manager.load_plugin(plugin)
1476    }
1477
1478    /// Unload a plugin from the engine
1479    pub fn unload_plugin(&mut self, name: &str) -> Result<()> {
1480        self.plugin_manager.unload_plugin(name)
1481    }
1482
1483    /// Hot reload a plugin
1484    pub fn hot_reload_plugin(
1485        &mut self,
1486        name: &str,
1487        new_plugin: std::sync::Arc<dyn crate::engine::plugin::RulePlugin>,
1488    ) -> Result<()> {
1489        // Unload old plugin
1490        self.plugin_manager.unload_plugin(name)?;
1491
1492        // Register new plugin actions
1493        new_plugin.register_actions(self)?;
1494        new_plugin.register_functions(self)?;
1495
1496        // Load new plugin
1497        self.plugin_manager.load_plugin(new_plugin)
1498    }
1499
1500    /// Get plugin information
1501    pub fn get_plugin_info(&self, name: &str) -> Option<&crate::engine::plugin::PluginMetadata> {
1502        self.plugin_manager.get_plugin_info(name)
1503    }
1504
1505    /// List all loaded plugins
1506    pub fn list_plugins(&self) -> Vec<PluginInfo> {
1507        self.plugin_manager.list_plugins()
1508    }
1509
1510    /// Get plugin statistics
1511    pub fn get_plugin_stats(&self) -> PluginStats {
1512        self.plugin_manager.get_stats()
1513    }
1514
1515    /// Check health of all plugins
1516    pub fn plugin_health_check(&mut self) -> HashMap<String, crate::engine::plugin::PluginHealth> {
1517        self.plugin_manager.plugin_health_check()
1518    }
1519
1520    /// Configure plugin manager
1521    pub fn configure_plugins(&mut self, config: PluginConfig) {
1522        self.plugin_manager = PluginManager::new(config);
1523    }
1524}