use std::{
	any::{Any, TypeId},
	collections::HashMap,
	sync::Arc,
};

use funcext::FuncExt;
use futures_util::future::join_all;
use tokio::sync::RwLock;

#[async_trait::async_trait]
pub trait Handler: Send + Sync {
	type E;
	async fn handle(&self, e: Arc<Self::E>);
}

#[async_trait::async_trait]
trait ErasedHandler: Send + Sync {
	async fn handle_erased(&self, e: Arc<dyn Any + Send + Sync>);
}

struct HandlerWrapper<E>(Arc<dyn Handler<E = E> + Send + Sync>);

#[async_trait::async_trait]
impl<E: Any + Send + Sync + 'static> ErasedHandler for HandlerWrapper<E> {
	async fn handle_erased(&self, e: Arc<dyn Any + Send + Sync>) {
		if let Ok(event) = e.downcast::<E>() {
			self.0.handle(event).await;
		}
	}
}

#[derive(Default)]
pub struct EventDispatcher(RwLock<HashMap<TypeId, Vec<Arc<dyn ErasedHandler>>>>);

impl EventDispatcher {
	pub fn new() -> Self {
		Self(RwLock::new(HashMap::new()))
	}

	pub async fn register<E: Send + Sync + 'static>(
		&self,
		handler: Arc<dyn Handler<E = E> + Send + Sync>,
	) {
		let mut collection = self.0.write().await;
		collection
			.entry(TypeId::of::<E>())
			.or_default()
			.push(Arc::new(HandlerWrapper(handler)));
	}

	pub async fn emit<E: Send + Sync + 'static>(&self, event: E) {
		self.emit_arc(Arc::new(event)).await;
	}
	pub async fn emit_arc<E: Send + Sync + 'static>(&self, event: Arc<E>) {
		let collection = self.0.read().await;
		let Some(handlers) = collection.get(&TypeId::of::<E>()) else {
			return;
		};
		let event: Arc<dyn Any + Send + Sync> = event;
		handlers
			.iter()
			.map(|h| h.handle_erased(event.clone()))
			.R(join_all)
			.await;
	}
}
