use std::collections::HashMap;
use std::sync::LazyLock;

use tokio::sync::RwLock;

pub(crate) struct MessageDispatcher<H>
where
    H: Fn(String) + Send + Sync,
{
    handlers: RwLock<HashMap<String, H>>,
}

pub(crate) static DISPATCHER: LazyLock<MessageDispatcher<Box<dyn Fn(String) + Send + Sync>>> =
    LazyLock::new(MessageDispatcher::new);

impl<H> MessageDispatcher<H>
where
    H: Fn(String) + Send + Sync,
{
    fn new() -> Self {
        Self {
            handlers: RwLock::new(HashMap::new()),
        }
    }

    pub(crate) async fn register_handler(&self, handler_name: String, handler: H) {
        let mut handlers = self.handlers.write().await;
        handlers.insert(handler_name, handler);
    }

    pub(crate) async fn dispatch(&self, handler_name: String, message: String) {
        let handlers = self.handlers.read().await;

        match handlers.get(&handler_name) {
            Some(handler) => handler(message),
            None => log::error!(
                "The handler corresponds to this kind of message hasn't been registered!"
            ),
        }
    }
}

mod test {
    #[tokio::test]
    async fn test_dispatch() {
        use super::DISPATCHER;

        DISPATCHER
            .register_handler(
                "UserLogin".into(),
                Box::new(|message| println!("Received message: {message}")),
            )
            .await;
        DISPATCHER
            .register_handler(
                "UserSignUp".into(),
                Box::new(|message| println!("Received message: {message}")),
            )
            .await;

        DISPATCHER
            .dispatch("UserLogin".into(), "I want to login".into())
            .await;
        DISPATCHER
            .dispatch("UserSignUp".into(), "I want to signup".into())
            .await;
    }
}
