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