import type { Constructor, Type } from "../types";
import type { ForwardReference } from "../utils/forward-ref";

export type ProviderScope = "singleton" | "transient";

export type ProviderToken<T = unknown> =
	| Type<T>
	| string
	| symbol
	| ForwardReference<Type<T>>;

export interface BaseProvider<T = unknown> {
	provide: ProviderToken<T>;
	scope?: ProviderScope;
}

export interface ClassProvider<T = unknown> extends BaseProvider<T> {
	useClass: Constructor<T>;
}

export interface ValueProvider<T = unknown> extends BaseProvider<T> {
	useValue: T;
}

export interface FactoryProvider<T = unknown> extends BaseProvider<T> {
	useFactory: (...args: any[]) => T | Promise<T>;
	inject?: ProviderToken[];
}

export interface ExistingProvider<T = unknown> extends BaseProvider<T> {
	useExisting: ProviderToken<T>;
}

export type Provider<T = unknown> =
	| Constructor<T>
	| ClassProvider<T>
	| ValueProvider<T>
	| FactoryProvider<T>
	| ExistingProvider<T>;

export interface ResolvedProvider<T = unknown> {
	token: ProviderToken<T>;
	scope: ProviderScope;
	useClass?: Constructor<T>;
	useValue?: T;
	useFactory?: (...args: any[]) => T | Promise<T>;
	useExisting?: ProviderToken<T>;
	inject?: ProviderToken[];
	moduleName?: string;
}

export interface InjectableOptions {
	scope?: ProviderScope;
}

export interface DynamicModule extends ModuleOptions {
	module: Constructor;
}

export interface ModuleOptions {
	imports?: (
		| Constructor
		| DynamicModule
		| Promise<DynamicModule>
		| ForwardReference<Constructor>
	)[];
	providers?: Provider[];
	exports?: ProviderToken[];
}
