import { DynamicModule, Module, ModuleMetadata, Provider } from '@nestjs/common'
import { AnyType } from '@core/types'
import { ClassConstructorReturnType, ClassDefinition } from './types'
import { getClientToken } from './utils'

interface RegisterOptions<C extends ClassDefinition> {
	isGlobal?: boolean
	key?: string
	client: ClassConstructorReturnType<C>
}

interface RegisterAsyncOptions<C extends ClassDefinition> extends Pick<ModuleMetadata, 'imports'> {
	isGlobal?: boolean
	key?: string
	clientType: C
	inject?: AnyType[]
	useFactory: (...args: AnyType[]) => ClassConstructorReturnType<C> | Promise<ClassConstructorReturnType<C>>
}

@Module({})
export class AwsSdkModule {
	static register<C extends ClassDefinition>(options: RegisterOptions<C>): DynamicModule {
		const { client, key = '', isGlobal = false } = options

		const providerToken = getClientToken(client, key)

		const ClientProvider: Provider = {
			provide: providerToken,
			useValue: client,
		}

		return {
			module: AwsSdkModule,
			providers: [ClientProvider],
			exports: [ClientProvider],
			global: isGlobal,
		}
	}

	static async registerAsync<C extends ClassDefinition>(options: RegisterAsyncOptions<C>): Promise<DynamicModule> {
		const { useFactory, imports = [], inject = [], isGlobal, key = '', clientType } = options

		const providerToken = getClientToken(clientType, key)

		const ClientProvider: Provider = {
			inject,
			provide: providerToken,
			useFactory,
		}

		return {
			imports,
			module: AwsSdkModule,
			providers: [ClientProvider],
			exports: [ClientProvider],
			global: isGlobal,
		}
	}
}
