//! Extension Loading Module
//!
//! Manages dynamic loading of extensions (providers, taskservs, clusters)
//! by calling Nushell extension loader scripts.

pub mod loader;

use std::collections::HashMap;
use std::sync::Arc;

use anyhow::Result;
pub use loader::{Extension, ExtensionLoader, ExtensionMetadata, ExtensionType};
use tokio::sync::RwLock;
use tracing::{debug, info};

/// Extension manager for coordinating extension loading
pub struct ExtensionManager {
    loader: ExtensionLoader,
    loaded_extensions: Arc<RwLock<HashMap<String, Extension>>>,
}

impl ExtensionManager {
    /// Create new extension manager
    pub fn new(nu_path: String, provisioning_path: String) -> Self {
        Self {
            loader: ExtensionLoader::new(nu_path, provisioning_path),
            loaded_extensions: Arc::new(RwLock::new(HashMap::new())),
        }
    }

    /// Load extension dynamically
    pub async fn load_extension(
        &self,
        extension_type: ExtensionType,
        name: String,
        version: Option<String>,
    ) -> Result<Extension> {
        let cache_key = format!(
            "{}:{}:{}",
            extension_type,
            name,
            version.as_deref().unwrap_or("latest")
        );

        // Check cache first
        {
            let extensions = self.loaded_extensions.read().await;
            if let Some(extension) = extensions.get(&cache_key) {
                debug!("Extension {} found in cache", cache_key);
                return Ok(extension.clone());
            }
        }

        info!("Loading extension: {} ({})", name, extension_type);

        // Load extension via Nushell
        let extension = self
            .loader
            .load_extension(extension_type, name.clone(), version)
            .await?;

        // Cache loaded extension
        {
            let mut extensions = self.loaded_extensions.write().await;
            extensions.insert(cache_key.clone(), extension.clone());
        }

        info!("Successfully loaded extension: {}", cache_key);

        Ok(extension)
    }

    /// List loaded extensions
    pub async fn list_loaded_extensions(&self) -> Vec<Extension> {
        let extensions = self.loaded_extensions.read().await;
        extensions.values().cloned().collect()
    }

    /// Reload extension (clear cache and load again)
    pub async fn reload_extension(
        &self,
        extension_type: ExtensionType,
        name: String,
    ) -> Result<Extension> {
        let cache_pattern = format!("{}:{}:", extension_type, name);

        // Remove from cache
        {
            let mut extensions = self.loaded_extensions.write().await;
            extensions.retain(|k, _| !k.starts_with(&cache_pattern));
        }

        info!("Reloading extension: {} ({})", name, extension_type);

        // Load fresh
        self.load_extension(extension_type, name, None).await
    }

    /// Clear all loaded extensions from cache
    pub async fn clear_cache(&self) {
        let mut extensions = self.loaded_extensions.write().await;
        let count = extensions.len();
        extensions.clear();
        info!("Cleared {} extensions from cache", count);
    }

    /// Get extension metadata
    pub async fn get_extension_metadata(
        &self,
        extension_type: ExtensionType,
        name: String,
    ) -> Result<ExtensionMetadata> {
        self.loader
            .get_extension_metadata(extension_type, name)
            .await
    }

    /// List available extensions (not loaded)
    pub async fn list_available_extensions(
        &self,
        extension_type: ExtensionType,
    ) -> Result<Vec<String>> {
        self.loader.list_available_extensions(extension_type).await
    }

    /// Check if extension is loaded
    pub async fn is_extension_loaded(&self, extension_type: ExtensionType, name: &str) -> bool {
        let extensions = self.loaded_extensions.read().await;
        let pattern = format!("{}:{}:", extension_type, name);
        extensions.keys().any(|k| k.starts_with(&pattern))
    }
}

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

    #[tokio::test]
    async fn test_extension_manager_creation() {
        let manager = ExtensionManager::new(
            "/usr/local/bin/nu".to_string(),
            "/usr/local/bin/provisioning".to_string(),
        );

        let loaded = manager.list_loaded_extensions().await;
        assert_eq!(loaded.len(), 0);
    }

    #[tokio::test]
    async fn test_extension_cache_operations() {
        let manager = ExtensionManager::new(
            "/usr/local/bin/nu".to_string(),
            "/usr/local/bin/provisioning".to_string(),
        );

        // Initially nothing loaded
        assert!(
            !manager
                .is_extension_loaded(ExtensionType::Taskserv, "kubernetes")
                .await
        );

        // Clear empty cache
        manager.clear_cache().await;

        let loaded = manager.list_loaded_extensions().await;
        assert_eq!(loaded.len(), 0);
    }
}
