//! Event dispatcher service.

use crate::constants::MAX_SUBSCRIPTIONS;
use crate::domain::{
    Event, Handler, HandlerId, ReactiveError, Result, Subscription, SubscriptionId,
};
use std::collections::HashMap;
use std::sync::{Arc, RwLock};

/// Type alias for a boxed handler.
pub type BoxedHandler = Arc<dyn Handler>;

/// Event dispatcher that routes events to handlers.
pub struct Dispatcher {
    /// Active subscriptions.
    subscriptions: RwLock<HashMap<SubscriptionId, Subscription>>,
    /// Registered handlers.
    handlers: RwLock<HashMap<HandlerId, BoxedHandler>>,
}

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

    /// Register a handler.
    ///
    /// # Errors
    ///
    /// Returns an error if the handler registry lock cannot be acquired.
    pub fn register_handler<H>(&self, handler: H) -> Result<()>
    where
        H: Handler + 'static,
    {
        let handler = 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, handler);
        Ok(())
    }

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

    /// Add a subscription.
    ///
    /// # Errors
    ///
    /// Returns an error if the subscription limit is reached or the lock cannot be acquired.
    pub fn subscribe(&self, subscription: Subscription) -> Result<()> {
        let mut subscriptions = self.subscriptions.write().map_err(|e| {
            ReactiveError::subscription(format!("Failed to acquire write lock: {e}"))
        })?;

        if subscriptions.len() >= MAX_SUBSCRIPTIONS {
            return Err(ReactiveError::MaxSubscriptions(MAX_SUBSCRIPTIONS));
        }

        subscriptions.insert(subscription.id.clone(), subscription);
        drop(subscriptions);
        Ok(())
    }

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

    /// Dispatch an event to all matching handlers.
    ///
    /// # Errors
    ///
    /// Returns an error if the handler registry lock cannot be acquired
    /// or any handler fails while processing the event.
    pub fn dispatch(&self, event: &Event) -> Result<()> {
        let handlers: Vec<_> = self
            .handlers
            .read()
            .map_err(|e| ReactiveError::dispatch(format!("Failed to acquire read lock: {e}")))?
            .values()
            .cloned()
            .collect();

        for handler in handlers {
            handler.handle(event)?;
        }

        Ok(())
    }

    /// Get the number of active subscriptions.
    #[must_use]
    pub fn subscription_count(&self) -> usize {
        self.subscriptions
            .read()
            .map(|s| s.values().filter(|sub| sub.active).count())
            .unwrap_or(0)
    }

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

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

#[cfg(test)]
mod tests {
    use std::sync::Arc;
    use std::sync::atomic::{AtomicUsize, Ordering};

    use super::*;
    use crate::domain::{EventKind, EventMetadata};

    struct TestHandler {
        id: HandlerId,
        call_count: Arc<AtomicUsize>,
    }

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

        fn handle(&self, _event: &Event) -> Result<()> {
            self.call_count.fetch_add(1, Ordering::SeqCst);
            Ok(())
        }
    }

    struct SimpleHandler {
        id: HandlerId,
    }

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

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

    #[test]
    fn test_dispatcher_new() {
        let dispatcher = Dispatcher::new();
        assert_eq!(dispatcher.subscription_count(), 0);
        assert_eq!(dispatcher.handler_count(), 0);
    }

    #[test]
    fn test_dispatcher_default() {
        let dispatcher = Dispatcher::default();
        assert_eq!(dispatcher.subscription_count(), 0);
    }

    #[test]
    fn test_subscribe_unsubscribe() {
        let dispatcher = Dispatcher::new();
        let subscription = Subscription::file("/test/path");

        let id = subscription.id.clone();
        dispatcher.subscribe(subscription).unwrap();
        assert_eq!(dispatcher.subscription_count(), 1);

        dispatcher.unsubscribe(&id).unwrap();
        assert_eq!(dispatcher.subscription_count(), 0);
    }

    #[test]
    fn test_dispatch_event() {
        let dispatcher = Dispatcher::new();
        let call_count = Arc::new(AtomicUsize::new(0));

        let handler = TestHandler {
            id: HandlerId::new("test-handler"),
            call_count: call_count.clone(),
        };

        dispatcher.register_handler(handler).unwrap();
        assert_eq!(dispatcher.handler_count(), 1);

        let event = Event::new(EventKind::Change, "/test/path", EventMetadata::new("test"));
        dispatcher.dispatch(&event).unwrap();

        assert_eq!(call_count.load(Ordering::SeqCst), 1);
    }

    #[test]
    fn test_unregister_handler() {
        let dispatcher = Dispatcher::new();

        let handler = SimpleHandler {
            id: HandlerId::new("simple-handler"),
        };
        let id = handler.id.clone();

        dispatcher.register_handler(handler).unwrap();
        assert_eq!(dispatcher.handler_count(), 1);

        dispatcher.unregister_handler(&id).unwrap();
        assert_eq!(dispatcher.handler_count(), 0);
    }
}
