/// Shared Tokio Runtime Manager
/// 
/// Provides a global shared Tokio runtime handle for use across
/// the entire application, especially for Python bindings.
/// 
/// This eliminates the need to create new runtimes on every
/// Python method call, preventing GIL blocking and improving
/// performance.

use tokio::runtime::{Handle, Runtime as TokioRuntime};
use std::sync::OnceLock;
use tracing::info;

/// Global shared Tokio runtime handle
static RUNTIME_HANDLE: OnceLock<Handle> = OnceLock::new();

/// Initialize the global Tokio runtime
/// 
/// This should be called once at application startup (e.g., in Python module __init__).
/// If called multiple times, subsequent calls are no-ops.
/// 
/// # Returns
/// The runtime handle
/// 
/// # Panics
/// Panics if Tokio runtime creation fails
pub fn init_runtime() -> Handle {
    RUNTIME_HANDLE.get_or_init(|| {
        info!("Initializing global Tokio runtime");
        let rt = TokioRuntime::new()
            .expect("Failed to create Tokio runtime");
        rt.handle().clone()
    }).clone()
}

/// Get the global runtime handle
/// 
/// # Returns
/// The runtime handle if initialized
/// 
/// # Panics
/// Panics if runtime not initialized. Call `init_runtime()` first.
pub fn get_runtime_handle() -> Handle {
    RUNTIME_HANDLE
        .get()
        .expect("Runtime not initialized. Call init_runtime() first.")
        .clone()
}

/// Try to get runtime handle without panicking
/// 
/// # Returns
/// `Some(Handle)` if runtime is initialized, `None` otherwise
pub fn try_get_runtime_handle() -> Option<Handle> {
    RUNTIME_HANDLE.get().cloned()
}

/// Check if runtime is initialized
pub fn is_runtime_initialized() -> bool {
    RUNTIME_HANDLE.get().is_some()
}

/// Spawn a task on the shared runtime
/// 
/// This is a convenience function that spawns a task on the global
/// runtime handle, or initializes it if not already done.
/// 
/// # Arguments
/// * `task` - The async task to spawn
/// 
/// # Returns
/// JoinHandle for the spawned task
pub fn spawn<F>(task: F) -> tokio::task::JoinHandle<F::Output>
where
    F: std::future::Future + Send + 'static,
    F::Output: Send + 'static,
{
    let handle = get_runtime_handle();
    handle.spawn(task)
}

/// Spawn a blocking task on the shared runtime
/// 
/// Use this for CPU-intensive operations that should run
/// on a blocking thread pool.
/// 
/// # Arguments
/// * `task` - The blocking task to spawn
/// 
/// # Returns
/// JoinHandle for the spawned task
pub fn spawn_blocking<F, R>(task: F) -> tokio::task::JoinHandle<R>
where
    F: FnOnce() -> R + Send + 'static,
    R: Send + 'static,
{
    let handle = get_runtime_handle();
    handle.spawn_blocking(task)
}

#[cfg(test)]
mod tests {
    use super::*;
    
    #[tokio::test]
    async fn test_runtime_initialization() {
        // Note: In tests, Tokio runtime is already provided by #[tokio::test]
        // This test verifies the functions work with an existing runtime
        
        // The init_runtime will use the existing runtime context
        let handle = init_runtime();
        assert!(is_runtime_initialized());
        
        // Spawn a simple task
        let result = spawn(async { 42 }).await.unwrap();
        assert_eq!(result, 42);
    }
}

