elif_core/modules/
runtime.rs

1//! Runtime module integration for Epic 4 - Runtime Integration & Validation
2//!
3//! Provides runtime module initialization, dependency resolution, and integration
4//! with the existing IoC container and HTTP routing system.
5//!
6//! ## Features
7//! - **Topological sorting** for module initialization order based on dependencies
8//! - **Runtime dependency resolution** with clear error reporting and module context
9//! - **Integration** with existing `IocContainer` and controller registration systems
10//! - **Module lifecycle hooks** for startup, shutdown, and health checks
11//! - **Performance monitoring** and validation for large module graphs
12
13use std::collections::{HashMap, HashSet, VecDeque};
14use std::time::{Duration, Instant};
15
16use crate::container::IocContainer;
17use crate::errors::CoreError;
18use crate::modules::ModuleDescriptor;
19
20/// Errors that can occur during module runtime operations
21#[derive(Debug, Clone)]
22pub enum ModuleRuntimeError {
23    /// Circular dependency detected between modules
24    CircularDependency { cycle: Vec<String>, message: String },
25    /// Missing module dependency
26    MissingDependency {
27        module: String,
28        missing_dependency: String,
29        message: String,
30    },
31    /// Module initialization failed
32    InitializationFailed {
33        module: String,
34        error: String,
35        phase: String,
36    },
37    /// Module lifecycle operation failed
38    LifecycleOperationFailed {
39        module: String,
40        operation: String,
41        error: String,
42    },
43    /// Configuration conflict between modules
44    ConfigurationConflict {
45        module1: String,
46        module2: String,
47        conflict: String,
48    },
49    /// Runtime validation failed
50    ValidationFailed {
51        module: String,
52        validation_errors: Vec<String>,
53    },
54}
55
56impl std::fmt::Display for ModuleRuntimeError {
57    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
58        match self {
59            ModuleRuntimeError::CircularDependency { cycle, message } => {
60                write!(
61                    f,
62                    "Circular dependency: {} -> {}",
63                    cycle.join(" -> "),
64                    message
65                )
66            }
67            ModuleRuntimeError::MissingDependency {
68                module,
69                missing_dependency,
70                message,
71            } => {
72                write!(
73                    f,
74                    "Module '{}' missing dependency '{}': {}",
75                    module, missing_dependency, message
76                )
77            }
78            ModuleRuntimeError::InitializationFailed {
79                module,
80                error,
81                phase,
82            } => {
83                write!(
84                    f,
85                    "Module '{}' initialization failed in phase '{}': {}",
86                    module, phase, error
87                )
88            }
89            ModuleRuntimeError::LifecycleOperationFailed {
90                module,
91                operation,
92                error,
93            } => {
94                write!(
95                    f,
96                    "Module '{}' lifecycle operation '{}' failed: {}",
97                    module, operation, error
98                )
99            }
100            ModuleRuntimeError::ConfigurationConflict {
101                module1,
102                module2,
103                conflict,
104            } => {
105                write!(
106                    f,
107                    "Configuration conflict between modules '{}' and '{}': {}",
108                    module1, module2, conflict
109                )
110            }
111            ModuleRuntimeError::ValidationFailed {
112                module,
113                validation_errors,
114            } => {
115                write!(
116                    f,
117                    "Module '{}' validation failed: {}",
118                    module,
119                    validation_errors.join("; ")
120                )
121            }
122        }
123    }
124}
125
126impl std::error::Error for ModuleRuntimeError {}
127
128/// Convert ModuleRuntimeError to CoreError for compatibility
129impl From<ModuleRuntimeError> for CoreError {
130    fn from(err: ModuleRuntimeError) -> Self {
131        CoreError::InvalidServiceDescriptor {
132            message: err.to_string(),
133        }
134    }
135}
136
137/// State of a module during runtime initialization
138#[derive(Debug, Clone, PartialEq, Eq)]
139pub enum ModuleState {
140    /// Module is registered but not yet processed
141    Registered,
142    /// Module dependencies are being resolved
143    ResolvingDependencies,
144    /// Module is being configured with IoC container
145    Configuring,
146    /// Module is being initialized (lifecycle hooks)
147    Initializing,
148    /// Module is fully initialized and ready
149    Ready,
150    /// Module initialization failed
151    Failed(String),
152    /// Module is being shut down
153    ShuttingDown,
154    /// Module is shut down
155    Shutdown,
156}
157
158/// Runtime information about a module
159#[derive(Debug, Clone)]
160pub struct ModuleRuntimeInfo {
161    /// Module descriptor
162    pub descriptor: ModuleDescriptor,
163    /// Current module state
164    pub state: ModuleState,
165    /// Module load order (0-based)
166    pub load_order: Option<usize>,
167    /// Time taken for initialization
168    pub init_duration: Option<Duration>,
169    /// Time taken for configuration
170    pub config_duration: Option<Duration>,
171    /// Any errors encountered
172    pub errors: Vec<String>,
173    /// Health check status
174    pub health_status: HealthStatus,
175    /// Last health check time
176    pub last_health_check: Option<Instant>,
177}
178
179impl ModuleRuntimeInfo {
180    /// Create new runtime info from descriptor
181    pub fn new(descriptor: ModuleDescriptor) -> Self {
182        Self {
183            descriptor,
184            state: ModuleState::Registered,
185            load_order: None,
186            init_duration: None,
187            config_duration: None,
188            errors: Vec::new(),
189            health_status: HealthStatus::Unknown,
190            last_health_check: None,
191        }
192    }
193
194    /// Check if module is ready
195    pub fn is_ready(&self) -> bool {
196        self.state == ModuleState::Ready
197    }
198
199    /// Check if module failed
200    pub fn has_failed(&self) -> bool {
201        matches!(self.state, ModuleState::Failed(_))
202    }
203
204    /// Add error to module
205    pub fn add_error(&mut self, error: String) {
206        self.errors.push(error);
207    }
208}
209
210/// Health status of a module
211#[derive(Debug, Clone, PartialEq, Eq)]
212pub enum HealthStatus {
213    /// Health status unknown
214    Unknown,
215    /// Module is healthy
216    Healthy,
217    /// Module is degraded but functional
218    Degraded,
219    /// Module is unhealthy
220    Unhealthy,
221}
222
223/// Performance metrics for module runtime operations
224#[derive(Debug, Default, Clone)]
225pub struct ModulePerformanceMetrics {
226    /// Total modules processed
227    pub total_modules: usize,
228    /// Time taken for topological sorting
229    pub topological_sort_duration: Duration,
230    /// Time taken for dependency resolution
231    pub dependency_resolution_duration: Duration,
232    /// Time taken for configuration phase
233    pub configuration_duration: Duration,
234    /// Time taken for initialization phase
235    pub initialization_duration: Duration,
236    /// Average initialization time per module
237    pub avg_init_time_per_module: Duration,
238    /// Slowest module to initialize
239    pub slowest_module: Option<String>,
240    /// Slowest module initialization time
241    pub slowest_init_time: Duration,
242}
243
244/// Runtime module manager - orchestrates module loading and lifecycle
245pub struct ModuleRuntime {
246    /// Module runtime information indexed by module name
247    modules: HashMap<String, ModuleRuntimeInfo>,
248    /// Module dependency graph (module -> dependencies)
249    dependency_graph: HashMap<String, Vec<String>>,
250    /// Topological load order
251    load_order: Vec<String>,
252    /// Performance metrics
253    metrics: ModulePerformanceMetrics,
254    /// Module lifecycle hooks
255    lifecycle_hooks: HashMap<String, Box<dyn ModuleLifecycleHook>>,
256    /// Health check configuration
257    #[allow(dead_code)]
258    health_check_config: HealthCheckConfig,
259}
260
261/// Configuration for health checks
262#[derive(Debug, Clone)]
263pub struct HealthCheckConfig {
264    /// How often to run health checks
265    pub interval: Duration,
266    /// Health check timeout
267    pub timeout: Duration,
268    /// Whether health checks are enabled
269    pub enabled: bool,
270}
271
272impl Default for HealthCheckConfig {
273    fn default() -> Self {
274        Self {
275            interval: Duration::from_secs(30),
276            timeout: Duration::from_secs(5),
277            enabled: true,
278        }
279    }
280}
281
282/// Trait for module lifecycle hooks
283pub trait ModuleLifecycleHook: Send + Sync {
284    /// Called before module initialization
285    fn before_init(&self, module_name: &str) -> Result<(), ModuleRuntimeError> {
286        let _ = module_name;
287        Ok(())
288    }
289
290    /// Called after successful module initialization
291    fn after_init(&self, module_name: &str, duration: Duration) -> Result<(), ModuleRuntimeError> {
292        let _ = (module_name, duration);
293        Ok(())
294    }
295
296    /// Called when module initialization fails
297    fn on_init_failure(&self, module_name: &str, error: &ModuleRuntimeError) {
298        let _ = (module_name, error);
299    }
300
301    /// Called before module shutdown
302    fn before_shutdown(&self, module_name: &str) -> Result<(), ModuleRuntimeError> {
303        let _ = module_name;
304        Ok(())
305    }
306
307    /// Called after module shutdown
308    fn after_shutdown(&self, module_name: &str) -> Result<(), ModuleRuntimeError> {
309        let _ = module_name;
310        Ok(())
311    }
312
313    /// Health check for the module
314    fn health_check(&self, module_name: &str) -> Result<HealthStatus, ModuleRuntimeError> {
315        let _ = module_name;
316        Ok(HealthStatus::Unknown)
317    }
318}
319
320impl ModuleRuntime {
321    /// Create new module runtime
322    pub fn new() -> Self {
323        Self {
324            modules: HashMap::new(),
325            dependency_graph: HashMap::new(),
326            load_order: Vec::new(),
327            metrics: ModulePerformanceMetrics::default(),
328            lifecycle_hooks: HashMap::new(),
329            health_check_config: HealthCheckConfig::default(),
330        }
331    }
332
333    /// Create module runtime with custom health check config
334    pub fn with_health_config(health_config: HealthCheckConfig) -> Self {
335        Self {
336            modules: HashMap::new(),
337            dependency_graph: HashMap::new(),
338            load_order: Vec::new(),
339            metrics: ModulePerformanceMetrics::default(),
340            lifecycle_hooks: HashMap::new(),
341            health_check_config: health_config,
342        }
343    }
344
345    /// Register a module for runtime management
346    pub fn register_module(
347        &mut self,
348        descriptor: ModuleDescriptor,
349    ) -> Result<(), ModuleRuntimeError> {
350        let module_name = descriptor.name.clone();
351
352        // Check for duplicate registration
353        if self.modules.contains_key(&module_name) {
354            return Err(ModuleRuntimeError::ConfigurationConflict {
355                module1: module_name.clone(),
356                module2: module_name,
357                conflict: "Module already registered".to_string(),
358            });
359        }
360
361        // Store dependency information
362        self.dependency_graph
363            .insert(module_name.clone(), descriptor.dependencies.clone());
364
365        // Create runtime info
366        let runtime_info = ModuleRuntimeInfo::new(descriptor);
367        self.modules.insert(module_name, runtime_info);
368
369        Ok(())
370    }
371
372    /// Register multiple modules
373    pub fn register_modules(
374        &mut self,
375        descriptors: Vec<ModuleDescriptor>,
376    ) -> Result<(), ModuleRuntimeError> {
377        for descriptor in descriptors {
378            self.register_module(descriptor)?;
379        }
380        Ok(())
381    }
382
383    /// Add lifecycle hook for a module
384    pub fn add_lifecycle_hook<H: ModuleLifecycleHook + 'static>(
385        &mut self,
386        module_name: String,
387        hook: H,
388    ) {
389        self.lifecycle_hooks.insert(module_name, Box::new(hook));
390    }
391
392    /// Task 4.1: Implement topological sorting for module initialization order
393    pub fn calculate_load_order(&mut self) -> Result<Vec<String>, ModuleRuntimeError> {
394        let start_time = Instant::now();
395
396        let sorted_modules = self.topological_sort()?;
397
398        // Update load order in module info
399        for (index, module_name) in sorted_modules.iter().enumerate() {
400            if let Some(module_info) = self.modules.get_mut(module_name) {
401                module_info.load_order = Some(index);
402            }
403        }
404
405        self.load_order = sorted_modules.clone();
406        self.metrics.topological_sort_duration = start_time.elapsed();
407
408        Ok(sorted_modules)
409    }
410
411    /// Get the current module load order
412    pub fn load_order(&self) -> &[String] {
413        &self.load_order
414    }
415
416    /// Topological sort implementation using Kahn's algorithm for better error reporting
417    fn topological_sort(&self) -> Result<Vec<String>, ModuleRuntimeError> {
418        let mut in_degree: HashMap<String, usize> = HashMap::new();
419        let mut graph: HashMap<String, Vec<String>> = HashMap::new();
420
421        // Initialize in-degree count and build forward graph
422        for module_name in self.modules.keys() {
423            in_degree.insert(module_name.clone(), 0);
424            graph.insert(module_name.clone(), Vec::new());
425        }
426
427        // Build the graph and calculate in-degrees
428        for (module_name, dependencies) in &self.dependency_graph {
429            for dependency in dependencies {
430                // Check if dependency exists
431                if !self.modules.contains_key(dependency) {
432                    return Err(ModuleRuntimeError::MissingDependency {
433                        module: module_name.clone(),
434                        missing_dependency: dependency.clone(),
435                        message: "Dependency not registered".to_string(),
436                    });
437                }
438
439                // Add edge from dependency to module
440                graph.get_mut(dependency).unwrap().push(module_name.clone());
441                *in_degree.get_mut(module_name).unwrap() += 1;
442            }
443        }
444
445        let mut queue: VecDeque<String> = in_degree
446            .iter()
447            .filter(|(_, &degree)| degree == 0)
448            .map(|(name, _)| name.clone())
449            .collect();
450
451        let mut result = Vec::new();
452
453        while let Some(current) = queue.pop_front() {
454            result.push(current.clone());
455
456            // Reduce in-degree for all dependents
457            for dependent in &graph[&current] {
458                let degree = in_degree.get_mut(dependent).unwrap();
459                *degree -= 1;
460                if *degree == 0 {
461                    queue.push_back(dependent.clone());
462                }
463            }
464        }
465
466        // Check for cycles
467        if result.len() != self.modules.len() {
468            // Find cycle using DFS
469            let cycle = self.find_cycle()?;
470            return Err(ModuleRuntimeError::CircularDependency {
471                cycle: cycle.clone(),
472                message: format!(
473                    "Circular dependency detected: {}. This creates an infinite loop during module initialization.",
474                    cycle.join(" -> ")
475                ),
476            });
477        }
478
479        Ok(result)
480    }
481
482    /// Find a cycle in the dependency graph using DFS
483    fn find_cycle(&self) -> Result<Vec<String>, ModuleRuntimeError> {
484        let mut visited = HashSet::new();
485        let mut rec_stack = HashSet::new();
486        let mut path = Vec::new();
487
488        for module_name in self.modules.keys() {
489            if !visited.contains(module_name) {
490                if let Some(cycle) =
491                    self.dfs_cycle_detection(module_name, &mut visited, &mut rec_stack, &mut path)?
492                {
493                    return Ok(cycle);
494                }
495            }
496        }
497
498        // Shouldn't reach here if we detected a cycle earlier
499        Ok(vec!["unknown".to_string()])
500    }
501
502    /// DFS helper for cycle detection
503    fn dfs_cycle_detection(
504        &self,
505        current: &str,
506        visited: &mut HashSet<String>,
507        rec_stack: &mut HashSet<String>,
508        path: &mut Vec<String>,
509    ) -> Result<Option<Vec<String>>, ModuleRuntimeError> {
510        visited.insert(current.to_string());
511        rec_stack.insert(current.to_string());
512        path.push(current.to_string());
513
514        if let Some(dependencies) = self.dependency_graph.get(current) {
515            for dep in dependencies {
516                if !visited.contains(dep) {
517                    if let Some(cycle) = self.dfs_cycle_detection(dep, visited, rec_stack, path)? {
518                        return Ok(Some(cycle));
519                    }
520                } else if rec_stack.contains(dep) {
521                    // Found cycle - extract it from path
522                    if let Some(start_idx) = path.iter().position(|x| x == dep) {
523                        let mut cycle = path[start_idx..].to_vec();
524                        cycle.push(dep.clone()); // Complete the cycle
525                        return Ok(Some(cycle));
526                    }
527                }
528            }
529        }
530
531        rec_stack.remove(current);
532        path.pop();
533        Ok(None)
534    }
535
536    /// Task 4.2: Add runtime dependency resolution with clear error reporting
537    pub async fn resolve_dependencies(
538        &mut self,
539        container: &mut IocContainer,
540    ) -> Result<(), ModuleRuntimeError> {
541        let start_time = Instant::now();
542
543        // Ensure load order is calculated
544        if self.load_order.is_empty() {
545            self.calculate_load_order()?;
546        }
547
548        // Process modules in dependency order
549        for module_name in &self.load_order.clone() {
550            self.resolve_module_dependencies(module_name, container)
551                .await?;
552        }
553
554        self.metrics.dependency_resolution_duration = start_time.elapsed();
555        Ok(())
556    }
557
558    /// Resolve dependencies for a specific module
559    async fn resolve_module_dependencies(
560        &mut self,
561        module_name: &str,
562        container: &mut IocContainer,
563    ) -> Result<(), ModuleRuntimeError> {
564        // Update module state
565        if let Some(module_info) = self.modules.get_mut(module_name) {
566            module_info.state = ModuleState::ResolvingDependencies;
567        }
568
569        let dependencies = self
570            .dependency_graph
571            .get(module_name)
572            .cloned()
573            .unwrap_or_default();
574
575        // Verify all dependencies are ready
576        for dep_name in &dependencies {
577            let dep_info = self.modules.get(dep_name).ok_or_else(|| {
578                ModuleRuntimeError::MissingDependency {
579                    module: module_name.to_string(),
580                    missing_dependency: dep_name.clone(),
581                    message: "Dependency module not found in runtime".to_string(),
582                }
583            })?;
584
585            if matches!(dep_info.state, ModuleState::Failed(_)) {
586                return Err(ModuleRuntimeError::InitializationFailed {
587                    module: module_name.to_string(),
588                    error: format!(
589                        "Dependency '{}' failed initialization (state: {:?})",
590                        dep_name, dep_info.state
591                    ),
592                    phase: "dependency_resolution".to_string(),
593                });
594            }
595        }
596
597        // Configure module with container
598        self.configure_module(module_name, container).await?;
599
600        Ok(())
601    }
602
603    /// Task 4.3: Integrate with existing IocContainer and controller registration systems
604    async fn configure_module(
605        &mut self,
606        module_name: &str,
607        container: &mut IocContainer,
608    ) -> Result<(), ModuleRuntimeError> {
609        let start_time = Instant::now();
610
611        // Update module state
612        if let Some(module_info) = self.modules.get_mut(module_name) {
613            module_info.state = ModuleState::Configuring;
614        }
615
616        // Get module descriptor
617        let descriptor = self
618            .modules
619            .get(module_name)
620            .ok_or_else(|| ModuleRuntimeError::MissingDependency {
621                module: module_name.to_string(),
622                missing_dependency: "module_descriptor".to_string(),
623                message: "Module not found".to_string(),
624            })?
625            .descriptor
626            .clone();
627
628        // Configure services with the IoC container
629        self.configure_module_services(&descriptor, container)
630            .await?;
631
632        // Configure controllers
633        self.configure_module_controllers(&descriptor, container)
634            .await?;
635
636        // Update timing and state
637        let config_duration = start_time.elapsed();
638        if let Some(module_info) = self.modules.get_mut(module_name) {
639            module_info.config_duration = Some(config_duration);
640            module_info.state = ModuleState::Initializing;
641        }
642
643        Ok(())
644    }
645
646    /// Configure module services with the IoC container
647    async fn configure_module_services(
648        &self,
649        descriptor: &ModuleDescriptor,
650        _container: &mut IocContainer,
651    ) -> Result<(), ModuleRuntimeError> {
652        for service in &descriptor.providers {
653            match (service.implementation_type, &service.name) {
654                // Named trait service
655                (Some(_), Some(name)) if service.is_trait_service => {
656                    // For trait services, we would need token-based binding
657                    // For now, we'll skip these and log a warning
658                    tracing::warn!(
659                        "Trait service '{}' with name '{}' requires token-based binding (not yet fully integrated)",
660                        service.service_name, name
661                    );
662                }
663                // Named concrete service
664                (None, Some(name)) => {
665                    // This is a named concrete service binding
666                    // We would need to bind by type + name, but this requires more complex integration
667                    tracing::warn!(
668                        "Named concrete service '{}' with name '{}' requires enhanced binding support",
669                        service.service_name, name
670                    );
671                }
672                // Regular trait service (unnamed)
673                (Some(_), None) if service.is_trait_service => {
674                    // Trait service without name - requires token-based binding
675                    tracing::warn!(
676                        "Trait service '{}' requires token-based binding (not yet fully integrated)",
677                        service.service_name
678                    );
679                }
680                // Regular concrete service (most common case)
681                (None, None) => {
682                    // This is a basic concrete service - we can't bind it without knowing the actual types
683                    // The current system needs compile-time type information that we don't have at runtime
684                    tracing::info!(
685                        "Concrete service '{}' registered (runtime binding not yet implemented)",
686                        service.service_name
687                    );
688                }
689                // Other cases
690                _ => {
691                    tracing::warn!(
692                        "Unknown service configuration for '{}' - skipping",
693                        service.service_name
694                    );
695                }
696            }
697        }
698
699        Ok(())
700    }
701
702    /// Configure module controllers  
703    async fn configure_module_controllers(
704        &self,
705        descriptor: &ModuleDescriptor,
706        _container: &mut IocContainer,
707    ) -> Result<(), ModuleRuntimeError> {
708        for controller in &descriptor.controllers {
709            // Controller registration would be integrated with HTTP routing system
710            // For now, we'll track them for future integration
711            tracing::info!(
712                "Controller '{}' registered (HTTP routing integration pending)",
713                controller.controller_name
714            );
715        }
716
717        Ok(())
718    }
719
720    /// Task 4.4: Add module lifecycle hooks (startup, shutdown, health checks)
721    pub async fn initialize_all_modules(
722        &mut self,
723        container: &IocContainer,
724    ) -> Result<(), ModuleRuntimeError> {
725        let start_time = Instant::now();
726
727        for module_name in &self.load_order.clone() {
728            self.initialize_module(module_name, container).await?;
729        }
730
731        self.metrics.initialization_duration = start_time.elapsed();
732        self.calculate_performance_metrics();
733
734        Ok(())
735    }
736
737    /// Initialize a specific module
738    async fn initialize_module(
739        &mut self,
740        module_name: &str,
741        _container: &IocContainer,
742    ) -> Result<(), ModuleRuntimeError> {
743        let init_start = Instant::now();
744
745        // Call before_init hook
746        if let Some(hook) = self.lifecycle_hooks.get(module_name) {
747            hook.before_init(module_name)?;
748        }
749
750        // Update module state
751        if let Some(module_info) = self.modules.get_mut(module_name) {
752            module_info.state = ModuleState::Initializing;
753        }
754
755        // Here we would call the actual module initialization
756        // For now, we'll simulate with a small delay
757        tokio::time::sleep(Duration::from_millis(10)).await;
758
759        let init_duration = init_start.elapsed();
760
761        // Update module state and timing
762        if let Some(module_info) = self.modules.get_mut(module_name) {
763            module_info.state = ModuleState::Ready;
764            module_info.init_duration = Some(init_duration);
765        }
766
767        // Call after_init hook
768        if let Some(hook) = self.lifecycle_hooks.get(module_name) {
769            if let Err(e) = hook.after_init(module_name, init_duration) {
770                // Mark module as failed if after_init hook fails
771                if let Some(module_info) = self.modules.get_mut(module_name) {
772                    module_info.state = ModuleState::Failed(e.to_string());
773                    module_info.add_error(e.to_string());
774                }
775                return Err(e);
776            }
777        }
778
779        Ok(())
780    }
781
782    /// Shutdown all modules in reverse order
783    pub async fn shutdown_all_modules(&mut self) -> Result<(), ModuleRuntimeError> {
784        let mut shutdown_order = self.load_order.clone();
785        shutdown_order.reverse();
786
787        for module_name in shutdown_order {
788            self.shutdown_module(&module_name).await?;
789        }
790
791        Ok(())
792    }
793
794    /// Shutdown a specific module
795    async fn shutdown_module(&mut self, module_name: &str) -> Result<(), ModuleRuntimeError> {
796        // Call before_shutdown hook
797        if let Some(hook) = self.lifecycle_hooks.get(module_name) {
798            hook.before_shutdown(module_name)?;
799        }
800
801        // Update module state
802        if let Some(module_info) = self.modules.get_mut(module_name) {
803            module_info.state = ModuleState::ShuttingDown;
804        }
805
806        // Perform actual shutdown operations here
807        tokio::time::sleep(Duration::from_millis(5)).await;
808
809        // Update state
810        if let Some(module_info) = self.modules.get_mut(module_name) {
811            module_info.state = ModuleState::Shutdown;
812        }
813
814        // Call after_shutdown hook
815        if let Some(hook) = self.lifecycle_hooks.get(module_name) {
816            hook.after_shutdown(module_name)?;
817        }
818
819        Ok(())
820    }
821
822    /// Run health checks for all modules
823    pub async fn health_check_all_modules(
824        &mut self,
825    ) -> Result<HashMap<String, HealthStatus>, ModuleRuntimeError> {
826        let mut health_results = HashMap::new();
827        let check_time = Instant::now();
828
829        for module_name in &self.load_order.clone() {
830            let health_status = if let Some(hook) = self.lifecycle_hooks.get(module_name) {
831                hook.health_check(module_name)
832                    .unwrap_or(HealthStatus::Unknown)
833            } else {
834                // Default health check - if module is ready, it's healthy
835                match self.modules.get(module_name).map(|m| &m.state) {
836                    Some(ModuleState::Ready) => HealthStatus::Healthy,
837                    Some(ModuleState::Failed(_)) => HealthStatus::Unhealthy,
838                    _ => HealthStatus::Unknown,
839                }
840            };
841
842            // Update module health status
843            if let Some(module_info) = self.modules.get_mut(module_name) {
844                module_info.health_status = health_status.clone();
845                module_info.last_health_check = Some(check_time);
846            }
847
848            health_results.insert(module_name.clone(), health_status);
849        }
850
851        Ok(health_results)
852    }
853
854    /// Calculate performance metrics
855    fn calculate_performance_metrics(&mut self) {
856        self.metrics.total_modules = self.modules.len();
857
858        if self.metrics.total_modules > 0 {
859            let total_init_time: Duration =
860                self.modules.values().filter_map(|m| m.init_duration).sum();
861
862            self.metrics.avg_init_time_per_module =
863                total_init_time / self.metrics.total_modules as u32;
864
865            // Find slowest module
866            let slowest = self
867                .modules
868                .iter()
869                .filter_map(|(name, info)| info.init_duration.map(|d| (name, d)))
870                .max_by_key(|(_, duration)| *duration);
871
872            if let Some((name, duration)) = slowest {
873                self.metrics.slowest_module = Some(name.clone());
874                self.metrics.slowest_init_time = duration;
875            }
876        }
877    }
878
879    /// Get module runtime information
880    pub fn get_module_info(&self, module_name: &str) -> Option<&ModuleRuntimeInfo> {
881        self.modules.get(module_name)
882    }
883
884    /// Get all module runtime information
885    pub fn get_all_module_info(&self) -> &HashMap<String, ModuleRuntimeInfo> {
886        &self.modules
887    }
888
889    /// Get load order
890    pub fn get_load_order(&self) -> &[String] {
891        &self.load_order
892    }
893
894    /// Get performance metrics
895    pub fn get_performance_metrics(&self) -> &ModulePerformanceMetrics {
896        &self.metrics
897    }
898
899    /// Validate runtime state
900    pub fn validate_runtime_state(&self) -> Result<(), Vec<ModuleRuntimeError>> {
901        let mut errors = Vec::new();
902
903        // Check that all modules are in a valid state
904        for (name, info) in &self.modules {
905            match &info.state {
906                ModuleState::Failed(err) => {
907                    errors.push(ModuleRuntimeError::InitializationFailed {
908                        module: name.clone(),
909                        error: err.clone(),
910                        phase: "runtime_validation".to_string(),
911                    });
912                }
913                ModuleState::ResolvingDependencies
914                | ModuleState::Configuring
915                | ModuleState::Initializing => {
916                    errors.push(ModuleRuntimeError::InitializationFailed {
917                        module: name.clone(),
918                        error: "Module stuck in intermediate state".to_string(),
919                        phase: format!("{:?}", info.state),
920                    });
921                }
922                _ => {}
923            }
924
925            // Check for modules with too many errors
926            if info.errors.len() > 5 {
927                errors.push(ModuleRuntimeError::ValidationFailed {
928                    module: name.clone(),
929                    validation_errors: vec![format!("Module has {} errors", info.errors.len())],
930                });
931            }
932        }
933
934        // Check load order consistency
935        if self.load_order.len() != self.modules.len() {
936            errors.push(ModuleRuntimeError::ValidationFailed {
937                module: "runtime".to_string(),
938                validation_errors: vec![format!(
939                    "Load order length ({}) doesn't match module count ({})",
940                    self.load_order.len(),
941                    self.modules.len()
942                )],
943            });
944        }
945
946        if errors.is_empty() {
947            Ok(())
948        } else {
949            Err(errors)
950        }
951    }
952
953    /// Get mutable access to modules (for testing purposes)
954    #[cfg(test)]
955    pub fn modules_mut(&mut self) -> &mut HashMap<String, ModuleRuntimeInfo> {
956        &mut self.modules
957    }
958
959    /// Get runtime statistics for monitoring
960    pub fn get_runtime_statistics(&self) -> ModuleRuntimeStatistics {
961        let mut stats = ModuleRuntimeStatistics::default();
962
963        stats.total_modules = self.modules.len();
964
965        for info in self.modules.values() {
966            match &info.state {
967                ModuleState::Registered => stats.registered_modules += 1,
968                ModuleState::ResolvingDependencies => stats.resolving_modules += 1,
969                ModuleState::Configuring => stats.configuring_modules += 1,
970                ModuleState::Initializing => stats.initializing_modules += 1,
971                ModuleState::Ready => stats.ready_modules += 1,
972                ModuleState::Failed(_) => stats.failed_modules += 1,
973                ModuleState::ShuttingDown => stats.shutting_down_modules += 1,
974                ModuleState::Shutdown => stats.shutdown_modules += 1,
975            }
976
977            match &info.health_status {
978                HealthStatus::Healthy => stats.healthy_modules += 1,
979                HealthStatus::Degraded => stats.degraded_modules += 1,
980                HealthStatus::Unhealthy => stats.unhealthy_modules += 1,
981                HealthStatus::Unknown => stats.unknown_health_modules += 1,
982            }
983        }
984
985        stats.performance_metrics = self.metrics.clone();
986
987        stats
988    }
989}
990
991impl Default for ModuleRuntime {
992    fn default() -> Self {
993        Self::new()
994    }
995}
996
997impl std::fmt::Debug for ModuleRuntime {
998    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
999        f.debug_struct("ModuleRuntime")
1000            .field("modules", &self.modules)
1001            .field("dependency_graph", &self.dependency_graph)
1002            .field("load_order", &self.load_order)
1003            .field("metrics", &self.metrics)
1004            .field("lifecycle_hooks", &format!("{} hooks", self.lifecycle_hooks.len()))
1005            .field("health_check_config", &self.health_check_config)
1006            .finish()
1007    }
1008}
1009
1010/// Runtime statistics for monitoring and debugging
1011#[derive(Debug, Default, Clone)]
1012pub struct ModuleRuntimeStatistics {
1013    pub total_modules: usize,
1014    pub registered_modules: usize,
1015    pub resolving_modules: usize,
1016    pub configuring_modules: usize,
1017    pub initializing_modules: usize,
1018    pub ready_modules: usize,
1019    pub failed_modules: usize,
1020    pub shutting_down_modules: usize,
1021    pub shutdown_modules: usize,
1022    pub healthy_modules: usize,
1023    pub degraded_modules: usize,
1024    pub unhealthy_modules: usize,
1025    pub unknown_health_modules: usize,
1026    pub performance_metrics: ModulePerformanceMetrics,
1027}
1028
1029/// Default lifecycle hook implementation
1030pub struct DefaultLifecycleHook;
1031
1032impl ModuleLifecycleHook for DefaultLifecycleHook {
1033    fn before_init(&self, module_name: &str) -> Result<(), ModuleRuntimeError> {
1034        tracing::info!("Starting initialization of module '{}'", module_name);
1035        Ok(())
1036    }
1037
1038    fn after_init(&self, module_name: &str, duration: Duration) -> Result<(), ModuleRuntimeError> {
1039        tracing::info!(
1040            "Module '{}' initialized successfully in {:?}",
1041            module_name,
1042            duration
1043        );
1044        Ok(())
1045    }
1046
1047    fn on_init_failure(&self, module_name: &str, error: &ModuleRuntimeError) {
1048        tracing::error!("Module '{}' initialization failed: {}", module_name, error);
1049    }
1050
1051    fn health_check(&self, module_name: &str) -> Result<HealthStatus, ModuleRuntimeError> {
1052        // Default implementation - always healthy
1053        let _ = module_name;
1054        Ok(HealthStatus::Healthy)
1055    }
1056}
1057
1058#[cfg(test)]
1059mod tests {
1060    use super::*;
1061
1062    fn create_test_module(name: &str, dependencies: Vec<String>) -> ModuleDescriptor {
1063        ModuleDescriptor::new(name)
1064            .with_dependencies(dependencies)
1065            .with_description(format!("Test module {}", name))
1066    }
1067
1068    #[tokio::test]
1069    async fn test_topological_sorting_simple() {
1070        let mut runtime = ModuleRuntime::new();
1071
1072        // A -> B -> C dependency chain
1073        runtime
1074            .register_module(create_test_module("A", vec![]))
1075            .unwrap();
1076        runtime
1077            .register_module(create_test_module("B", vec!["A".to_string()]))
1078            .unwrap();
1079        runtime
1080            .register_module(create_test_module("C", vec!["B".to_string()]))
1081            .unwrap();
1082
1083        let load_order = runtime.calculate_load_order().unwrap();
1084
1085        assert_eq!(load_order, vec!["A", "B", "C"]);
1086    }
1087
1088    #[tokio::test]
1089    async fn test_circular_dependency_detection() {
1090        let mut runtime = ModuleRuntime::new();
1091
1092        // A -> B -> C -> A (circular)
1093        runtime
1094            .register_module(create_test_module("A", vec!["C".to_string()]))
1095            .unwrap();
1096        runtime
1097            .register_module(create_test_module("B", vec!["A".to_string()]))
1098            .unwrap();
1099        runtime
1100            .register_module(create_test_module("C", vec!["B".to_string()]))
1101            .unwrap();
1102
1103        let result = runtime.calculate_load_order();
1104
1105        assert!(result.is_err());
1106        match result.unwrap_err() {
1107            ModuleRuntimeError::CircularDependency { cycle, .. } => {
1108                assert!(cycle.len() >= 3);
1109            }
1110            _ => panic!("Expected CircularDependency error"),
1111        }
1112    }
1113
1114    #[tokio::test]
1115    async fn test_missing_dependency_detection() {
1116        let mut runtime = ModuleRuntime::new();
1117
1118        runtime
1119            .register_module(create_test_module("A", vec!["NonExistent".to_string()]))
1120            .unwrap();
1121
1122        let result = runtime.calculate_load_order();
1123
1124        assert!(result.is_err());
1125        match result.unwrap_err() {
1126            ModuleRuntimeError::MissingDependency {
1127                module,
1128                missing_dependency,
1129                ..
1130            } => {
1131                assert_eq!(module, "A");
1132                assert_eq!(missing_dependency, "NonExistent");
1133            }
1134            _ => panic!("Expected MissingDependency error"),
1135        }
1136    }
1137
1138    #[tokio::test]
1139    async fn test_complex_dependency_graph() {
1140        let mut runtime = ModuleRuntime::new();
1141
1142        // Complex dependency graph:
1143        // A (no deps)
1144        // B -> A
1145        // C -> A
1146        // D -> B, C
1147        // E -> D
1148        runtime
1149            .register_module(create_test_module("A", vec![]))
1150            .unwrap();
1151        runtime
1152            .register_module(create_test_module("B", vec!["A".to_string()]))
1153            .unwrap();
1154        runtime
1155            .register_module(create_test_module("C", vec!["A".to_string()]))
1156            .unwrap();
1157        runtime
1158            .register_module(create_test_module(
1159                "D",
1160                vec!["B".to_string(), "C".to_string()],
1161            ))
1162            .unwrap();
1163        runtime
1164            .register_module(create_test_module("E", vec!["D".to_string()]))
1165            .unwrap();
1166
1167        let load_order = runtime.calculate_load_order().unwrap();
1168
1169        // A should be first
1170        assert_eq!(load_order[0], "A");
1171
1172        // B and C should come after A but before D
1173        let a_pos = load_order.iter().position(|x| x == "A").unwrap();
1174        let b_pos = load_order.iter().position(|x| x == "B").unwrap();
1175        let c_pos = load_order.iter().position(|x| x == "C").unwrap();
1176        let d_pos = load_order.iter().position(|x| x == "D").unwrap();
1177        let e_pos = load_order.iter().position(|x| x == "E").unwrap();
1178
1179        assert!(a_pos < b_pos);
1180        assert!(a_pos < c_pos);
1181        assert!(b_pos < d_pos);
1182        assert!(c_pos < d_pos);
1183        assert!(d_pos < e_pos);
1184    }
1185
1186    #[tokio::test]
1187    async fn test_module_lifecycle_hooks() {
1188        let mut runtime = ModuleRuntime::new();
1189        runtime
1190            .register_module(create_test_module("TestModule", vec![]))
1191            .unwrap();
1192
1193        // Add default lifecycle hook
1194        runtime.add_lifecycle_hook("TestModule".to_string(), DefaultLifecycleHook);
1195
1196        // Calculate load order
1197        runtime.calculate_load_order().unwrap();
1198
1199        // Create mock container
1200        let mut container = IocContainer::new();
1201        container.build().unwrap();
1202
1203        // Test initialization with hooks
1204        let result = runtime.initialize_all_modules(&container).await;
1205        assert!(result.is_ok());
1206
1207        // Verify module is ready
1208        let module_info = runtime.get_module_info("TestModule").unwrap();
1209        assert_eq!(module_info.state, ModuleState::Ready);
1210        assert!(module_info.init_duration.is_some());
1211    }
1212
1213    #[tokio::test]
1214    async fn test_health_checks() {
1215        let mut runtime = ModuleRuntime::new();
1216        runtime
1217            .register_module(create_test_module("TestModule", vec![]))
1218            .unwrap();
1219        runtime.add_lifecycle_hook("TestModule".to_string(), DefaultLifecycleHook);
1220
1221        runtime.calculate_load_order().unwrap();
1222
1223        let mut container = IocContainer::new();
1224        container.build().unwrap();
1225        runtime.initialize_all_modules(&container).await.unwrap();
1226
1227        // Run health checks
1228        let health_results = runtime.health_check_all_modules().await.unwrap();
1229
1230        assert_eq!(health_results.len(), 1);
1231        assert_eq!(health_results["TestModule"], HealthStatus::Healthy);
1232    }
1233
1234    #[tokio::test]
1235    async fn test_runtime_validation() {
1236        let mut runtime = ModuleRuntime::new();
1237        runtime
1238            .register_module(create_test_module("TestModule", vec![]))
1239            .unwrap();
1240
1241        runtime.calculate_load_order().unwrap();
1242
1243        let mut container = IocContainer::new();
1244        container.build().unwrap();
1245        runtime.initialize_all_modules(&container).await.unwrap();
1246
1247        // Validate runtime state
1248        let validation_result = runtime.validate_runtime_state();
1249        assert!(validation_result.is_ok());
1250    }
1251
1252    #[tokio::test]
1253    async fn test_performance_metrics() {
1254        let mut runtime = ModuleRuntime::new();
1255
1256        for i in 0..10 {
1257            runtime
1258                .register_module(create_test_module(&format!("Module{}", i), vec![]))
1259                .unwrap();
1260        }
1261
1262        runtime.calculate_load_order().unwrap();
1263
1264        let mut container = IocContainer::new();
1265        container.build().unwrap();
1266        runtime.initialize_all_modules(&container).await.unwrap();
1267
1268        let metrics = runtime.get_performance_metrics();
1269
1270        assert_eq!(metrics.total_modules, 10);
1271        assert!(metrics.initialization_duration > Duration::ZERO);
1272        assert!(metrics.avg_init_time_per_module > Duration::ZERO);
1273    }
1274
1275    #[tokio::test]
1276    async fn test_shutdown_order() {
1277        let mut runtime = ModuleRuntime::new();
1278
1279        runtime
1280            .register_module(create_test_module("A", vec![]))
1281            .unwrap();
1282        runtime
1283            .register_module(create_test_module("B", vec!["A".to_string()]))
1284            .unwrap();
1285        runtime
1286            .register_module(create_test_module("C", vec!["B".to_string()]))
1287            .unwrap();
1288
1289        runtime.calculate_load_order().unwrap();
1290
1291        let mut container = IocContainer::new();
1292        container.build().unwrap();
1293        runtime.initialize_all_modules(&container).await.unwrap();
1294
1295        // Shutdown should happen in reverse order
1296        runtime.shutdown_all_modules().await.unwrap();
1297
1298        // All modules should be shut down
1299        for info in runtime.get_all_module_info().values() {
1300            assert_eq!(info.state, ModuleState::Shutdown);
1301        }
1302    }
1303}