Skip to main content

mockforge_intelligence/behavioral_economics/
engine.rs

1//! Behavioral Economics Engine
2//!
3//! Main engine that evaluates behavior rules and executes actions based on
4//! current system state (latency, load, pricing, fraud, etc.).
5
6use crate::behavioral_economics::actions::ActionExecutor;
7use crate::behavioral_economics::conditions::ConditionEvaluator;
8use crate::behavioral_economics::config::BehavioralEconomicsConfig;
9use crate::behavioral_economics::rules::BehaviorRule;
10use mockforge_foundation::Result;
11use std::sync::Arc;
12use tokio::sync::RwLock;
13use tracing::{debug, info, warn};
14
15/// Behavioral Economics Engine
16///
17/// Evaluates behavior rules and executes actions based on system state.
18/// Rules are evaluated in priority order, with declarative rules evaluated
19/// before scriptable rules.
20pub struct BehavioralEconomicsEngine {
21    /// Engine configuration
22    config: BehavioralEconomicsConfig,
23    /// Condition evaluator
24    condition_evaluator: Arc<RwLock<ConditionEvaluator>>,
25    /// Action executor
26    action_executor: ActionExecutor,
27    /// Rules sorted by priority (highest first)
28    rules: Vec<BehaviorRule>,
29}
30
31impl BehavioralEconomicsEngine {
32    /// Create a new behavioral economics engine
33    pub fn new(config: BehavioralEconomicsConfig) -> Result<Self> {
34        // Validate all rules
35        for rule in &config.rules {
36            rule.validate()?;
37        }
38
39        // Sort rules by priority (highest first)
40        let mut rules = config.rules.clone();
41        rules.sort_by(|a, b| b.priority.cmp(&a.priority));
42
43        Ok(Self {
44            config,
45            condition_evaluator: Arc::new(RwLock::new(ConditionEvaluator::new())),
46            action_executor: ActionExecutor::new(),
47            rules,
48        })
49    }
50
51    /// Create engine with default config
52    #[allow(clippy::should_implement_trait)]
53    pub fn default() -> Self {
54        Self::new(BehavioralEconomicsConfig::default()).expect("Failed to create default engine")
55    }
56
57    /// Get condition evaluator (for updating metrics)
58    pub fn condition_evaluator(&self) -> Arc<RwLock<ConditionEvaluator>> {
59        Arc::clone(&self.condition_evaluator)
60    }
61
62    /// Evaluate all rules and execute matching actions
63    ///
64    /// Returns a list of executed actions (for logging/debugging)
65    pub async fn evaluate(&self) -> Result<Vec<String>> {
66        if !self.config.enabled {
67            return Ok(Vec::new());
68        }
69
70        let evaluator = self.condition_evaluator.read().await;
71        let mut executed_actions = Vec::new();
72
73        // Evaluate rules in priority order
74        for rule in &self.rules {
75            match evaluator.evaluate(&rule.condition) {
76                Ok(true) => {
77                    debug!("Rule '{}' condition met, executing action", rule.name);
78                    match self.action_executor.execute(&rule.action) {
79                        Ok(action_desc) => {
80                            info!("Executed action for rule '{}': {}", rule.name, action_desc);
81                            executed_actions.push(format!("{}: {}", rule.name, action_desc));
82                        }
83                        Err(e) => {
84                            warn!("Failed to execute action for rule '{}': {}", rule.name, e);
85                        }
86                    }
87                }
88                Ok(false) => {
89                    debug!("Rule '{}' condition not met", rule.name);
90                }
91                Err(e) => {
92                    warn!("Failed to evaluate condition for rule '{}': {}", rule.name, e);
93                }
94            }
95        }
96
97        Ok(executed_actions)
98    }
99
100    /// Add a rule at runtime
101    pub fn add_rule(&mut self, rule: BehaviorRule) -> Result<()> {
102        rule.validate()?;
103        self.rules.push(rule);
104        // Re-sort by priority
105        self.rules.sort_by(|a, b| b.priority.cmp(&a.priority));
106        Ok(())
107    }
108
109    /// Remove a rule by name
110    pub fn remove_rule(&mut self, name: &str) -> bool {
111        let initial_len = self.rules.len();
112        self.rules.retain(|r| r.name != name);
113        self.rules.len() < initial_len
114    }
115
116    /// Get all rules
117    pub fn rules(&self) -> &[BehaviorRule] {
118        &self.rules
119    }
120
121    /// Update configuration
122    pub fn update_config(&mut self, config: BehavioralEconomicsConfig) -> Result<()> {
123        // Validate all rules
124        for rule in &config.rules {
125            rule.validate()?;
126        }
127
128        // Sort rules by priority
129        let mut rules = config.rules.clone();
130        rules.sort_by(|a, b| b.priority.cmp(&a.priority));
131
132        self.config = config;
133        self.rules = rules;
134        Ok(())
135    }
136}
137
138#[cfg(test)]
139mod tests {
140    use super::*;
141    use crate::behavioral_economics::actions::BehaviorAction;
142    use crate::behavioral_economics::conditions::BehaviorCondition;
143    use crate::behavioral_economics::rules::BehaviorRule;
144
145    #[tokio::test]
146    async fn test_engine_creation() {
147        let config = BehavioralEconomicsConfig::new();
148        let engine = BehavioralEconomicsEngine::new(config).unwrap();
149        assert!(engine.rules().is_empty());
150    }
151
152    #[tokio::test]
153    async fn test_engine_evaluation() {
154        let rule = BehaviorRule::declarative(
155            "test-rule".to_string(),
156            BehaviorCondition::Always,
157            BehaviorAction::NoOp,
158            100,
159        );
160        let config = BehavioralEconomicsConfig::new().enable().with_rule(rule);
161        let engine = BehavioralEconomicsEngine::new(config).unwrap();
162        let results = engine.evaluate().await.unwrap();
163        assert!(!results.is_empty());
164    }
165
166    #[tokio::test]
167    async fn test_engine_disabled() {
168        let rule = BehaviorRule::declarative(
169            "test-rule".to_string(),
170            BehaviorCondition::Always,
171            BehaviorAction::NoOp,
172            100,
173        );
174        let config = BehavioralEconomicsConfig::new().disable().with_rule(rule);
175        let engine = BehavioralEconomicsEngine::new(config).unwrap();
176        let results = engine.evaluate().await.unwrap();
177        assert!(results.is_empty());
178    }
179}