// @flow
/* Actions for Redux
 * all the actions for redux are defined here
 * some actions are actually async action creators for async stuff (API calls)
 * most are pretty simple and tied to a specific BEAT entity
 * each entity has the same actions available, allowing lots of templating stuff
 * these actions specify a type, using the constants in the ActionTypes
*/
import type {
	ActionType,
} from './actionTypes';

import * as Types from './actionTypes.js';
import type { Dispatch } from 'redux';
import { genModuleApiFuncs } from '@helpers/api';
import { BEAT_ENTITIES } from '@helpers/beat';

import type { BeatEntity, BeatObject, BeatSettings, BeatEnvironment } from '@helpers/beat';

export type Action = {|
	type: ActionType,
	payload: any,
	error?: boolean,
	meta?: mixed,
|};

export type ActionCreator = (any, any) => Action;

export type ActionThunkCreator = (any) => (dispatch: Dispatch<*>) => Promise<*>;

// helper to sort of flatten api response objects to arrays
const objToArr = (data: any): any[] => {
	if(!data)
		throw new Error(`Invalid data "${ data }"`);

	if(Array.isArray(data))
		return data;
	return Object.entries(data)
	.map(([k, obj]) => objToArr(obj))
	.reduce((a: any[], objs: any[]) => [...a, ...objs], [])
	;
};

export const fetchAllObjects: ActionThunkCreator = () => async (dispatch: Dispatch<*>) => {
	//console.log(`fetchAllObjects`);
	const getFuncs = [...BEAT_ENTITIES].map(e => genModuleApiFuncs(e).get);
	const jsons = await Promise.all(getFuncs.map(async (get): Promise<*> => get()));
	const arrs = jsons.map((j, i, js) => objToArr(j));

	//dispatch(settingsSave(arrs[8]));

	dispatch(databaseSave(arrs[0]));
	dispatch(librarySave(arrs[1]));
	dispatch(dataformatSave(arrs[2]));
	dispatch(algorithmSave(arrs[3]));
	dispatch(toolchainSave(arrs[4]));
	dispatch(experimentSave(arrs[5]));
	dispatch(plotterSave(arrs[6]));
	dispatch(plotterparameterSave(arrs[7]));
	dispatch(clearUnsaved());

	const settingsGet = genModuleApiFuncs('settings').get;
	const settingsJson = await settingsGet();
	dispatch(settingsSave(settingsJson));

	const envGet = genModuleApiFuncs('environments').get;
	const envJson = await envGet();
	dispatch(environmentsSave(envJson));
};

export const deleteObject = (entity: BeatEntity, obj: BeatObject): ActionThunkCreator => () =>  async (dispatch: Dispatch<*>) => {
	const del = genModuleApiFuncs(entity).delete;
	const res = await del(obj);

	switch(entity){
		case 'database':
			dispatch(databaseDelete(obj));
			break;
		case 'library':
			dispatch(libraryDelete(obj));
			break;
		case 'dataformat':
			dispatch(dataformatDelete(obj));
			break;
		case 'algorithm':
			dispatch(algorithmDelete(obj));
			break;
		case 'toolchain':
			dispatch(toolchainDelete(obj));
			break;
		case 'experiment':
			dispatch(experimentDelete(obj));
		case 'plotter':
			dispatch(plotterDelete(obj));
		case 'plotterparameter':
			dispatch(plotterparameterDelete(obj));
			break;
	}
};

export const createObject = (entity: BeatEntity, obj: BeatObject, copiedObjName?: string): ActionThunkCreator => () =>  async (dispatch: Dispatch<*>) => {
	const post = genModuleApiFuncs(entity).post;
	const payload = {
		obj,
		copiedObjName: copiedObjName || '',
	};
	const res = await post(payload);

	switch(entity){
		case 'database':
			dispatch(databaseAdd(obj));
			break;
		case 'library':
			dispatch(libraryAdd(obj));
			break;
		case 'dataformat':
			dispatch(dataformatAdd(obj));
			break;
		case 'algorithm':
			dispatch(algorithmAdd(obj));
			break;
		case 'toolchain':
			dispatch(toolchainAdd(obj));
			break;
		case 'experiment':
			dispatch(experimentAdd(obj));
		case 'plotter':
			dispatch(plotterAdd(obj));
		case 'plotterparameter':
			dispatch(plotterparameterAdd(obj));
			break;
	}
};

export const databaseSave: ActionCreator = (objs: BeatObject[]): Action => ({ type: Types.SAVE_DATABASE, payload: objs, });
const databaseAdd: ActionCreator = (obj: BeatObject): Action => ({ type: Types.ADD_DATABASE, payload: obj, });
const databaseDelete: ActionCreator = (obj: BeatObject): Action => ({ type: Types.DELETE_DATABASE, payload: obj, });
export const databaseUpdate: ActionCreator = (oldName: string, obj: BeatObject): Action => ({ type: Types.UPDATE_DATABASE, payload: {oldName, obj}, });

export const librarySave: ActionCreator = (objs: BeatObject[]): Action => ({ type: Types.SAVE_LIBRARY, payload: objs, });
const libraryAdd: ActionCreator = (obj: BeatObject): Action => ({ type: Types.ADD_LIBRARY, payload: obj, });
const libraryDelete: ActionCreator = (obj: BeatObject): Action => ({ type: Types.DELETE_LIBRARY, payload: obj, });
export const libraryUpdate: ActionCreator = (oldName: string, obj: BeatObject): Action => ({ type: Types.UPDATE_LIBRARY, payload: {oldName, obj}, });

export const dataformatSave: ActionCreator = (objs: BeatObject[]): Action => ({ type: Types.SAVE_DATAFORMAT, payload: objs, });
const dataformatAdd: ActionCreator = (obj: BeatObject): Action => ({ type: Types.ADD_DATAFORMAT, payload: obj, });
const dataformatDelete: ActionCreator = (obj: BeatObject): Action => ({ type: Types.DELETE_DATAFORMAT, payload: obj, });
export const dataformatUpdate: ActionCreator = (oldName: string, obj: BeatObject): Action => ({ type: Types.UPDATE_DATAFORMAT, payload: {oldName, obj}, });

export const algorithmSave: ActionCreator = (objs: BeatObject[]): Action => ({ type: Types.SAVE_ALGORITHM, payload: objs, });
const algorithmAdd: ActionCreator = (obj: BeatObject): Action => ({ type: Types.ADD_ALGORITHM, payload: obj, });
const algorithmDelete: ActionCreator = (obj: BeatObject): Action => ({ type: Types.DELETE_ALGORITHM, payload: obj, });
export const algorithmUpdate: ActionCreator = (oldName: string, obj: BeatObject): Action => ({ type: Types.UPDATE_ALGORITHM, payload: {oldName, obj}, });

export const toolchainSave: ActionCreator = (objs: BeatObject[]): Action => ({ type: Types.SAVE_TOOLCHAIN, payload: objs, });
const toolchainAdd: ActionCreator = (obj: BeatObject): Action => ({ type: Types.ADD_TOOLCHAIN, payload: obj, });
const toolchainDelete: ActionCreator = (obj: BeatObject): Action => ({ type: Types.DELETE_TOOLCHAIN, payload: obj, });
export const toolchainUpdate: ActionCreator = (oldName: string, obj: BeatObject): Action => ({ type: Types.UPDATE_TOOLCHAIN, payload: {oldName, obj}, });

export const experimentSave: ActionCreator = (objs: BeatObject[]): Action => ({ type: Types.SAVE_EXPERIMENT, payload: objs, });
const experimentAdd: ActionCreator = (obj: BeatObject): Action => ({ type: Types.ADD_EXPERIMENT, payload: obj, });
const experimentDelete: ActionCreator = (obj: BeatObject): Action => ({ type: Types.DELETE_EXPERIMENT, payload: obj, });
export const experimentUpdate: ActionCreator = (oldName: string, obj: BeatObject): Action => ({ type: Types.UPDATE_EXPERIMENT, payload: {oldName, obj}, });

export const plotterSave: ActionCreator = (objs: BeatObject[]): Action => ({ type: Types.SAVE_PLOTTER, payload: objs, });
const plotterAdd: ActionCreator = (obj: BeatObject): Action => ({ type: Types.ADD_PLOTTER, payload: obj, });
const plotterDelete: ActionCreator = (obj: BeatObject): Action => ({ type: Types.DELETE_PLOTTER, payload: obj, });
export const plotterUpdate: ActionCreator = (oldName: string, obj: BeatObject): Action => ({ type: Types.UPDATE_PLOTTER, payload: {oldName, obj}, });

export const plotterparameterSave: ActionCreator = (objs: BeatObject[]): Action => ({ type: Types.SAVE_PLOTTERPARAMETER, payload: objs, });
const plotterparameterAdd: ActionCreator = (obj: BeatObject): Action => ({ type: Types.ADD_PLOTTERPARAMETER, payload: obj, });
const plotterparameterDelete: ActionCreator = (obj: BeatObject): Action => ({ type: Types.DELETE_PLOTTERPARAMETER, payload: obj, });
export const plotterparameterUpdate: ActionCreator = (oldName: string, obj: BeatObject): Action => ({ type: Types.UPDATE_PLOTTERPARAMETER, payload: {oldName, obj}, });

export const environmentsSave: ActionCreator = (objs: BeatEnvironment[]): Action => ({ type: Types.SAVE_ENVIRONMENTS, payload: objs, });

export const settingsSave: ActionCreator = (obj: BeatSettings): Action => ({ type: Types.SAVE_SETTINGS, payload: obj, });
export const settingsUpdate: ActionCreator = (obj: BeatSettings): Action => ({ type: Types.UPDATE_SETTINGS, payload: obj, });

export const clearUnsaved: ActionCreator = (): Action => ({ type: Types.CLEAR_UNSAVED, payload: null, });
