eventuali_core/tenancy/
configuration.rs

1//! Tenant-specific dynamic configuration management
2//!
3//! This module provides comprehensive tenant configuration management capabilities including:
4//! - Dynamic per-tenant settings with hot-reloading
5//! - Configuration validation and type safety
6//! - Environment-specific overrides (dev/staging/prod)
7//! - Configuration versioning and rollback
8//! - Real-time configuration monitoring and alerts
9//! - Configuration templates and inheritance
10
11use std::sync::{Arc, RwLock};
12use std::collections::HashMap;
13use std::time::{Duration, Instant};
14use chrono::{DateTime, Utc};
15use serde::{Deserialize, Serialize};
16
17/// Type alias for change listener callback
18pub type ChangeListener = Box<dyn Fn(&ConfigurationChangeEvent) + Send + Sync>;
19use serde_json::Value;
20
21use super::tenant::TenantId;
22use crate::error::{EventualiError, Result};
23
24/// Configuration environments for environment-specific overrides
25#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
26#[derive(Default)]
27pub enum ConfigurationEnvironment {
28    Development,
29    Staging,
30    #[default]
31    Production,
32    Testing,
33}
34
35
36/// Configuration data types with validation
37#[derive(Debug, Clone, Serialize, Deserialize)]
38#[serde(tag = "type", content = "value")]
39pub enum ConfigurationValue {
40    String(String),
41    Integer(i64),
42    Float(f64),
43    Boolean(bool),
44    Array(Vec<ConfigurationValue>),
45    Object(HashMap<String, ConfigurationValue>),
46}
47
48impl ConfigurationValue {
49    /// Validate configuration value against schema
50    pub fn validate(&self, schema: &ConfigurationSchema) -> Result<()> {
51        match (self, schema) {
52            (ConfigurationValue::String(s), ConfigurationSchema::String { min_length, max_length, pattern }) => {
53                if let Some(min) = min_length {
54                    if s.len() < *min {
55                        return Err(EventualiError::Tenant(format!("String too short: {} < {}", s.len(), min)));
56                    }
57                }
58                if let Some(max) = max_length {
59                    if s.len() > *max {
60                        return Err(EventualiError::Tenant(format!("String too long: {} > {}", s.len(), max)));
61                    }
62                }
63                if let Some(pattern) = pattern {
64                    if !regex::Regex::new(pattern).unwrap().is_match(s) {
65                        return Err(EventualiError::Tenant(format!("String doesn't match pattern: {pattern}")));
66                    }
67                }
68                Ok(())
69            },
70            (ConfigurationValue::Integer(i), ConfigurationSchema::Integer { min, max }) => {
71                if let Some(min_val) = min {
72                    if i < min_val {
73                        return Err(EventualiError::Tenant(format!("Integer too small: {i} < {min_val}")));
74                    }
75                }
76                if let Some(max_val) = max {
77                    if i > max_val {
78                        return Err(EventualiError::Tenant(format!("Integer too large: {i} > {max_val}")));
79                    }
80                }
81                Ok(())
82            },
83            (ConfigurationValue::Float(f), ConfigurationSchema::Float { min, max }) => {
84                if let Some(min_val) = min {
85                    if f < min_val {
86                        return Err(EventualiError::Tenant(format!("Float too small: {f} < {min_val}")));
87                    }
88                }
89                if let Some(max_val) = max {
90                    if f > max_val {
91                        return Err(EventualiError::Tenant(format!("Float too large: {f} > {max_val}")));
92                    }
93                }
94                Ok(())
95            },
96            (ConfigurationValue::Boolean(_), ConfigurationSchema::Boolean) => Ok(()),
97            (ConfigurationValue::Array(items), ConfigurationSchema::Array { item_schema, min_items, max_items }) => {
98                if let Some(min) = min_items {
99                    if items.len() < *min {
100                        return Err(EventualiError::Tenant(format!("Array too small: {} < {}", items.len(), min)));
101                    }
102                }
103                if let Some(max) = max_items {
104                    if items.len() > *max {
105                        return Err(EventualiError::Tenant(format!("Array too large: {} > {}", items.len(), max)));
106                    }
107                }
108                for item in items {
109                    item.validate(item_schema)?;
110                }
111                Ok(())
112            },
113            (ConfigurationValue::Object(obj), ConfigurationSchema::Object { properties, required }) => {
114                // Check required fields
115                for req_field in required {
116                    if !obj.contains_key(req_field) {
117                        return Err(EventualiError::Tenant(format!("Required field missing: {req_field}")));
118                    }
119                }
120                // Validate each property
121                for (key, value) in obj {
122                    if let Some(prop_schema) = properties.get(key) {
123                        value.validate(prop_schema)?;
124                    }
125                }
126                Ok(())
127            },
128            _ => Err(EventualiError::Tenant("Configuration type mismatch".to_string())),
129        }
130    }
131
132    /// Convert to JSON Value
133    pub fn to_json(&self) -> Value {
134        match self {
135            ConfigurationValue::String(s) => Value::String(s.clone()),
136            ConfigurationValue::Integer(i) => Value::Number(serde_json::Number::from(*i)),
137            ConfigurationValue::Float(f) => Value::Number(serde_json::Number::from_f64(*f).unwrap_or_else(|| serde_json::Number::from(0))),
138            ConfigurationValue::Boolean(b) => Value::Bool(*b),
139            ConfigurationValue::Array(arr) => {
140                Value::Array(arr.iter().map(|v| v.to_json()).collect())
141            },
142            ConfigurationValue::Object(obj) => {
143                let mut map = serde_json::Map::new();
144                for (k, v) in obj {
145                    map.insert(k.clone(), v.to_json());
146                }
147                Value::Object(map)
148            },
149        }
150    }
151
152    /// Create from JSON Value
153    pub fn from_json(value: &Value) -> Self {
154        match value {
155            Value::String(s) => ConfigurationValue::String(s.clone()),
156            Value::Number(n) => {
157                if let Some(i) = n.as_i64() {
158                    ConfigurationValue::Integer(i)
159                } else if let Some(f) = n.as_f64() {
160                    ConfigurationValue::Float(f)
161                } else {
162                    ConfigurationValue::Float(0.0)
163                }
164            },
165            Value::Bool(b) => ConfigurationValue::Boolean(*b),
166            Value::Array(arr) => {
167                ConfigurationValue::Array(arr.iter().map(ConfigurationValue::from_json).collect())
168            },
169            Value::Object(obj) => {
170                let mut map = HashMap::new();
171                for (k, v) in obj {
172                    map.insert(k.clone(), ConfigurationValue::from_json(v));
173                }
174                ConfigurationValue::Object(map)
175            },
176            Value::Null => ConfigurationValue::String("".to_string()),
177        }
178    }
179}
180
181/// Configuration schema for validation
182#[derive(Debug, Clone, Serialize, Deserialize)]
183#[serde(tag = "type")]
184pub enum ConfigurationSchema {
185    String {
186        min_length: Option<usize>,
187        max_length: Option<usize>,
188        pattern: Option<String>,
189    },
190    Integer {
191        min: Option<i64>,
192        max: Option<i64>,
193    },
194    Float {
195        min: Option<f64>,
196        max: Option<f64>,
197    },
198    Boolean,
199    Array {
200        item_schema: Box<ConfigurationSchema>,
201        min_items: Option<usize>,
202        max_items: Option<usize>,
203    },
204    Object {
205        properties: HashMap<String, ConfigurationSchema>,
206        required: Vec<String>,
207    },
208}
209
210/// Configuration entry with metadata
211#[derive(Debug, Clone, Serialize, Deserialize)]
212pub struct ConfigurationEntry {
213    pub key: String,
214    pub value: ConfigurationValue,
215    pub schema: ConfigurationSchema,
216    pub environment: ConfigurationEnvironment,
217    pub description: Option<String>,
218    pub created_at: DateTime<Utc>,
219    pub updated_at: DateTime<Utc>,
220    pub version: u64,
221    pub is_sensitive: bool,
222    pub tags: Vec<String>,
223}
224
225impl ConfigurationEntry {
226    pub fn new(
227        key: String,
228        value: ConfigurationValue,
229        schema: ConfigurationSchema,
230        environment: ConfigurationEnvironment,
231    ) -> Result<Self> {
232        // Validate the value against the schema
233        value.validate(&schema)?;
234        
235        let now = Utc::now();
236        Ok(ConfigurationEntry {
237            key,
238            value,
239            schema,
240            environment,
241            description: None,
242            created_at: now,
243            updated_at: now,
244            version: 1,
245            is_sensitive: false,
246            tags: Vec::new(),
247        })
248    }
249
250    pub fn update_value(&mut self, new_value: ConfigurationValue) -> Result<()> {
251        // Validate new value
252        new_value.validate(&self.schema)?;
253        
254        self.value = new_value;
255        self.updated_at = Utc::now();
256        self.version += 1;
257        Ok(())
258    }
259}
260
261/// Configuration template for tenant onboarding
262#[derive(Debug, Clone, Serialize, Deserialize)]
263pub struct ConfigurationTemplate {
264    pub name: String,
265    pub description: String,
266    pub entries: Vec<ConfigurationEntry>,
267    pub inheritance_chain: Vec<String>, // Parent template names
268    pub created_at: DateTime<Utc>,
269    pub updated_at: DateTime<Utc>,
270}
271
272impl ConfigurationTemplate {
273    pub fn new(name: String, description: String) -> Self {
274        let now = Utc::now();
275        ConfigurationTemplate {
276            name,
277            description,
278            entries: Vec::new(),
279            inheritance_chain: Vec::new(),
280            created_at: now,
281            updated_at: now,
282        }
283    }
284
285    pub fn add_entry(&mut self, entry: ConfigurationEntry) {
286        self.entries.push(entry);
287        self.updated_at = Utc::now();
288    }
289
290    /// Resolve template with inheritance
291    pub fn resolve_with_inheritance(&self, templates: &HashMap<String, ConfigurationTemplate>) -> Result<Vec<ConfigurationEntry>> {
292        let mut resolved_entries = Vec::new();
293        let mut resolved_keys = std::collections::HashSet::new();
294
295        // Start with parent templates (inheritance chain)
296        for parent_name in &self.inheritance_chain {
297            if let Some(parent_template) = templates.get(parent_name) {
298                for entry in &parent_template.entries {
299                    if !resolved_keys.contains(&entry.key) {
300                        resolved_entries.push(entry.clone());
301                        resolved_keys.insert(entry.key.clone());
302                    }
303                }
304            }
305        }
306
307        // Apply this template's entries (override parents)
308        for entry in &self.entries {
309            if let Some(existing_idx) = resolved_entries.iter().position(|e| e.key == entry.key) {
310                resolved_entries[existing_idx] = entry.clone();
311            } else {
312                resolved_entries.push(entry.clone());
313            }
314        }
315
316        Ok(resolved_entries)
317    }
318}
319
320/// Configuration change event for auditing and monitoring
321#[derive(Debug, Clone, Serialize, Deserialize)]
322pub struct ConfigurationChangeEvent {
323    pub tenant_id: TenantId,
324    pub key: String,
325    pub old_value: Option<ConfigurationValue>,
326    pub new_value: ConfigurationValue,
327    pub environment: ConfigurationEnvironment,
328    pub changed_by: String, // User or system
329    pub change_reason: String,
330    pub timestamp: DateTime<Utc>,
331    pub rollback_point: bool,
332}
333
334/// Configuration metrics for monitoring
335#[derive(Debug, Clone, Serialize, Deserialize)]
336pub struct ConfigurationMetrics {
337    pub tenant_id: TenantId,
338    pub total_configurations: usize,
339    pub configurations_by_environment: HashMap<ConfigurationEnvironment, usize>,
340    pub last_change_timestamp: Option<DateTime<Utc>>,
341    pub total_changes_today: usize,
342    pub hot_reload_count: usize,
343    pub validation_errors_count: usize,
344    pub cache_hit_rate: f64,
345    pub average_retrieval_time_ms: f64,
346}
347
348/// Configuration cache for performance
349#[derive(Debug)]
350struct ConfigurationCache {
351    entries: HashMap<String, (ConfigurationValue, Instant)>,
352    ttl: Duration,
353    hit_count: u64,
354    miss_count: u64,
355}
356
357impl ConfigurationCache {
358    fn new(ttl_seconds: u64) -> Self {
359        ConfigurationCache {
360            entries: HashMap::new(),
361            ttl: Duration::from_secs(ttl_seconds),
362            hit_count: 0,
363            miss_count: 0,
364        }
365    }
366
367    fn get(&mut self, key: &str) -> Option<ConfigurationValue> {
368        if let Some((value, timestamp)) = self.entries.get(key) {
369            if timestamp.elapsed() < self.ttl {
370                self.hit_count += 1;
371                return Some(value.clone());
372            } else {
373                self.entries.remove(key);
374            }
375        }
376        self.miss_count += 1;
377        None
378    }
379
380    fn set(&mut self, key: String, value: ConfigurationValue) {
381        self.entries.insert(key, (value, Instant::now()));
382    }
383
384    fn invalidate(&mut self, key: &str) {
385        self.entries.remove(key);
386    }
387
388    fn hit_rate(&self) -> f64 {
389        if self.hit_count + self.miss_count == 0 {
390            0.0
391        } else {
392            self.hit_count as f64 / (self.hit_count + self.miss_count) as f64
393        }
394    }
395
396    fn clear(&mut self) {
397        self.entries.clear();
398    }
399}
400
401/// Advanced tenant configuration manager with hot-reloading and validation
402pub struct TenantConfigurationManager {
403    tenant_id: TenantId,
404    configurations: Arc<RwLock<HashMap<(String, ConfigurationEnvironment), ConfigurationEntry>>>,
405    templates: Arc<RwLock<HashMap<String, ConfigurationTemplate>>>,
406    change_history: Arc<RwLock<Vec<ConfigurationChangeEvent>>>,
407    cache: Arc<RwLock<ConfigurationCache>>,
408    current_environment: ConfigurationEnvironment,
409    hot_reload_enabled: bool,
410    validation_enabled: bool,
411    change_listeners: Arc<RwLock<Vec<ChangeListener>>>,
412}
413
414impl TenantConfigurationManager {
415    pub fn new(tenant_id: TenantId) -> Self {
416        TenantConfigurationManager {
417            tenant_id,
418            configurations: Arc::new(RwLock::new(HashMap::new())),
419            templates: Arc::new(RwLock::new(HashMap::new())),
420            change_history: Arc::new(RwLock::new(Vec::new())),
421            cache: Arc::new(RwLock::new(ConfigurationCache::new(300))), // 5 minutes TTL
422            current_environment: ConfigurationEnvironment::Production,
423            hot_reload_enabled: true,
424            validation_enabled: true,
425            change_listeners: Arc::new(RwLock::new(Vec::new())),
426        }
427    }
428
429    /// Set configuration value with validation and hot-reload
430    pub fn set_configuration(
431        &self,
432        key: String,
433        value: ConfigurationValue,
434        schema: ConfigurationSchema,
435        environment: Option<ConfigurationEnvironment>,
436        changed_by: String,
437        change_reason: String,
438    ) -> Result<()> {
439        let env = environment.unwrap_or_else(|| self.current_environment.clone());
440
441        // Validate if enabled
442        if self.validation_enabled {
443            value.validate(&schema)?;
444        }
445
446        let mut configurations = self.configurations.write().unwrap();
447        let config_key = (key.clone(), env.clone());
448
449        // Get old value for change tracking
450        let old_value = configurations.get(&config_key).map(|entry| entry.value.clone());
451
452        // Create or update configuration entry
453        let entry = if let Some(existing) = configurations.get(&config_key) {
454            let mut updated = existing.clone();
455            updated.update_value(value.clone())?;
456            updated
457        } else {
458            ConfigurationEntry::new(key.clone(), value.clone(), schema, env.clone())?
459        };
460
461        configurations.insert(config_key, entry);
462        drop(configurations); // Release the write lock
463
464        // Invalidate cache
465        {
466            let mut cache = self.cache.write().unwrap();
467            cache.invalidate(&key);
468        }
469
470        // Record change event
471        let change_event = ConfigurationChangeEvent {
472            tenant_id: self.tenant_id.clone(),
473            key: key.clone(),
474            old_value,
475            new_value: value,
476            environment: env,
477            changed_by,
478            change_reason,
479            timestamp: Utc::now(),
480            rollback_point: false,
481        };
482
483        // Add to change history
484        {
485            let mut history = self.change_history.write().unwrap();
486            history.push(change_event.clone());
487            
488            // Keep only last 1000 changes
489            if history.len() > 1000 {
490                let excess = history.len() - 1000;
491                history.drain(0..excess);
492            }
493        }
494
495        // Notify listeners for hot reload
496        if self.hot_reload_enabled {
497            let listeners = self.change_listeners.read().unwrap();
498            for listener in listeners.iter() {
499                listener(&change_event);
500            }
501        }
502
503        Ok(())
504    }
505
506    /// Get configuration value with caching
507    pub fn get_configuration(
508        &self,
509        key: &str,
510        environment: Option<ConfigurationEnvironment>,
511    ) -> Option<ConfigurationValue> {
512        let env = environment.unwrap_or_else(|| self.current_environment.clone());
513        let cache_key = format!("{key}:{env:?}");
514
515        // Check cache first
516        {
517            let mut cache = self.cache.write().unwrap();
518            if let Some(cached_value) = cache.get(&cache_key) {
519                return Some(cached_value);
520            }
521        }
522
523        // Get from storage
524        let configurations = self.configurations.read().unwrap();
525        if let Some(entry) = configurations.get(&(key.to_string(), env.clone())) {
526            let value = entry.value.clone();
527            
528            // Update cache
529            {
530                let mut cache = self.cache.write().unwrap();
531                cache.set(cache_key, value.clone());
532            }
533            
534            return Some(value);
535        }
536
537        // Try fallback environments
538        let fallback_envs = match env {
539            ConfigurationEnvironment::Development => vec![ConfigurationEnvironment::Staging, ConfigurationEnvironment::Production],
540            ConfigurationEnvironment::Testing => vec![ConfigurationEnvironment::Development, ConfigurationEnvironment::Production],
541            ConfigurationEnvironment::Staging => vec![ConfigurationEnvironment::Production],
542            ConfigurationEnvironment::Production => vec![],
543        };
544
545        for fallback_env in fallback_envs {
546            if let Some(entry) = configurations.get(&(key.to_string(), fallback_env)) {
547                let value = entry.value.clone();
548                
549                // Update cache with fallback value
550                {
551                    let mut cache = self.cache.write().unwrap();
552                    cache.set(cache_key, value.clone());
553                }
554                
555                return Some(value);
556            }
557        }
558
559        None
560    }
561
562    /// Get all configurations for environment
563    pub fn get_all_configurations(
564        &self,
565        environment: Option<ConfigurationEnvironment>,
566    ) -> HashMap<String, ConfigurationValue> {
567        let env = environment.unwrap_or_else(|| self.current_environment.clone());
568        let configurations = self.configurations.read().unwrap();
569        
570        let mut result = HashMap::new();
571        for ((key, config_env), entry) in configurations.iter() {
572            if *config_env == env {
573                result.insert(key.clone(), entry.value.clone());
574            }
575        }
576        
577        result
578    }
579
580    /// Apply configuration template to tenant
581    pub fn apply_template(&self, template_name: &str, environment: ConfigurationEnvironment) -> Result<usize> {
582        let templates = self.templates.read().unwrap();
583        let template = templates.get(template_name)
584            .ok_or_else(|| EventualiError::Tenant(format!("Template not found: {template_name}")))?;
585
586        let resolved_entries = template.resolve_with_inheritance(&templates)?;
587        drop(templates); // Release read lock
588
589        let mut applied_count = 0;
590        for mut entry in resolved_entries {
591            // Update environment for this application
592            entry.environment = environment.clone();
593            
594            let config_key = (entry.key.clone(), environment.clone());
595            {
596                let mut configurations = self.configurations.write().unwrap();
597                configurations.insert(config_key, entry);
598            }
599            
600            applied_count += 1;
601        }
602
603        // Clear cache after template application
604        {
605            let mut cache = self.cache.write().unwrap();
606            cache.clear();
607        }
608
609        Ok(applied_count)
610    }
611
612    /// Create configuration template
613    pub fn create_template(&self, template: ConfigurationTemplate) -> Result<()> {
614        let mut templates = self.templates.write().unwrap();
615        templates.insert(template.name.clone(), template);
616        Ok(())
617    }
618
619    /// Delete configuration
620    pub fn delete_configuration(
621        &self,
622        key: &str,
623        environment: Option<ConfigurationEnvironment>,
624        changed_by: String,
625        change_reason: String,
626    ) -> Result<bool> {
627        let env = environment.unwrap_or_else(|| self.current_environment.clone());
628        let config_key = (key.to_string(), env.clone());
629
630        let mut configurations = self.configurations.write().unwrap();
631        if let Some(removed_entry) = configurations.remove(&config_key) {
632            drop(configurations); // Release write lock
633            
634            // Invalidate cache
635            {
636                let mut cache = self.cache.write().unwrap();
637                cache.invalidate(key);
638            }
639
640            // Record deletion event
641            let change_event = ConfigurationChangeEvent {
642                tenant_id: self.tenant_id.clone(),
643                key: key.to_string(),
644                old_value: Some(removed_entry.value),
645                new_value: ConfigurationValue::String("".to_string()), // Placeholder for deletion
646                environment: env,
647                changed_by,
648                change_reason,
649                timestamp: Utc::now(),
650                rollback_point: false,
651            };
652
653            // Add to change history
654            {
655                let mut history = self.change_history.write().unwrap();
656                history.push(change_event.clone());
657            }
658
659            // Notify listeners
660            if self.hot_reload_enabled {
661                let listeners = self.change_listeners.read().unwrap();
662                for listener in listeners.iter() {
663                    listener(&change_event);
664                }
665            }
666
667            Ok(true)
668        } else {
669            Ok(false)
670        }
671    }
672
673    /// Get configuration metrics
674    pub fn get_metrics(&self) -> ConfigurationMetrics {
675        let configurations = self.configurations.read().unwrap();
676        let history = self.change_history.read().unwrap();
677        let cache = self.cache.read().unwrap();
678
679        let mut configurations_by_environment = HashMap::new();
680        for ((_key, env), _entry) in configurations.iter() {
681            *configurations_by_environment.entry(env.clone()).or_insert(0) += 1;
682        }
683
684        let today = Utc::now().date_naive();
685        let total_changes_today = history.iter()
686            .filter(|event| event.timestamp.date_naive() == today)
687            .count();
688
689        let last_change_timestamp = history.last().map(|event| event.timestamp);
690
691        ConfigurationMetrics {
692            tenant_id: self.tenant_id.clone(),
693            total_configurations: configurations.len(),
694            configurations_by_environment,
695            last_change_timestamp,
696            total_changes_today,
697            hot_reload_count: 0, // Would be tracked separately
698            validation_errors_count: 0, // Would be tracked separately
699            cache_hit_rate: cache.hit_rate() * 100.0,
700            average_retrieval_time_ms: 1.0, // Would be measured
701        }
702    }
703
704    /// Get configuration change history
705    pub fn get_change_history(&self, limit: Option<usize>) -> Vec<ConfigurationChangeEvent> {
706        let history = self.change_history.read().unwrap();
707        let limit = limit.unwrap_or(100);
708        
709        if history.len() > limit {
710            history[history.len() - limit..].to_vec()
711        } else {
712            history.clone()
713        }
714    }
715
716    /// Create rollback point
717    pub fn create_rollback_point(&self, changed_by: String, reason: String) -> Result<()> {
718        let rollback_event = ConfigurationChangeEvent {
719            tenant_id: self.tenant_id.clone(),
720            key: "ROLLBACK_POINT".to_string(),
721            old_value: None,
722            new_value: ConfigurationValue::String(reason.clone()),
723            environment: self.current_environment.clone(),
724            changed_by,
725            change_reason: reason,
726            timestamp: Utc::now(),
727            rollback_point: true,
728        };
729
730        let mut history = self.change_history.write().unwrap();
731        history.push(rollback_event);
732        
733        Ok(())
734    }
735
736    /// Rollback to a specific point in time
737    pub fn rollback_to_point(&self, rollback_timestamp: DateTime<Utc>) -> Result<usize> {
738        let history = self.change_history.read().unwrap();
739        
740        // Find rollback point
741        let _rollback_index = history.iter()
742            .position(|event| event.rollback_point && event.timestamp <= rollback_timestamp)
743            .ok_or_else(|| EventualiError::Tenant("Rollback point not found".to_string()))?;
744
745        drop(history); // Release read lock
746
747        // Apply configurations from rollback point
748        let rollback_count = 0;
749        // This would involve restoring configuration state from the rollback point
750        // For now, this is a placeholder implementation
751
752        Ok(rollback_count)
753    }
754
755    /// Add change listener for hot reload
756    pub fn add_change_listener<F>(&self, listener: F)
757    where
758        F: Fn(&ConfigurationChangeEvent) + Send + Sync + 'static,
759    {
760        let mut listeners = self.change_listeners.write().unwrap();
761        listeners.push(Box::new(listener));
762    }
763
764    /// Set current environment
765    pub fn set_environment(&mut self, environment: ConfigurationEnvironment) {
766        self.current_environment = environment;
767        
768        // Clear cache when environment changes
769        let mut cache = self.cache.write().unwrap();
770        cache.clear();
771    }
772
773    /// Enable/disable hot reload
774    pub fn set_hot_reload_enabled(&mut self, enabled: bool) {
775        self.hot_reload_enabled = enabled;
776    }
777
778    /// Enable/disable validation
779    pub fn set_validation_enabled(&mut self, enabled: bool) {
780        self.validation_enabled = enabled;
781    }
782
783    /// Export configurations to JSON
784    pub fn export_configurations(&self, environment: Option<ConfigurationEnvironment>) -> Value {
785        let configurations = self.get_all_configurations(environment);
786        let mut result = serde_json::Map::new();
787        
788        for (key, value) in configurations {
789            result.insert(key, value.to_json());
790        }
791        
792        Value::Object(result)
793    }
794
795    /// Import configurations from JSON
796    pub fn import_configurations(
797        &self,
798        json_data: &Value,
799        environment: ConfigurationEnvironment,
800        changed_by: String,
801    ) -> Result<usize> {
802        if let Value::Object(obj) = json_data {
803            let mut imported_count = 0;
804            
805            for (key, value) in obj {
806                let config_value = ConfigurationValue::from_json(value);
807                // Use a basic schema for imported values
808                let schema = match config_value {
809                    ConfigurationValue::String(_) => ConfigurationSchema::String {
810                        min_length: None,
811                        max_length: None,
812                        pattern: None,
813                    },
814                    ConfigurationValue::Integer(_) => ConfigurationSchema::Integer {
815                        min: None,
816                        max: None,
817                    },
818                    ConfigurationValue::Float(_) => ConfigurationSchema::Float {
819                        min: None,
820                        max: None,
821                    },
822                    ConfigurationValue::Boolean(_) => ConfigurationSchema::Boolean,
823                    ConfigurationValue::Array(_) => ConfigurationSchema::Array {
824                        item_schema: Box::new(ConfigurationSchema::String {
825                            min_length: None,
826                            max_length: None,
827                            pattern: None,
828                        }),
829                        min_items: None,
830                        max_items: None,
831                    },
832                    ConfigurationValue::Object(_) => ConfigurationSchema::Object {
833                        properties: HashMap::new(),
834                        required: Vec::new(),
835                    },
836                };
837
838                self.set_configuration(
839                    key.clone(),
840                    config_value,
841                    schema,
842                    Some(environment.clone()),
843                    changed_by.clone(),
844                    "Imported from JSON".to_string(),
845                )?;
846                
847                imported_count += 1;
848            }
849            
850            Ok(imported_count)
851        } else {
852            Err(EventualiError::Tenant("Invalid JSON format for import".to_string()))
853        }
854    }
855}
856
857#[cfg(test)]
858mod tests {
859    use super::*;
860
861    #[test]
862    fn test_configuration_value_validation() {
863        let schema = ConfigurationSchema::String {
864            min_length: Some(3),
865            max_length: Some(10),
866            pattern: None,
867        };
868        
869        let valid_value = ConfigurationValue::String("hello".to_string());
870        assert!(valid_value.validate(&schema).is_ok());
871        
872        let invalid_value = ConfigurationValue::String("hi".to_string());
873        assert!(invalid_value.validate(&schema).is_err());
874    }
875
876    #[test]
877    fn test_configuration_manager_basic_operations() {
878        let tenant_id = TenantId::new("test-tenant".to_string()).unwrap();
879        let manager = TenantConfigurationManager::new(tenant_id);
880
881        // Set configuration
882        let schema = ConfigurationSchema::String {
883            min_length: None,
884            max_length: None,
885            pattern: None,
886        };
887        
888        manager.set_configuration(
889            "test_key".to_string(),
890            ConfigurationValue::String("test_value".to_string()),
891            schema,
892            None,
893            "test_user".to_string(),
894            "Testing".to_string(),
895        ).unwrap();
896
897        // Get configuration
898        let value = manager.get_configuration("test_key", None);
899        assert!(value.is_some());
900        
901        if let Some(ConfigurationValue::String(s)) = value {
902            assert_eq!(s, "test_value");
903        } else {
904            panic!("Expected string value");
905        }
906    }
907
908    #[test]
909    fn test_configuration_template() {
910        let mut template = ConfigurationTemplate::new(
911            "test_template".to_string(),
912            "Test template".to_string(),
913        );
914
915        let entry = ConfigurationEntry::new(
916            "test_key".to_string(),
917            ConfigurationValue::String("template_value".to_string()),
918            ConfigurationSchema::String {
919                min_length: None,
920                max_length: None,
921                pattern: None,
922            },
923            ConfigurationEnvironment::Development,
924        ).unwrap();
925
926        template.add_entry(entry);
927        assert_eq!(template.entries.len(), 1);
928    }
929}