import { fromJS } from 'immutable';

import createReducer from 'lib/reducer';


//basic initial state
export const initialState = {
	status: {
		isPreparing: false,
		isFetching: false,
		isSaving: false,
		isDeleting: false,
		any: false
	},
	options: {},
	formStatus: null,
	data: {},
	errors: null
};


//FETCH, FORM_OPTIONS, SAVE, DELETE

//TODO API!!! + implementation
//CRUD + filter options
export default function(reduxKey, { 
		reducer, 

		fetchRequest, 
		updateFetchPendingState,

		formOptionsRequest, 
		createRequest, 
		updateRequest, 
		deleteRequest, 

		prepareRequest, prepareFunction, 

		onSaveSuccess, onDeleteSuccess, onFetchSuccess } = {}) {

	let output = {};
	let actions = {};

	if (!reducer) {
		reducer = createReducer(initialState);
	} else if (process.env.NODE_ENV !== 'production' && typeof reducer.handleAction !== "function") {
		console.log("Provided reducer must implement handleAction(key::string, function(state, action)::state ) method");
		return output;
	}

	//inject prepare action
	if (typeof prepareRequest === "function") {
		actions.prepareEntity = function(opts) {
			return {
				type: reduxKey + "/PREPARE",
				payload: prepareRequest(opts),
				meta: opts
			}
		}
	}

	if (typeof prepareFunction !== "undefined") {
		actions.prepareEntity = function() {
			return {
				type: reduxKey + "/PREPARE",
				payload: Promise.resolve(prepareFunction.apply(null, arguments)),
				meta: arguments
			}
		}
	}

	if (typeof prepareRequest === "function" || typeof prepareFunction !== "undefined") {
		reducer.handleAction(reduxKey + '/PREPARE', (state, action) => { 
			return  _pendingState(state.setIn(["status", "isPreparing"], false).set("data", fromJS(action.payload)));
		});

		reducer.handleAction(reduxKey + '/PREPARE_PENDING', (state, action) => { 
			return  _pendingState(state.setIn(["status", "isPreparing"], true));
		});

		reducer.handleAction(reduxKey + '/PREPARE_FULFILLED', (state, action) => {
			return  _pendingState(state.setIn(["status", "isPreparing"], false).set("data", fromJS(action.payload)));
		});

		reducer.handleAction(reduxKey + '/PREPARE_REJECTED', (state, action) => {
			return  _pendingState(state.setIn(["status", "isPreparing"], false).set("error", fromJS(action.payload)).delete("data"));
		});
	}




	//inject fetch action
	if (typeof fetchRequest === "function") {
		//TODO add paging
		actions.fetchEntity = function(id, opts) {
			let obj = { ...opts, id };
			return {
				type: reduxKey + "/FETCH",
				payload: fetchRequest(obj),
				meta: obj
			}
		}

		if (typeof updateFetchPendingState === "function") {
			reducer.handleAction(reduxKey + '/FETCH_PENDING', (state, action) => { 
				let s = state.setIn(["status", "isFetching"], true);
				return _pendingState(updateFetchPendingState(s) || s);
			});
		} else {
			reducer.handleAction(reduxKey + '/FETCH_PENDING', (state, action) => { 
				return _pendingState(state.setIn(["status", "isFetching"], true));
			});
		}

		reducer.handleAction(reduxKey + '/FETCH_FULFILLED', (state, action) => {
			return _pendingState(state.setIn(["status", "isFetching"], false).set("data", fromJS(action.payload.body)));
		});

		reducer.handleAction(reduxKey + '/FETCH_REJECTED', (state, action) => {
			return _pendingState(state.setIn(["status", "isFetching"], false).set("error", fromJS(action.payload.body)).delete("data"));
		});
	}

	//TODO implement
	if (typeof formOptionsRequest === "function") {
		actions.fetchFormOptions = function(filter, {...opts}) {
			return {
				type: reduxKey + '/FORM',
				payload: formOptionsRequest({ ...opts, filter })
			}
		}

		reducer.handleAction(reduxKey + "/FORM_PENDING", (state, action) => {
			return  _pendingState(state.set("formStatus", "pending").setIn(["status", "isFormPending"], true));
		});
		reducer.handleAction(reduxKey + "/FORM_FULFILLED", (state, action) => {
			return  _pendingState(state.set("formStatus", "success").setIn(["status", "isFormPending"], false));
		});
		reducer.handleAction(reduxKey + "/FORM_REJECTED", (state, action) => {
			return  _pendingState(state.set("formStatus", "failure").setIn(["status", "isFormPending"], false));
		});
	}

	if (typeof createRequest === "function" || typeof updateRequest === "function") {
		actions.saveEntity = function(id, data, {...opts}) {
			let obj = { id, data, ...opts };
			//create
			if (!id && typeof createRequest === "function") {
				return {
					type: reduxKey + '/SAVE',
					payload: createRequest(data, opts),
					meta: obj
				}
			}
			if (id && typeof updateRequest === "function") {
				return {
					type: reduxKey + '/SAVE',
					payload: updateRequest(id, data, opts),
					meta: obj
				}
			}
			return { type: 'NOOP' };
		}

		reducer.handleAction(reduxKey + '/SAVE_PENDING', (state, action) => { 
			return  _pendingState(state.setIn(["status", "isSaving"], true));
		});

		reducer.handleAction(reduxKey + '/SAVE_FULFILLED', (state, action) => {
			state = state.set("data", fromJS(action.payload.body));
			if (typeof onSaveSuccess === "function") {
				let s = onSaveSuccess(state, action);
				if (s) {
					state = s;
				}
			}
			return  _pendingState(state.setIn(["status", "isSaving"], false));
		});

		reducer.handleAction(reduxKey + '/SAVE_REJECTED', (state, action) => {
			return  _pendingState(state.set("error", fromJS(action.payload.body)).setIn(["status", "isSaving"], false));
		});
	}

	if (typeof deleteRequest === "function") {
		actions.deleteEntity = function(id, {...opts}) {
			return {
				type: reduxKey + '/DELETE',
				payload: deleteRequest({ ...opts, id }),
				meta: { ...opts, id }
			}
		}

		reducer.handleAction(reduxKey + '/DELETE_PENDING', (state, action) => { 
			return _pendingState(state.setIn(["status", "isDeleting"], true));
		});

		reducer.handleAction(reduxKey + '/DELETE_FULFILLED', (state, action) => {
			return _pendingState(state.delete("data").setIn(["status", "isDeleting"], false));
		});

		reducer.handleAction(reduxKey + '/DELETE_REJECTED', (state, action) => {
			return _pendingState(state.set("error", fromJS(action.payload.body)).setIn(["status", "isDeleting"], false));
		});
	}

	//construct response
	output.reduxKey = reduxKey;
	output.actions = actions;
	output.reducer = reducer;
	output.selector = function(state) {
		let s = state && state.get && state.get(reduxKey);
		return s ? s.toJS() : initialState;
	};

	return output;
}


function _pendingState(state) {
	let status = state.get("status");
	let any = false; 
	if (status) {
		any = status.get("isPreparing") || status.get("isFetching") || status.get("isDeleting") || status.get("isSaving");
	}
	return state.setIn(["status", "any"], any);
}
