//! Registry for managing ecosystem integrations

use crate::ecosystem::{EcosystemIntegration, EcosystemIntegrationError, IntegrationMetadata};
use std::collections::HashMap;
use std::sync::Arc;
use tracing::{debug, info};

/// Registry for ecosystem integrations
pub struct IntegrationRegistry {
    integrations: HashMap<String, Arc<dyn EcosystemIntegration>>,
}

impl IntegrationRegistry {
    /// Create a new empty registry
    pub fn new() -> Self {
        Self {
            integrations: HashMap::new(),
        }
    }

    /// Register an integration
    pub fn register(
        &mut self,
        integration: Arc<dyn EcosystemIntegration>,
    ) -> Result<(), EcosystemIntegrationError> {
        let name = integration.name().to_string();

        if self.integrations.contains_key(&name) {
            return Err(EcosystemIntegrationError::IntegrationError(format!(
                "Integration '{}' already registered",
                name
            )));
        }

        info!("Registering ecosystem integration: {}", name);
        self.integrations.insert(name, integration);
        Ok(())
    }

    /// Get an integration by name
    pub fn get(
        &self,
        name: &str,
    ) -> Result<Arc<dyn EcosystemIntegration>, EcosystemIntegrationError> {
        self.integrations.get(name).map(Arc::clone).ok_or(
            EcosystemIntegrationError::NotFound {
                name: name.to_string(),
            },
        )
    }

    /// Get all registered integrations
    pub fn all(&self) -> Vec<Arc<dyn EcosystemIntegration>> {
        self.integrations.values().cloned().collect()
    }

    /// Get all available integrations (those that are configured and available)
    pub fn available(&self) -> Vec<Arc<dyn EcosystemIntegration>> {
        self.integrations
            .values()
            .filter(|i| i.is_available())
            .cloned()
            .collect()
    }

    /// Get metadata for all integrations
    pub fn metadata(&self) -> Vec<IntegrationMetadata> {
        self.integrations.values().map(|i| i.metadata()).collect()
    }

    /// Check if integration is registered
    pub fn has(&self, name: &str) -> bool {
        self.integrations.contains_key(name)
    }

    /// Get number of registered integrations
    pub fn count(&self) -> usize {
        self.integrations.len()
    }

    /// Get number of available integrations
    pub fn available_count(&self) -> usize {
        self.integrations
            .values()
            .filter(|i| i.is_available())
            .count()
    }

    /// Unregister an integration
    pub fn unregister(&mut self, name: &str) -> Result<(), EcosystemIntegrationError> {
        if self.integrations.remove(name).is_some() {
            info!("Unregistered ecosystem integration: {}", name);
            Ok(())
        } else {
            Err(EcosystemIntegrationError::NotFound {
                name: name.to_string(),
            })
        }
    }

    /// Clear all registrations
    pub fn clear(&mut self) {
        debug!("Clearing all ecosystem integrations");
        self.integrations.clear();
    }
}

impl Default for IntegrationRegistry {
    fn default() -> Self {
        Self::new()
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use async_trait::async_trait;

    struct TestIntegration {
        name: String,
    }

    #[async_trait]
    impl EcosystemIntegration for TestIntegration {
        fn name(&self) -> &str {
            &self.name
        }
    }

    #[test]
    fn test_registry_register() {
        let mut registry = IntegrationRegistry::new();
        let integration = Arc::new(TestIntegration {
            name: "test".to_string(),
        });

        assert!(registry.register(integration).is_ok());
        assert!(registry.has("test"));
        assert_eq!(registry.count(), 1);
    }

    #[test]
    fn test_registry_duplicate_registration() {
        let mut registry = IntegrationRegistry::new();
        let integration = Arc::new(TestIntegration {
            name: "test".to_string(),
        });

        assert!(registry.register(integration.clone()).is_ok());
        assert!(registry.register(integration).is_err());
    }

    #[test]
    fn test_registry_get() {
        let mut registry = IntegrationRegistry::new();
        let integration = Arc::new(TestIntegration {
            name: "test".to_string(),
        });

        registry.register(integration).unwrap();

        let retrieved = registry.get("test").unwrap();
        assert_eq!(retrieved.name(), "test");
    }

    #[test]
    fn test_registry_get_not_found() {
        let registry = IntegrationRegistry::new();
        assert!(registry.get("nonexistent").is_err());
    }

    #[test]
    fn test_registry_unregister() {
        let mut registry = IntegrationRegistry::new();
        let integration = Arc::new(TestIntegration {
            name: "test".to_string(),
        });

        registry.register(integration).unwrap();
        assert_eq!(registry.count(), 1);

        assert!(registry.unregister("test").is_ok());
        assert_eq!(registry.count(), 0);
        assert!(!registry.has("test"));
    }

    #[test]
    fn test_registry_clear() {
        let mut registry = IntegrationRegistry::new();

        for i in 0..3 {
            let integration = Arc::new(TestIntegration {
                name: format!("test{}", i),
            });
            registry.register(integration).unwrap();
        }

        assert_eq!(registry.count(), 3);
        registry.clear();
        assert_eq!(registry.count(), 0);
    }

    #[test]
    fn test_registry_metadata() {
        let mut registry = IntegrationRegistry::new();
        let integration = Arc::new(TestIntegration {
            name: "test".to_string(),
        });

        registry.register(integration).unwrap();

        let metadata = registry.metadata();
        assert_eq!(metadata.len(), 1);
        assert_eq!(metadata[0].name, "test");
    }
}
