Documentation
use std::{any::TypeId, sync::Arc};

use kabel::{DI, DIK, ServiceCollection};

pub trait Greeter: Send + Sync {
	fn greet(&self) -> String;
}

pub struct EnglishGreeter {
	pub name: String,
}

impl Greeter for EnglishGreeter {
	fn greet(&self) -> String { format!("Hello, {}!", self.name) }
}

pub struct SpanishGreeter;

impl Greeter for SpanishGreeter {
	fn greet(&self) -> String { "¡Hola!".to_string() }
}

pub struct KeyA;
pub struct KeyB;

#[test]
fn test_singleton_trait() {
	let sc = ServiceCollection::new();
	let greeter: Arc<dyn Greeter> = Arc::new(EnglishGreeter {
		name: "World".to_string(),
	});

	sc.add_singleton_trait::<dyn Greeter>(greeter).unwrap();

	let resolved = sc.dir::<dyn Greeter>().unwrap();
	assert_eq!(resolved.greet(), "Hello, World!");
}

#[test]
fn test_singleton_trait_keyed() {
	let sc = ServiceCollection::new();
	let eng: Arc<dyn Greeter> = Arc::new(EnglishGreeter {
		name: "Alice".to_string(),
	});
	let esp: Arc<dyn Greeter> = Arc::new(SpanishGreeter);

	sc.add_singleton_trait_keyed::<dyn Greeter>(eng, TypeId::of::<KeyA>())
		.unwrap();
	sc.add_singleton_trait_keyed::<dyn Greeter>(esp, TypeId::of::<KeyB>())
		.unwrap();

	let res_a = sc.dikr::<dyn Greeter, KeyA>().unwrap();
	assert_eq!(res_a.greet(), "Hello, Alice!");

	let res_b = sc.dikr::<dyn Greeter, KeyB>().unwrap();
	assert_eq!(res_b.greet(), "¡Hola!");
}

#[test]
fn test_transient_trait() {
	let sc = ServiceCollection::new();
	let counter = std::sync::atomic::AtomicUsize::new(0);
	let counter_arc = Arc::new(counter);

	let counter_clone = counter_arc.clone();
	sc.add_transient_trait::<dyn Greeter, _>(move || {
		let count = counter_clone.fetch_add(1, std::sync::atomic::Ordering::SeqCst);
		Arc::new(EnglishGreeter {
			name: format!("User #{}", count),
		}) as Arc<dyn Greeter>
	})
	.unwrap();

	let g1 = sc.dir::<dyn Greeter>().unwrap();
	let g2 = sc.dir::<dyn Greeter>().unwrap();

	assert_eq!(g1.greet(), "Hello, User #0!");
	assert_eq!(g2.greet(), "Hello, User #1!");
}

#[test]
fn test_scoped_trait() {
	let sc = ServiceCollection::new();
	let counter = Arc::new(std::sync::atomic::AtomicUsize::new(0));

	let counter_clone = counter.clone();
	sc.add_scoped_trait::<dyn Greeter, _>(move || {
		let count = counter_clone.fetch_add(1, std::sync::atomic::Ordering::SeqCst);
		Arc::new(EnglishGreeter {
			name: format!("ScopeInstance #{}", count),
		}) as Arc<dyn Greeter>
	})
	.unwrap();

	let scope1 = sc.create_scope();
	let scope2 = sc.create_scope();

	let scope_provider1 =
		Arc::new(Arc::new(ScopeDummy(scope1)) as Arc<dyn kabel::scope::ScopeProvider>);
	let scope_provider2 =
		Arc::new(Arc::new(ScopeDummy(scope2)) as Arc<dyn kabel::scope::ScopeProvider>);

	let g1_a = sc.disr::<dyn Greeter>(scope_provider1.clone()).unwrap();
	let g1_b = sc.disr::<dyn Greeter>(scope_provider1.clone()).unwrap();
	assert_eq!(g1_a.greet(), "Hello, ScopeInstance #0!");
	assert_eq!(g1_b.greet(), "Hello, ScopeInstance #0!");

	let g2_a = sc.disr::<dyn Greeter>(scope_provider2.clone()).unwrap();
	assert_eq!(g2_a.greet(), "Hello, ScopeInstance #1!");
}

struct ScopeDummy(Arc<kabel::scope::Scope>);

impl kabel::scope::ScopeProvider for ScopeDummy {
	fn provide_scope(
		&self,
	) -> Result<Arc<kabel::scope::Scope>, kabel::error::ServiceNotFoundError> {
		Ok(self.0.clone())
	}
}

#[test]
fn test_di_wrapper_struct() {
	let sc = ServiceCollection::new();
	let greeter: Arc<dyn Greeter> = Arc::new(EnglishGreeter {
		name: "Bob".to_string(),
	});
	sc.add_singleton_trait::<dyn Greeter>(greeter).unwrap();

	let resolved = sc.dir::<dyn Greeter>().unwrap();
	let di_wrapper: DI<dyn Greeter> = DI(resolved);
	assert_eq!(di_wrapper.0.greet(), "Hello, Bob!");

	let greeter_k: Arc<dyn Greeter> = Arc::new(SpanishGreeter);
	sc.add_singleton_trait_keyed::<dyn Greeter>(greeter_k, TypeId::of::<KeyA>())
		.unwrap();

	let resolved_k = sc.dikr::<dyn Greeter, KeyA>().unwrap();
	let dik_wrapper: DIK<dyn Greeter, KeyA> = DIK(resolved_k, std::marker::PhantomData);
	assert_eq!(dik_wrapper.0.greet(), "¡Hola!");
}