/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved.
 * Global Trust Authority is licensed under the Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *     http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
 * PURPOSE.
 * See the Mulan PSL v2 for more details.
 */

// Plugin manager implementation

use libloading::{Library, Symbol};
use std::collections::HashMap;
use std::error::Error;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{Arc, OnceLock, RwLock};

use log::{error, info};

use crate::host_functions::{AgentHostFunctions, HostFunctions, ServiceHostFunctions};
use crate::traits::{AgentPlugin, PluginBase, PluginManagerInstance, ServicePlugin};

/// Plugin storage structure
pub struct PluginEntry {
    pub(crate) _lib: Library, // Keep the library loaded
}

/// Generic plugin creation function type that takes a specific host function type
pub type CreatePluginFn<T, H> = fn(&H, &str) -> Result<Box<T>, Box<dyn Error>>;

/// Generic plugin manager that can work with any plugin type and host function type
pub struct PluginManager<T: PluginBase + ?Sized, H: HostFunctions> {
    // Store plugins with a single Arc layer for shared ownership
    plugins: RwLock<HashMap<String, (Arc<T>, PluginEntry)>>,
    // Track initialization state
    initialized: AtomicBool,
    // Phantom data to track the host function type
    _phantom: std::marker::PhantomData<H>,
}

impl<T: PluginBase + ?Sized + 'static, H: HostFunctions> PluginManager<T, H> {
    /// Create a new `PluginManager`
    fn new() -> Self {
        Self {
            plugins: RwLock::new(HashMap::new()),
            initialized: AtomicBool::new(false),
            _phantom: std::marker::PhantomData,
        }
    }

    /// Register a plugin with this manager
    fn register_plugin(&self, name: String, plugin: Box<T>, lib: Library) -> Result<(), String> {
        let mut plugins =
            self.plugins.write().map_err(|e| format!("Failed to acquire write lock for plugin registration: {e}"))?;
        let entry = PluginEntry { _lib: lib };

        plugins.insert(name, (Arc::from(plugin), entry));
        Ok(())
    }

    /// Get a plugin by name - returns an Arc to the plugin
    pub fn get_plugin(&self, name: &str) -> Option<Arc<T>> {
        let plugins = self.plugins.read().ok()?;
        plugins.get(name).map(|(plugin, _)| plugin.clone())
    }

    /// Get all plugin names
    pub fn get_plugin_types(&self) -> Vec<String> {
        match self.plugins.read() {
            Ok(plugins) => plugins.keys().cloned().collect(),
            Err(e) => {
                error!("Failed to acquire read lock for plugin types: {e}");
                Vec::new()
            },
        }
    }

    /// Check if the manager has been successfully initialized
    pub fn is_initialized(&self) -> bool {
        self.initialized.load(Ordering::Relaxed)
    }

    /// Helper function to load a single plugin
    /// Returns Ok(()) if successful, Err with error message otherwise
    ///
    /// ## Safety Requirements:
    ///
    /// - **Trusted source**: Plugin libraries must be from trusted sources (e.g.,
    ///   compiled from the same codebase with proper build verification).
    /// - **ABI compatibility**: Plugins must be compiled with the same Rust version
    ///   and compatible compiler flags to ensure ABI stability.
    /// - **Correct signature**: The `create_plugin` symbol must have the exact signature
    ///   specified by `CreatePluginFn<T, H>`.
    /// - **Memory safety**: Plugin code must not violate Rust's safety guarantees
    ///   (no data races, no use-after-free, etc.).
    /// - **Path validation**: The `path` parameter should be validated to prevent
    ///   loading plugins from untrusted locations.
    unsafe fn load_plugin(&self, name: &str, path: &str, host_functions: &H) -> Result<(), String> {
        // Try to load the library
        let lib = Library::new(path).map_err(|e| format!("Failed to load library {path}: {e}"))?;

        // Try to get the create_plugin symbol
        let constructor = lib
            .get::<Symbol<'_, CreatePluginFn<T, H>>>(b"create_plugin")
            .map_err(|e| format!("Failed to find create_plugin symbol: {e}"))?;

        // Try to create the plugin
        let plugin = constructor(host_functions, name).map_err(|e| format!("Plugin {name} creation failed for {e}"))?;

        if plugin.plugin_type() != name {
            return Err(format!("Plugin type mismatch for {name}"));
        }

        // Register the plugin
        self.register_plugin(name.to_string(), plugin, lib)?;
        Ok(())
    }

    /// Load plugins from a `HashMap` of plugin names and paths
    /// Returns true if all plugins were loaded successfully, false otherwise
    pub fn initialize(&self, plugin_paths: &HashMap<String, String>, host_functions: &H) -> bool {
        info!("Initializing plugin manager with {} plugins", plugin_paths.len());
        let mut all_successful = true;

        for (name, path) in plugin_paths {
            info!("Loading plugin '{name}' from path: {path}");
            // SAFETY: This unsafe block calls load_plugin() which performs dynamic library loading.
            // Safety requirements:
            // - Plugin paths must come from trusted configuration sources
            // - Plugin libraries must be compiled with compatible Rust toolchain
            // - Plugins must implement the required traits correctly
            // See load_plugin() documentation for detailed safety requirements.
            let result = unsafe { self.load_plugin(name, path, host_functions) };
            if let Err(error) = result {
                error!("Error loading plugin {name}: {error}");
                all_successful = false;
            } else {
                info!("Successfully registered plugin: {name}");
            }
        }

        // Set the initialization state based on the result
        self.initialized.store(all_successful, Ordering::Relaxed);
        if all_successful {
            info!("Plugin manager successfully initialized with all plugins");
        } else {
            error!("Plugin manager initialization completed with errors");
        }
        all_successful
    }
}

// Singleton implementations for different plugin types

// Implementation for ServicePlugin manager
impl PluginManagerInstance for PluginManager<dyn ServicePlugin, ServiceHostFunctions> {
    fn get_instance() -> &'static Self {
        static INSTANCE: OnceLock<PluginManager<dyn ServicePlugin, ServiceHostFunctions>> = OnceLock::new();
        INSTANCE.get_or_init(|| PluginManager::new())
    }
}

// Implementation for AgentPlugin manager
impl PluginManagerInstance for PluginManager<dyn AgentPlugin, AgentHostFunctions> {
    fn get_instance() -> &'static Self {
        static INSTANCE: OnceLock<PluginManager<dyn AgentPlugin, AgentHostFunctions>> = OnceLock::new();
        INSTANCE.get_or_init(|| PluginManager::new())
    }
}