import { Constructable } from "../types/constructable";
import { ReduxProviderModuleOptions, AbstractReduxModule } from "../decorators/module/module.decorator";
import { Resolver } from "../resolver/resolver";
import { InstanceContainer } from "../container/instance-container";
import { METADATA_KEY } from "../constants/metadata.keys";
import { applyMiddleware, createStore, Store } from "redux";

export function createReduxStore<ActionTree>(moduleTarget: Constructable<AbstractReduxModule>) {
	/** Verify moduleoptions (1) */
	if (!Reflect.hasMetadata(METADATA_KEY.REDUX_PROVIDER_MODULE_OPTIONS, moduleTarget)) {
		throw new Error("Are you sure " + moduleTarget.name + " is a module?");
	}

	/** Verify moduleoptions (2) */
	const moduleOptions: ReduxProviderModuleOptions = Reflect.getMetadata(METADATA_KEY.REDUX_PROVIDER_MODULE_OPTIONS, moduleTarget);
	if (!moduleOptions) {
		throw new Error("Module options of " + moduleTarget.name + "must be defined!");
	}

	/** Create the instance container and add providers */
	let container = new InstanceContainer();
	const providerList = moduleOptions.providers || [];
	for (let provider of providerList) {
		Resolver.resolve(provider, container, moduleTarget.name);
	}

	/** Creating action tree */
	let actionTree: any = {};
	for (let action in moduleOptions.actions) {
		if (moduleOptions.actions.hasOwnProperty(action)) {
			actionTree[action] = Resolver.resolve(moduleOptions.actions[action], container, moduleTarget.name);
		}
	}

	/** Set type for the actiontree to provide autocomplete etc */
	const actions: ActionTree = actionTree;

	/** Get the initial state  */
	const initialState = moduleOptions.initialState;

	/** Create the store */
	let store: Store;
	if (moduleOptions.middlewares && moduleOptions.middlewares.length > 0) {
		store = createStore(moduleOptions.rootReducer, initialState, applyMiddleware(...moduleOptions.middlewares));
	} else {
		store = createStore(moduleOptions.rootReducer, initialState);
	}

	return {
		container: container,
		reduxActions: actions,
		reduxStore: store,
	};
}
