Documentation
#[cfg(feature = "axum")]
pub mod axum;
pub mod error;
#[cfg(feature = "jav")]
pub mod jav;
pub mod scope;

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

use arc_swap::ArcSwapOption;
use scc::HashMap;

use crate::{
	error::{ServiceAlreadyExistsError, ServiceNotFoundError},
	scope::{Scope, ScopeProvider, get_default_scope_full},
};

pub struct DI<T: ?Sized + Send + Sync + 'static>(pub Arc<T>);
pub struct DIK<T: ?Sized + Send + Sync + 'static, K: 'static>(
	pub Arc<T>,
	pub std::marker::PhantomData<K>,
);

#[derive(Clone)]
pub enum Service {
	Singleton(Arc<dyn Any + Send + Sync>),
	Transient(Arc<dyn Fn() -> Arc<dyn Any + Send + Sync> + Send + Sync>),
	Scoped(Arc<dyn Fn() -> Arc<dyn Any + Send + Sync> + Send + Sync>),
}

#[derive(Clone)]
pub struct ServiceCollection {
	collection: HashMap<(TypeId, Option<TypeId>), Service>,
}

impl ServiceCollection {
	pub fn new() -> Self {
		Self {
			collection: HashMap::new(),
		}
	}

	pub fn add_singleton<T: Send + Sync + 'static>(
		&self,
		service: T,
	) -> Result<(), ServiceAlreadyExistsError> {
		self.add_internal::<T>(Service::Singleton(Arc::new(Arc::new(service))), None)
	}

	pub fn add_singleton_keyed<T: Send + Sync + 'static>(
		&self,
		service: T,
		key: TypeId,
	) -> Result<(), ServiceAlreadyExistsError> {
		self.add_internal::<T>(Service::Singleton(Arc::new(Arc::new(service))), Some(key))
	}

	pub fn add_singleton_trait<Trait: ?Sized + Send + Sync + 'static>(
		&self,
		service: Arc<Trait>,
	) -> Result<(), ServiceAlreadyExistsError> {
		self.add_internal::<Trait>(Service::Singleton(Arc::new(service)), None)
	}

	pub fn add_singleton_trait_keyed<Trait: ?Sized + Send + Sync + 'static>(
		&self,
		service: Arc<Trait>,
		key: TypeId,
	) -> Result<(), ServiceAlreadyExistsError> {
		self.add_internal::<Trait>(Service::Singleton(Arc::new(service)), Some(key))
	}

	fn add_internal<T: ?Sized + Send + Sync + 'static>(
		&self,
		service: Service,
		key: Option<TypeId>,
	) -> Result<(), ServiceAlreadyExistsError> {
		let tid = (TypeId::of::<T>(), key);
		if self.collection.contains_sync(&tid) {
			return Err(ServiceAlreadyExistsError);
		}
		let _ = self.collection.insert_sync(tid, service);
		Ok(())
	}

	pub fn add_transient<T: Send + Sync + 'static, F>(
		&self,
		service_factory: F,
	) -> Result<(), ServiceAlreadyExistsError>
	where
		F: Fn() -> T + Send + Sync + 'static,
	{
		self.add_internal::<T>(
			Service::Transient(Arc::new(move || Arc::new(Arc::new(service_factory())))),
			None,
		)
	}
	pub fn add_transient_keyed<T: Send + Sync + 'static, F>(
		&self,
		service_factory: F,
		key: TypeId,
	) -> Result<(), ServiceAlreadyExistsError>
	where
		F: Fn() -> T + Send + Sync + 'static,
	{
		self.add_internal::<T>(
			Service::Transient(Arc::new(move || Arc::new(Arc::new(service_factory())))),
			Some(key),
		)
	}

	pub fn add_transient_trait<Trait: ?Sized + Send + Sync + 'static, F>(
		&self,
		service_factory: F,
	) -> Result<(), ServiceAlreadyExistsError>
	where
		F: Fn() -> Arc<Trait> + Send + Sync + 'static,
	{
		self.add_internal::<Trait>(
			Service::Transient(Arc::new(move || Arc::new(service_factory()))),
			None,
		)
	}

	pub fn add_transient_trait_keyed<Trait: ?Sized + Send + Sync + 'static, F>(
		&self,
		service_factory: F,
		key: TypeId,
	) -> Result<(), ServiceAlreadyExistsError>
	where
		F: Fn() -> Arc<Trait> + Send + Sync + 'static,
	{
		self.add_internal::<Trait>(
			Service::Transient(Arc::new(move || Arc::new(service_factory()))),
			Some(key),
		)
	}

	pub fn add_scoped<T: Send + Sync + 'static, F>(
		&self,
		service_factory: F,
	) -> Result<(), ServiceAlreadyExistsError>
	where
		F: Fn() -> T + Send + Sync + 'static,
	{
		self.add_internal::<T>(
			Service::Scoped(Arc::new(move || Arc::new(Arc::new(service_factory())))),
			None,
		)
	}
	pub fn add_scoped_keyed<T: Send + Sync + 'static, F>(
		&self,
		service_factory: F,
		key: TypeId,
	) -> Result<(), ServiceAlreadyExistsError>
	where
		F: Fn() -> T + Send + Sync + 'static,
	{
		self.add_internal::<T>(
			Service::Scoped(Arc::new(move || Arc::new(Arc::new(service_factory())))),
			Some(key),
		)
	}

	pub fn add_scoped_trait<Trait: ?Sized + Send + Sync + 'static, F>(
		&self,
		service_factory: F,
	) -> Result<(), ServiceAlreadyExistsError>
	where
		F: Fn() -> Arc<Trait> + Send + Sync + 'static,
	{
		self.add_internal::<Trait>(
			Service::Scoped(Arc::new(move || Arc::new(service_factory()))),
			None,
		)
	}

	pub fn add_scoped_trait_keyed<Trait: ?Sized + Send + Sync + 'static, F>(
		&self,
		service_factory: F,
		key: TypeId,
	) -> Result<(), ServiceAlreadyExistsError>
	where
		F: Fn() -> Arc<Trait> + Send + Sync + 'static,
	{
		self.add_internal::<Trait>(
			Service::Scoped(Arc::new(move || Arc::new(service_factory()))),
			Some(key),
		)
	}

	pub fn create_scope(&self) -> Arc<Scope> {
		let scope: Scope = HashMap::new();
		self.collection.iter_sync(|k, v| {
			let Service::Scoped(_) = v else {
				return true;
			};
			let _ = scope.insert_sync(k.clone(), OnceLock::new());
			true
		});
		Arc::new(scope)
	}

	fn di_internal<T: ?Sized + Send + Sync + 'static>(
		&self,
		key: Option<TypeId>,
		scope: Option<Arc<Arc<dyn ScopeProvider>>>,
	) -> Result<Arc<T>, ServiceNotFoundError> {
		let tid = (TypeId::of::<T>(), key);
		if let Some(s) = self.collection.read_sync(&tid, |_, s| match s {
			Service::Singleton(service) if let Ok(res) = service.clone().downcast::<Arc<T>>() => {
				Ok((*res).clone())
			},
			Service::Transient(factory) => {
				let instance = factory.clone()();
				if let Ok(res) = instance.downcast::<Arc<T>>() {
					Ok((*res).clone())
				} else {
					Err(ServiceNotFoundError)
				}
			},
			Service::Scoped(factory) => {
				let scope = scope
					.unwrap_or_else(|| get_default_scope_full())
					.provide_scope()?;
				let Some(res) = scope.get_sync(&tid) else {
					return Err(ServiceNotFoundError);
				};
				let instance = res.get_or_init(|| factory.clone()()).clone();
				if let Ok(res) = instance.downcast::<Arc<T>>() {
					Ok((*res).clone())
				} else {
					Err(ServiceNotFoundError)
				}
			},
			_ => Err(ServiceNotFoundError),
		}) {
			return s;
		} else {
			Err(ServiceNotFoundError)
		}
	}

	pub fn dir<T: ?Sized + Send + Sync + 'static>(&self) -> Result<Arc<T>, ServiceNotFoundError> {
		self.di_internal(None, None)
	}

	pub fn dikr<T: ?Sized + Send + Sync + 'static, K: 'static>(
		&self,
	) -> Result<Arc<T>, ServiceNotFoundError> {
		self.di_internal(Some(TypeId::of::<K>()), None)
	}

	pub fn disr<T: ?Sized + Send + Sync + 'static>(
		&self,
		scope: Arc<Arc<dyn ScopeProvider>>,
	) -> Result<Arc<T>, ServiceNotFoundError> {
		self.di_internal(None, Some(scope))
	}
	pub fn diskr<T: ?Sized + Send + Sync + 'static, K: 'static>(
		&self,
		scope: Arc<Arc<dyn ScopeProvider>>,
	) -> Result<Arc<T>, ServiceNotFoundError> {
		self.di_internal(Some(TypeId::of::<K>()), Some(scope))
	}

	pub fn di<T: ?Sized + Send + Sync + 'static>(&self) -> Arc<T> {
		self.di_internal(None, None)
			.expect("Failed to find service")
	}

	pub fn dik<T: ?Sized + Send + Sync + 'static, K: 'static>(&self) -> Arc<T> {
		self.di_internal(Some(TypeId::of::<K>()), None)
			.expect("Failed to find service")
	}

	pub fn dis<T: ?Sized + Send + Sync + 'static>(
		&self,
		scope: Arc<Arc<dyn ScopeProvider>>,
	) -> Arc<T> {
		self.di_internal(None, Some(scope))
			.expect("Failed to find service")
	}
	pub fn disk<T: ?Sized + Send + Sync + 'static, K: 'static>(
		&self,
		scope: Arc<Arc<dyn ScopeProvider>>,
	) -> Arc<T> {
		self.di_internal(Some(TypeId::of::<K>()), Some(scope))
			.expect("Failed to find service")
	}
}

static SERVICE_PROVIDER: ArcSwapOption<ServiceCollection> = ArcSwapOption::const_empty();

pub fn spo() -> Option<Arc<ServiceCollection>> {
	SERVICE_PROVIDER.load_full()
}

pub fn sp() -> Arc<ServiceCollection> {
	spo().expect("ServiceProvider not yet initialized")
}

pub fn set_sp(sp: ServiceCollection) -> Option<Arc<ServiceCollection>> {
	set_sp_arc(Arc::new(sp))
}
pub fn set_sp_arc(sp: Arc<ServiceCollection>) -> Option<Arc<ServiceCollection>> {
	SERVICE_PROVIDER.swap(Some(sp))
}

pub fn init_sp() {
	set_sp(ServiceCollection::new());
}