//! Configuration manager service.

use crate::application::Dispatcher;
use crate::config::ConfigOptions;
use crate::domain::{
    Event, EventKind, EventMetadata, Handler, HandlerId, Result, Subscription, SubscriptionBuilder,
};
use std::path::PathBuf;
use std::sync::Arc;

/// Configuration manager for reactive configuration.
pub struct ConfigManager {
    /// Event dispatcher.
    dispatcher: Arc<Dispatcher>,
    /// Configuration options.
    options: ConfigOptions,
}

impl ConfigManager {
    /// Create a new configuration manager.
    #[must_use]
    pub fn new() -> Self {
        Self {
            dispatcher: Arc::new(Dispatcher::new()),
            options: ConfigOptions::default(),
        }
    }

    /// Create a new configuration manager with options.
    #[must_use]
    pub fn with_options(options: ConfigOptions) -> Self {
        Self {
            dispatcher: Arc::new(Dispatcher::new()),
            options,
        }
    }

    /// Register a handler.
    ///
    /// # Errors
    ///
    /// Returns an error if the dispatcher cannot register the handler.
    pub fn register_handler<H>(&self, handler: H) -> Result<()>
    where
        H: Handler + 'static,
    {
        self.dispatcher.register_handler(handler)
    }

    /// Unregister a handler by ID.
    ///
    /// # Errors
    ///
    /// Returns an error if the dispatcher cannot unregister the handler.
    pub fn unregister_handler(&self, id: &HandlerId) -> Result<()> {
        self.dispatcher.unregister_handler(id)
    }

    /// Create a subscription builder.
    #[must_use]
    pub const fn subscription_builder(&self) -> SubscriptionBuilder {
        SubscriptionBuilder::new()
    }

    /// Add a subscription.
    ///
    /// # Errors
    ///
    /// Returns an error if the dispatcher rejects the subscription.
    pub fn subscribe(&self, subscription: Subscription) -> Result<()> {
        self.dispatcher.subscribe(subscription)
    }

    /// Subscribe to a file path.
    ///
    /// # Errors
    ///
    /// Returns an error if the dispatcher rejects the derived subscription.
    pub fn subscribe_file(&self, path: impl Into<PathBuf>) -> Result<()> {
        let subscription = Subscription::file(path);
        self.dispatcher.subscribe(subscription)
    }

    /// Subscribe to a directory.
    ///
    /// # Errors
    ///
    /// Returns an error if the dispatcher rejects the derived subscription.
    pub fn subscribe_directory(&self, path: impl Into<PathBuf>) -> Result<()> {
        let subscription = Subscription::directory(path);
        self.dispatcher.subscribe(subscription)
    }

    /// Dispatch an event.
    ///
    /// # Errors
    ///
    /// Returns an error if dispatching fails.
    pub fn dispatch(&self, event: &Event) -> Result<()> {
        self.dispatcher.dispatch(event)
    }

    /// Notify of a configuration change.
    ///
    /// # Errors
    ///
    /// Returns an error if dispatching the change event fails.
    pub fn notify_change(&self, path: impl Into<String>) -> Result<()> {
        let event = Event::change(path);
        self.dispatch(&event)
    }

    /// Notify of a configuration update.
    ///
    /// # Errors
    ///
    /// Returns an error if dispatching the update event fails.
    pub fn notify_update(&self, path: impl Into<String>) -> Result<()> {
        let event = Event::new(
            EventKind::Update,
            path,
            EventMetadata::new("config-manager"),
        );
        self.dispatch(&event)
    }

    /// Notify of a configuration reload.
    ///
    /// # Errors
    ///
    /// Returns an error if dispatching the reload event fails.
    pub fn notify_reload(&self, path: impl Into<String>) -> Result<()> {
        let event = Event::new(
            EventKind::Reload,
            path,
            EventMetadata::new("config-manager"),
        );
        self.dispatch(&event)
    }

    /// Get the dispatcher.
    #[must_use]
    pub const fn dispatcher(&self) -> &Arc<Dispatcher> {
        &self.dispatcher
    }

    /// Get the configuration options.
    #[must_use]
    pub const fn options(&self) -> &ConfigOptions {
        &self.options
    }

    /// Get the number of active subscriptions.
    #[must_use]
    pub fn subscription_count(&self) -> usize {
        self.dispatcher.subscription_count()
    }

    /// Get the number of registered handlers.
    #[must_use]
    pub fn handler_count(&self) -> usize {
        self.dispatcher.handler_count()
    }
}

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

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

    use super::*;

    struct CountingHandler {
        id: HandlerId,
        seen_events: Arc<Mutex<Vec<(EventKind, String)>>>,
        calls: Arc<AtomicUsize>,
    }

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

        fn handle(&self, event: &Event) -> Result<()> {
            self.calls.fetch_add(1, Ordering::SeqCst);
            self.seen_events
                .lock()
                .unwrap()
                .push((event.kind, event.path.clone()));
            Ok(())
        }
    }

    #[test]
    fn test_config_manager_new_and_default() {
        let manager = ConfigManager::new();
        let default_manager = ConfigManager::default();

        assert_eq!(manager.subscription_count(), 0);
        assert_eq!(manager.handler_count(), 0);
        assert_eq!(default_manager.subscription_count(), 0);
        assert_eq!(default_manager.handler_count(), 0);
    }

    #[test]
    fn test_config_manager_with_options_and_subscription_builder() {
        let manager =
            ConfigManager::with_options(ConfigOptions::new().debounce_ms(250).buffer_size(16));
        let subscription = manager
            .subscription_builder()
            .file("/tmp/config.toml")
            .priority(42)
            .build();

        assert_eq!(manager.options().debounce_ms, 250);
        assert_eq!(manager.options().buffer_size, 16);
        assert_eq!(subscription.priority, 42);
    }

    #[test]
    fn test_config_manager_register_subscribe_dispatch_and_unregister() {
        let manager = ConfigManager::new();
        let seen_events = Arc::new(Mutex::new(Vec::new()));
        let calls = Arc::new(AtomicUsize::new(0));
        let handler_id = HandlerId::new("counting-handler");

        manager
            .register_handler(CountingHandler {
                id: handler_id.clone(),
                seen_events: seen_events.clone(),
                calls: calls.clone(),
            })
            .unwrap();
        manager.subscribe_file("/tmp/app.toml").unwrap();
        manager.subscribe_directory("/tmp").unwrap();

        assert_eq!(manager.handler_count(), 1);
        assert_eq!(manager.subscription_count(), 2);

        manager.notify_change("/tmp/app.toml").unwrap();
        manager.notify_update("/tmp/app.toml").unwrap();
        manager.notify_reload("/tmp/app.toml").unwrap();

        assert_eq!(calls.load(Ordering::SeqCst), 3);
        assert_eq!(
            seen_events.lock().unwrap().as_slice(),
            &[
                (EventKind::Change, "/tmp/app.toml".to_string()),
                (EventKind::Update, "/tmp/app.toml".to_string()),
                (EventKind::Reload, "/tmp/app.toml".to_string()),
            ]
        );

        manager.unregister_handler(&handler_id).unwrap();
        assert_eq!(manager.handler_count(), 0);
        assert!(Arc::ptr_eq(manager.dispatcher(), manager.dispatcher()));
    }
}
