use std::future::Future;
use std::pin::Pin;
use std::sync::Arc;

use dashmap::DashMap;
use once_cell::sync::Lazy;

use crate::{error::JobResult, job::Job, queue::JobQueue};

/// Type-erased job handler function (wrapped in Arc for cloneability)
pub type JobHandler = Arc<
    dyn Fn(Job, Arc<JobQueue>) -> Pin<Box<dyn Future<Output = JobResult<()>> + Send>>
        + Send
        + Sync,
>;

/// Global registry for job handlers
pub struct JobRegistry {
    handlers: DashMap<String, JobHandler>,
}

impl JobRegistry {
    /// Create a new empty registry
    pub fn new() -> Self {
        Self {
            handlers: DashMap::new(),
        }
    }

    /// Register a job handler (returns false if already registered)
    pub fn register(&self, job_type: String, handler: JobHandler) -> bool {
        if self.handlers.contains_key(&job_type) {
            // Already registered, skip
            return false;
        }
        self.handlers.insert(job_type, handler);
        true
    }

    /// Register or replace a job handler (always succeeds)
    pub fn register_or_replace(&self, job_type: String, handler: JobHandler) -> Option<JobHandler> {
        self.handlers.insert(job_type, handler)
    }

    /// Unregister a job handler (returns the removed handler if it existed)
    pub fn unregister(&self, job_type: &str) -> Option<JobHandler> {
        self.handlers.remove(job_type).map(|(_, handler)| handler)
    }

    /// Get a handler for a job type
    pub fn get_handler(&self, job_type: &str) -> Option<JobHandler> {
        self.handlers.get(job_type).map(|h| h.value().clone())
    }

    /// Check if a handler is registered
    pub fn has_handler(&self, job_type: &str) -> bool {
        self.handlers.contains_key(job_type)
    }

    /// Get all registered job types
    pub fn registered_types(&self) -> Vec<String> {
        self.handlers.iter().map(|entry| entry.key().clone()).collect()
    }
}

/// Global job registry instance
pub static JOB_REGISTRY: Lazy<JobRegistry> = Lazy::new(|| JobRegistry::new());

/// Register a job handler in the global registry (returns false if already registered)
pub fn register_handler(job_type: String, handler: JobHandler) -> bool {
    JOB_REGISTRY.register(job_type, handler)
}

/// Register or replace a job handler in the global registry
pub fn register_or_replace_handler(job_type: String, handler: JobHandler) -> Option<JobHandler> {
    JOB_REGISTRY.register_or_replace(job_type, handler)
}

/// Unregister a job handler from the global registry
pub fn unregister_handler(job_type: &str) -> Option<JobHandler> {
    JOB_REGISTRY.unregister(job_type)
}

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

    #[test]
    fn test_new_registry_is_empty() {
        // Arrange
        let registry = JobRegistry::new();

        // Act
        let types = registry.registered_types();

        // Assert
        assert!(types.is_empty());
    }

    #[test]
    fn test_register_and_get_handler() {
        // Arrange
        let registry = JobRegistry::new();
        let job_type = "test_job".to_string();
        let handler: JobHandler = Arc::new(|_job, _queue| {
            Box::pin(async { Ok(()) })
        });

        // Act
        registry.register(job_type.clone(), handler.clone());
        let retrieved = registry.get_handler(&job_type);

        // Assert
        assert!(retrieved.is_some());
    }

    #[test]
    fn test_has_handler_returns_true_when_registered() {
        // Arrange
        let registry = JobRegistry::new();
        let job_type = "test_job".to_string();
        let handler: JobHandler = Arc::new(|_job, _queue| {
            Box::pin(async { Ok(()) })
        });

        // Act
        registry.register(job_type.clone(), handler);
        let has = registry.has_handler(&job_type);

        // Assert
        assert!(has);
    }

    #[test]
    fn test_has_handler_returns_false_when_not_registered() {
        // Arrange
        let registry = JobRegistry::new();

        // Act
        let has = registry.has_handler("nonexistent");

        // Assert
        assert!(!has);
    }

    #[test]
    fn test_get_handler_returns_none_when_not_registered() {
        // Arrange
        let registry = JobRegistry::new();

        // Act
        let handler = registry.get_handler("nonexistent");

        // Assert
        assert!(handler.is_none());
    }

    #[test]
    fn test_registered_types_returns_all_types() {
        // Arrange
        let registry = JobRegistry::new();
        let handler: JobHandler = Arc::new(|_job, _queue| {
            Box::pin(async { Ok(()) })
        });

        // Act
        registry.register("job1".to_string(), handler.clone());
        registry.register("job2".to_string(), handler.clone());
        registry.register("job3".to_string(), handler);
        let types = registry.registered_types();

        // Assert
        assert_eq!(types.len(), 3);
        assert!(types.contains(&"job1".to_string()));
        assert!(types.contains(&"job2".to_string()));
        assert!(types.contains(&"job3".to_string()));
    }

    #[test]
    fn test_register_same_type_twice_does_not_duplicate() {
        // Arrange
        let registry = JobRegistry::new();
        let handler1: JobHandler = Arc::new(|_job, _queue| {
            Box::pin(async { Ok(()) })
        });
        let handler2: JobHandler = Arc::new(|_job, _queue| {
            Box::pin(async { Ok(()) })
        });

        // Act
        let first = registry.register("test_job".to_string(), handler1);
        let second = registry.register("test_job".to_string(), handler2);
        let types = registry.registered_types();

        // Assert
        assert!(first);
        assert!(!second);
        assert_eq!(types.len(), 1);
        assert!(registry.has_handler("test_job"));
    }

    #[test]
    fn test_unregister_removes_handler() {
        // Arrange
        let registry = JobRegistry::new();
        let job_type = "test_job".to_string();
        let handler: JobHandler = Arc::new(|_job, _queue| {
            Box::pin(async { Ok(()) })
        });

        // Act
        registry.register(job_type.clone(), handler);
        let removed = registry.unregister(&job_type);

        // Assert
        assert!(removed.is_some());
        assert!(!registry.has_handler(&job_type));
        assert!(registry.get_handler(&job_type).is_none());
    }

    #[test]
    fn test_unregister_nonexistent_returns_none() {
        // Arrange
        let registry = JobRegistry::new();

        // Act
        let removed = registry.unregister("nonexistent");

        // Assert
        assert!(removed.is_none());
    }

    #[test]
    fn test_register_or_replace_adds_new_handler() {
        // Arrange
        let registry = JobRegistry::new();
        let job_type = "test_job".to_string();
        let handler: JobHandler = Arc::new(|_job, _queue| {
            Box::pin(async { Ok(()) })
        });

        // Act
        let previous = registry.register_or_replace(job_type.clone(), handler);

        // Assert
        assert!(previous.is_none());
        assert!(registry.has_handler(&job_type));
    }

    #[test]
    fn test_register_or_replace_replaces_existing_handler() {
        // Arrange
        let registry = JobRegistry::new();
        let job_type = "test_job".to_string();
        let handler1: JobHandler = Arc::new(|_job, _queue| {
            Box::pin(async { Ok(()) })
        });
        let handler2: JobHandler = Arc::new(|_job, _queue| {
            Box::pin(async { Ok(()) })
        });

        // Act
        registry.register(job_type.clone(), handler1);
        let previous = registry.register_or_replace(job_type.clone(), handler2);

        // Assert
        assert!(previous.is_some());
        assert!(registry.has_handler(&job_type));
        assert_eq!(registry.registered_types().len(), 1);
    }
}
