//! Handler registry for tracking handlers by type.

use crate::application::dispatcher::BoxedHandler;
use crate::domain::{Handler, HandlerId, ReactiveError, Result};
use std::collections::HashMap;
use std::sync::{Arc, RwLock};

/// Registry for managing handlers.
pub struct HandlerRegistry {
    /// Registered handlers by ID.
    handlers: RwLock<HashMap<HandlerId, BoxedHandler>>,
}

impl HandlerRegistry {
    /// Create a new handler registry.
    #[must_use]
    pub fn new() -> Self {
        Self {
            handlers: RwLock::new(HashMap::new()),
        }
    }

    /// Register a handler.
    ///
    /// # Errors
    ///
    /// Returns an error if the registry lock cannot be acquired.
    pub fn register<H>(&self, handler: H) -> Result<HandlerId>
    where
        H: Handler + 'static,
    {
        let handler: BoxedHandler = Arc::new(handler);
        let id = handler.id().clone();
        self.handlers
            .write()
            .map_err(|e| {
                ReactiveError::handler_failed(format!("Failed to acquire write lock: {e}"))
            })?
            .insert(id.clone(), handler);
        Ok(id)
    }

    /// Unregister a handler.
    ///
    /// # Errors
    ///
    /// Returns an error if the registry lock cannot be acquired.
    pub fn unregister(&self, id: &HandlerId) -> Result<()> {
        self.handlers
            .write()
            .map_err(|e| {
                ReactiveError::handler_failed(format!("Failed to acquire write lock: {e}"))
            })?
            .remove(id);
        Ok(())
    }

    /// Get a handler by ID.
    #[must_use]
    pub fn get(&self, id: &HandlerId) -> Option<BoxedHandler> {
        self.handlers.read().ok()?.get(id).cloned()
    }

    /// Check if a handler exists.
    #[must_use]
    pub fn contains(&self, id: &HandlerId) -> bool {
        self.handlers
            .read()
            .map(|guard| guard.contains_key(id))
            .unwrap_or(false)
    }

    /// Get the number of registered handlers.
    #[must_use]
    pub fn len(&self) -> usize {
        self.handlers.read().map(|guard| guard.len()).unwrap_or(0)
    }

    /// Check if the registry is empty.
    #[must_use]
    pub fn is_empty(&self) -> bool {
        self.len() == 0
    }

    /// Clear all handlers.
    ///
    /// # Errors
    ///
    /// Returns an error if the registry lock cannot be acquired.
    pub fn clear(&self) -> Result<()> {
        self.handlers
            .write()
            .map_err(|e| {
                ReactiveError::handler_failed(format!("Failed to acquire write lock: {e}"))
            })?
            .clear();
        Ok(())
    }
}

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

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

    struct TestHandler {
        id: HandlerId,
    }

    impl Handler for TestHandler {
        fn id(&self) -> &HandlerId {
            &self.id
        }

        fn handle(&self, _event: &crate::domain::Event) -> Result<()> {
            Ok(())
        }
    }

    #[test]
    fn test_handler_registry_register_get_and_contains() {
        let registry = HandlerRegistry::new();
        let id = HandlerId::new("registry-test");

        let registered_id = registry.register(TestHandler { id: id.clone() }).unwrap();

        assert_eq!(registered_id, id);
        assert!(registry.contains(&id));
        assert!(registry.get(&id).is_some());
        assert_eq!(registry.len(), 1);
        assert!(!registry.is_empty());
    }

    #[test]
    fn test_handler_registry_unregister_and_clear() {
        let registry = HandlerRegistry::new();
        let first = HandlerId::new("first");
        let second = HandlerId::new("second");

        registry
            .register(TestHandler { id: first.clone() })
            .unwrap();
        registry.register(TestHandler { id: second }).unwrap();
        registry.unregister(&first).unwrap();

        assert!(!registry.contains(&first));
        assert_eq!(registry.len(), 1);

        registry.clear().unwrap();
        assert!(registry.is_empty());
        assert_eq!(registry.len(), 0);
    }

    #[test]
    fn test_handler_registry_default_and_missing_handler() {
        let registry = HandlerRegistry::default();
        let missing = HandlerId::new("missing");

        assert!(registry.is_empty());
        assert_eq!(registry.len(), 0);
        assert!(!registry.contains(&missing));
        assert!(registry.get(&missing).is_none());
        assert!(registry.unregister(&missing).is_ok());
    }
}
