#[cfg(feature = "axum")]
pub mod axum;
pub mod error;
pub mod scope;
#[cfg(feature = "axum")]
pub use axum::*;

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

use arc_swap::ArcSwapOption;

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

#[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>(
		&mut self,
		service: T,
	) -> Result<(), ServiceAlreadyExistsError> {
		self.add_internal::<T>(Service::Singleton(Arc::new(service)), None)
	}
	pub fn add_singleton_keyed<T: Send + Sync + 'static>(
		&mut self,
		service: T,
		key: TypeId,
	) -> Result<(), ServiceAlreadyExistsError> {
		self.add_internal::<T>(Service::Singleton(Arc::new(service)), Some(key))
	}

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

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

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

	pub fn create_scope(&self) -> Arc<Scope> {
		let mut scope: Scope = HashMap::new();
		self.collection
			.iter()
			.filter(|(_, v)| match v {
				Service::Scoped(_) => true,
				_ => false,
			})
			.for_each(|(k, _)| {
				scope.insert(k.clone(), OnceLock::new());
			});
		Arc::new(scope)
	}

	fn di_internal<T: 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.get(&tid) {
			match s {
				Service::Singleton(service) if let Ok(res) = service.clone().downcast() => Ok(res),
				Service::Transient(factory) if let Ok(res) = factory.clone()().downcast() => {
					Ok(res)
				},
				Service::Scoped(factory) => {
					let scope = scope
						.unwrap_or_else(|| get_default_scope_full())
						.provide_scope()?;
					let Some(res) = scope.get(&tid) else {
						return Err(ServiceNotFoundError);
					};
					res.get_or_init(|| factory.clone()())
						.clone()
						.downcast()
						.map_err(|_| ServiceNotFoundError)
				},
				_ => Err(ServiceNotFoundError),
			}
		} else {
			Err(ServiceNotFoundError)
		}
	}

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

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

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

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

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

pub fn spp() -> Arc<ServiceCollection> {
	sp().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))
}
