import { Dispatch } from "redux";

import { navigation } from "@ax/api";
import {
	IBreadcrumbItem,
	IModule,
	INavigation,
	INavPages,
	INotification,
	IRootState,
	ISchema,
	IUpdateNavigationParam,
} from "@ax/types";
import {
	getDefaultSchema,
	getSchema,
	handleRequest,
	removeEditorIds,
	deepClone,
	getNullValue,
	getDefaultNavigationModules,
	getImageFromHtml,
	protectFormKeys,
} from "@ax/helpers";
import {
	findByEditorID,
	generateEditorIDs,
	getLastComponentEditorID,
	getNewBreadcrumb,
	getParentKey,
	moveElement,
	replaceElements,
	updateByEditorID,
} from "@ax/forms";

import { appActions } from "@ax/containers/App";

import { getFormData, getStateValues, getTranslatedMenus } from "./utils";

import {
	SET_EDITOR_CONTENT,
	SET_BREADCRUMB,
	SET_SCHEMA,
	SET_SELECTED_CONTENT,
	SET_SELECTED_DEFAULT,
	SET_TAB,
	SET_HEADER,
	SET_FOOTER,
	SET_SELECTED_EDITOR_ID,
	SET_DEFAULTS_CONTENT,
	SET_TOTAL_ITEMS,
	ITEMS_PER_PAGE,
	SET_CURRENT_NAVIGATION_LANGUAGES,
	SET_IS_NEW_TRANSLATION,
	SET_SELECTED_PARENT,
	SET_COPY_MODULE,
	SET_NAV_PAGES,
} from "./constants";

import {
	ISetBreadcrumb,
	ISetSchema,
	ISetTab,
	ISetSelectedEditorID,
	ISetEditorContent,
	ISetSelectedContent,
	ISetSelectedDefault,
	ISetCurrentNavigationLanguages,
	ISetIsNewTranslation,
	ISetSelectedParent,
	ISetCopyModule,
	ISetNavPages,
} from "./interfaces";

const { setIsLoading } = appActions;

function setEditorContent(editorContent: any): ISetEditorContent {
	return { type: SET_EDITOR_CONTENT, payload: { editorContent } };
}

function setBreadcrumb(breadcrumb: IBreadcrumbItem[]): ISetBreadcrumb {
	return { type: SET_BREADCRUMB, payload: { breadcrumb } };
}

function setSchema(schema: ISchema | Record<string, unknown>): ISetSchema {
	return { type: SET_SCHEMA, payload: { schema } };
}

function setSelectedDefaultContent(selectedContent: any): ISetSelectedContent {
	return { type: SET_SELECTED_CONTENT, payload: { selectedContent } };
}

function setSelectedDefaultParentContent(selectedParent: any): ISetSelectedParent {
	return { type: SET_SELECTED_PARENT, payload: { selectedParent } };
}

function setSelectedEditorID(selectedEditorID: number): ISetSelectedEditorID {
	return { type: SET_SELECTED_EDITOR_ID, payload: { selectedEditorID } };
}

function setTab(tab: string): ISetTab {
	return { type: SET_TAB, payload: { tab } };
}

function setSelectedDefault(selectedDefault: string): ISetSelectedDefault {
	return { type: SET_SELECTED_DEFAULT, payload: { selectedDefault } };
}

function setHeader(id: number | null) {
	return { type: SET_HEADER, payload: { header: id } };
}

function setFooter(id: number | null) {
	return { type: SET_FOOTER, payload: { footer: id } };
}

function setCurrentDefaultsContent(currentDefaultsContent: any) {
	return { type: SET_DEFAULTS_CONTENT, payload: { currentDefaultsContent } };
}

function setTotalItems(totalItems: number) {
	return { type: SET_TOTAL_ITEMS, payload: { totalItems } };
}

function setCurrentNavigationLanguages(currentNavigationLanguages: any[]): ISetCurrentNavigationLanguages {
	return { type: SET_CURRENT_NAVIGATION_LANGUAGES, payload: { currentNavigationLanguages } };
}

function setIsNewTranslation(isNewTranslation: boolean): ISetIsNewTranslation {
	return { type: SET_IS_NEW_TRANSLATION, payload: { isNewTranslation } };
}

function setCopyModule(moduleCopy: { date: Date; elements: IModule[] } | null): ISetCopyModule {
	return { type: SET_COPY_MODULE, payload: { moduleCopy } };
}

function setNavPages(navigationPages: INavPages): ISetNavPages {
	return { type: SET_NAV_PAGES, payload: { navigationPages } };
}

// API RELATED FUNCTIONS

function getHeaders(params: any): (dispatch: Dispatch, getState: () => IRootState) => Promise<void> {
	return async (dispatch, getState) => {
		try {
			const { currentSiteInfo } = getStateValues(getState);
			const siteID = currentSiteInfo.id;

			const responseActions = {
				handleSuccess: (response: any) => {
					dispatch(setCurrentDefaultsContent(response.items));
					dispatch(setTotalItems(response.totalItems));
				},
				handleError: (response: any) => appActions.handleError(response)(dispatch),
			};

			const callback = async () => navigation.getHeaders(siteID, params);

			await handleRequest(callback, responseActions, [appActions.setIsLoading])(dispatch);
		} catch (e) {
			console.log(e); // TODO: capturar error bien
		}
	};
}

function getFooters(params: any): (dispatch: Dispatch, getState: any) => Promise<void> {
	return async (dispatch, getState) => {
		try {
			const { currentSiteInfo } = getStateValues(getState);
			const siteID = currentSiteInfo.id;

			const responseActions = {
				handleSuccess: (response: any) => {
					dispatch(setCurrentDefaultsContent(response.items));
					dispatch(setTotalItems(response.totalItems));
				},
				handleError: (response: any) => appActions.handleError(response)(dispatch),
			};

			const callback = async () => navigation.getFooters(siteID, params);

			await handleRequest(callback, responseActions, [appActions.setIsLoading])(dispatch);
		} catch (e) {
			console.log(e); // TODO: capturar error bien
		}
	};
}

function getDefaults(params?: any): (dispatch: Dispatch, getState: () => IRootState) => Promise<void> {
	return async (dispatch, getState) => {
		try {
			const { currentSiteInfo } = getStateValues(getState);
			const siteID = currentSiteInfo.id;

			const responseActions = {
				handleSuccess: (response: any) => {
					dispatch(setCurrentDefaultsContent(response.items));
				},
				handleError: (response: any) => appActions.handleError(response)(dispatch),
			};

			const callback = async () => {
				const headersPromise = navigation.getHeaders(siteID, params);
				const footersPromise = navigation.getFooters(siteID, params);
				const [headerDefaults, footerDefaults] = await Promise.all([headersPromise, footersPromise]);

				const defaults: any = { ...headerDefaults };
				defaults.data.items.push(...footerDefaults.data.items);

				return defaults;
			};

			await handleRequest(callback, responseActions, [])(dispatch);
		} catch (e) {
			console.log(e); // TODO: capturar error bien
		}
	};
}

function getNavigationByType(type: string): (dispatch: Dispatch, getState: () => IRootState) => void {
	return async (dispatch, getState) => {
		const {
			app: { token, lang },
		} = getState();

		const params = {
			page: 1,
			itemsPerPage: ITEMS_PER_PAGE,
			lang: lang.id,
			token,
			pagination: true,
			deleted: false,
			include_draft: false,
		};

		switch (type) {
			case "header":
				await getHeaders(params)(dispatch, getState);
				break;
			case "footer":
				await getFooters(params)(dispatch, getState);
				break;
		}
	};
}

function createNavigation(
	navHtml?: HTMLDivElement | null,
): (dispatch: Dispatch, getState: () => IRootState) => Promise<boolean> {
	return async (dispatch, getState) => {
		try {
			dispatch(appActions.setIsSaving(true));

			const {
				navigation: { editorContent, isNewTranslation },
				sites: { currentSiteInfo },
				app: { lang },
			} = getState();

			if (!editorContent || !currentSiteInfo) {
				return false;
			}

			if (isNewTranslation) {
				delete editorContent.id;
			}

			let image = null;
			if (navHtml) {
				image = await getImageFromHtml(navHtml, "navigation-thumbnail");
			}

			const navigationValues = { ...editorContent, site: currentSiteInfo.id, language: lang.id };
			const cleanValues = removeEditorIds(navigationValues);

			const form = getFormData(cleanValues, image);

			const successAction = async (response: any) => {
				const updatedContent = { ...editorContent, ...response };
				generateContent(updatedContent)(dispatch, getState);
				dispatch(setIsNewTranslation(false));
				dispatch(appActions.setIsSaving(false));
			};

			const responseActions = {
				handleSuccess: (response: any) => successAction(response),
				handleError: (response: any) => {
					appActions.handleError(response)(dispatch);
					dispatch(appActions.setIsSaving(false));
				},
			};

			const callback = async () => navigation.createNavigation(form);

			return await handleRequest(callback, responseActions, [])(dispatch);
		} catch (e) {
			console.log(e);
			return false;
		}
	};
}

function updateNavigation(
	navID: number,
	data: any,
	fromEditor?: boolean,
	navHtml?: HTMLDivElement | null,
): (dispatch: Dispatch, getState: () => IRootState) => Promise<boolean> {
	return async (dispatch, getState) => {
		try {
			dispatch(appActions.setIsSaving(true));

			const { isNewTranslation } = getStateValues(getState);
			if (isNewTranslation) {
				delete data.id;
			}

			let image = null;
			if (navHtml) {
				image = await getImageFromHtml(navHtml, "navigation-thumbnail");
			}

			const cleanValues = removeEditorIds(data);

			const form = getFormData(cleanValues, image);

			const successAction = async (response: any) => {
				if (fromEditor) {
					generateContent(response)(dispatch, getState);
				} else {
					getNavigationByType(data.type)(dispatch, getState);
				}
				dispatch(appActions.setIsSaving(false));
			};
			const responseActions = {
				handleSuccess: (response: any) => successAction(response),
				handleError: (response: any) => {
					appActions.handleError(response)(dispatch);
					dispatch(appActions.setIsSaving(false));
				},
			};

			const callback = async () => navigation.updateNavigation(navID, form);

			return await handleRequest(callback, responseActions, [appActions.setIsSaving])(dispatch);
		} catch (e) {
			console.log(e);
			return false;
		}
	};
}

function deleteNavigation(
	navID: number | number[],
	type: string,
	errorAction: () => void,
): (dispatch: Dispatch, getState: () => IRootState) => Promise<boolean> {
	return async (dispatch, getState) => {
		try {
			const isBulk = Array.isArray(navID);
			const responseActions = {
				handleSuccess: () => getNavigationByType(type)(dispatch, getState),
				handleError: (response: any) => {
					const {
						data: { message, code },
					} = response;

					if (isBulk) {
						getNavigationByType(type)(dispatch, getState);
					}

					if (code === 403) {
						response.btnText = "Show Pages";
						response.btnAction = errorAction;
					}

					const isMultiple = Array.isArray(message) && message.length > 1;
					const msg = isMultiple ? `The delete action failed due to ${message.length} errors.` : undefined;
					appActions.handleError(response, isMultiple, msg)(dispatch);
				},
			};

			const callback = async () =>
				Array.isArray(navID) ? navigation.deleteNavigationBulk(navID) : navigation.deleteNavigation(navID);

			return await handleRequest(callback, responseActions, [appActions.setIsLoading])(dispatch);
		} catch (e) {
			console.log(e); // TODO: capturar error bien
			return false;
		}
	};
}

function restoreNavigation(
	navID: number | number[],
	type: string,
): (dispatch: Dispatch, getState: () => IRootState) => Promise<boolean> {
	return async (dispatch, getState) => {
		try {
			const responseActions = {
				handleSuccess: () => getNavigationByType(type)(dispatch, getState),
				handleError: (response: any) => {
					const {
						data: { message },
					} = response;
					const isMultiple = Array.isArray(message) && message.length > 1;
					const msg = isMultiple ? `The restore action failed due to ${message.length} errors.` : undefined;
					appActions.handleError(response, isMultiple, msg)(dispatch);
				},
			};

			const callback = async () =>
				Array.isArray(navID) ? navigation.restoreNavigationBulk(navID) : navigation.restoreNavigation(navID);

			return await handleRequest(callback, responseActions, [appActions.setIsLoading])(dispatch);
		} catch (e) {
			console.log(e);
			return false;
		}
	};
}

function setDefaultNavigation(
	navID: number,
	type: string,
): (dispatch: Dispatch, getState: () => IRootState) => Promise<void> {
	return async (dispatch, getState) => {
		try {
			const responseActions = {
				handleSuccess: () => getNavigationByType(type)(dispatch, getState),
				handleError: (response: any) => appActions.handleError(response)(dispatch),
			};

			const callback = async () => navigation.setDefaultNavigation(navID);

			await handleRequest(callback, responseActions, [appActions.setIsSaving])(dispatch);
		} catch (e) {
			console.log(e); // TODO: capturar error bien
		}
	};
}

function generateNewDefault(dispatch: Dispatch, getState: () => IRootState) {
	const {
		selectedDefault,
		currentSiteInfo: { navigationModules },
	} = getStateValues(getState);

	const isHeader = selectedDefault === "Headers";
	const { defaultHeader, defaultFooter } = getDefaultNavigationModules();
	const headerComponent = navigationModules?.header || defaultHeader.component;
	const footerComponent = navigationModules?.footer || defaultFooter.component;
	const component = isHeader ? headerComponent : footerComponent;
	const defaultSchema = getDefaultSchema(component);

	const rootEditorID = 0;
	dispatch(setSelectedEditorID(rootEditorID));
	dispatch(setIsNewTranslation(false));
	dispatch(setCurrentNavigationLanguages([]));

	generateContent(defaultSchema)(dispatch, getState);
}

function getValues(): (dispatch: Dispatch, getState: () => IRootState) => Promise<void> {
	return async (dispatch, getState) => {
		try {
			const { selectedDefault, header, footer, isNewTranslation, currentSiteInfo, menus } = getStateValues(getState);

			const isHeader = selectedDefault === "Headers";
			const currentNavigation = isHeader ? header : footer;
			const type = isHeader ? "header" : "footer";
			const isNew = !currentNavigation;
			if (isNew) {
				if (!isNewTranslation) {
					dispatch(setCurrentNavigationLanguages([]));
				}
				generateNewDefault(dispatch, getState);
			} else {
				const responseActions = {
					handleSuccess: async (response: any) => {
						const rootEditorID = 0;
						const navigationModuleComponent = currentSiteInfo.navigationModules?.[type];
						if (navigationModuleComponent) {
							response.component = navigationModuleComponent;
						}
						if (isNewTranslation) {
							getTranslatedMenus(response, menus);
						}
						dispatch(setSelectedEditorID(rootEditorID));
						generateContent(response)(dispatch, getState);
					},
					handleError: (response: any) => appActions.handleError(response)(dispatch),
				};

				const callback = async () => navigation.getNavigation(currentNavigation);
				await handleRequest(callback, responseActions, [appActions.setIsLoading])(dispatch);
			}

			getNavigationByType(type)(dispatch, getState);
		} catch (e) {
			console.log(e); // TODO: capturar error bien
		}
	};
}

function createNewTranslation(isNewTranslation: boolean): (dispatch: Dispatch) => void {
	return async (dispatch) => {
		try {
			dispatch(setIsNewTranslation(isNewTranslation));
		} catch (e) {
			console.log(e); // TODO: capturar error bien
		}
	};
}

const addElement = (element: any) => {
	const defaultSchema = getDefaultSchema(element.component);
	for (const key in defaultSchema) {
		element[key] = defaultSchema[key];
	}
};

const updateCollection = (elementType: string, prevCollection: any[]) => {
	const newModule = getDefaultSchema(elementType);
	return [...prevCollection, newModule];
};

const getUpdatedComponents = (editorContent: any, element: any, key: string) => {
	const { editorID, type } = element;
	const isCollectionItem = typeof type === "string";
	const updatedDefault = isCollectionItem ? deepClone(editorContent) : editorContent;

	const mapValues = (item: any) => {
		if (item.editorID !== undefined && item.editorID === editorID) {
			if (isCollectionItem) {
				item[key] = updateCollection(type, item[key] || []);
			} else {
				addElement(type);
			}
		}

		const itemKeys = Object.keys(item);
		itemKeys.forEach((key: string) => {
			const containedValue = item[key];
			if (containedValue && typeof containedValue === "object") {
				mapValues(containedValue);
			}
		});
	};

	mapValues(updatedDefault);
	return updatedDefault;
};

function addModule(
	type: string,
	key: string,
	// biome-ignore lint/correctness/noUnusedFunctionParameters: TODO: fix this
	selectedID: number,
	// biome-ignore lint/correctness/noUnusedFunctionParameters: TODO: fix this
	isComponentModule?: boolean,
): (dispatch: Dispatch, getState: () => IRootState) => void {
	return (dispatch, getState) => {
		addComponent(type, key)(dispatch, getState);
	};
}

function addComponent(type: any, key: string): (dispatch: Dispatch, getState: () => IRootState) => void {
	return (dispatch, getState) => {
		const { editorContent, selectedEditorID } = getStateValues(getState);
		const component = {
			editorID: selectedEditorID || 0,
			type,
		};
		const updatedContent = getUpdatedComponents(editorContent, component, key);
		generateContent(updatedContent)(dispatch, getState);

		if (type.editorID) {
			localStorage.setItem("selectedID", `${type.editorID}`);
		} else {
			const { editorContent } = getStateValues(getState);
			const lastElementEditorID = getLastComponentEditorID(editorContent, component.editorID, key);
			localStorage.setItem("selectedID", `${lastElementEditorID}`);
		}
	};
}

function deleteModule(editorID: number[], key: string): (dispatch: Dispatch, getState: () => IRootState) => void {
	return (dispatch, getState) => {
		const { editorContent } = getStateValues(getState);
		const updatedContent: any = deepClone(editorContent);

		const { parent, grandParent } = findByEditorID(updatedContent, editorID[0]);
		const parentModule = Array.isArray(parent) ? grandParent : parent;

		const parentKey = key ? key : getParentKey(parentModule, editorID[0]);
		const itemsArr = parentModule[parentKey];

		editorID.forEach((moduleID) => {
			const index = itemsArr.findIndex((module: IModule) => module.editorID === moduleID);
			itemsArr.splice(index, 1);
		});

		generateContent(updatedContent)(dispatch, getState);
	};
}

function replaceModule(
	module: any,
	parent: any,
	objKey: string,
): (dispatch: Dispatch, getState: () => IRootState) => void {
	return async (dispatch, getState) => {
		const { editorContent } = getStateValues(getState);
		const updatedVal = {
			...getNullValue(parent[objKey], true),
		};

		Object.keys(updatedVal).forEach((key: string) => {
			if (updatedVal[key].editorID === module.editorID) {
				const defaultSchema = getDefaultSchema(module.component);
				updatedVal[key] = {
					...defaultSchema,
					...updatedVal[key],
				};
			}
		});

		const updatedContent = updateByEditorID(editorContent, parent.editorID, objKey, updatedVal);
		generateContent(updatedContent)(dispatch, getState);
	};
}

function duplicateModule(editorID: number[], key?: string): (dispatch: Dispatch, getState: () => IRootState) => number {
	return (dispatch, getState) => {
		const { editorContent } = getStateValues(getState);

		const updatedContent: any = { ...editorContent };
		const { parent, grandParent } = findByEditorID(updatedContent, editorID[0]);
		const parentModule = Array.isArray(parent) ? grandParent : parent;

		const parentKey = key ? key : getParentKey(parentModule, editorID[0]);
		const itemsArr = parentModule[parentKey];
		let duplicatedItemIndex = 0;

		editorID.forEach((id) => {
			const { element: originalItem } = findByEditorID(updatedContent, id);
			const originalItemIndex = itemsArr.findIndex((module: IModule) => module.editorID === id);
			duplicatedItemIndex = originalItemIndex + 1;
			itemsArr.splice(duplicatedItemIndex, 0, originalItem);
		});

		generateContent(updatedContent)(dispatch, getState);

		const { editorContent: generatedContent } = getStateValues(getState);
		const { parent: generatedParent, grandParent: generatedGrandParent } = findByEditorID(
			generatedContent,
			editorID[0],
		);
		const module = Array.isArray(generatedParent) ? generatedGrandParent : generatedParent;
		const duplicatedEditorID = module[parentKey][duplicatedItemIndex].editorID;

		localStorage.setItem("selectedID", `${duplicatedEditorID}`);

		return duplicatedEditorID;
	};
}

function moveModule(
	elementID: number[],
	content: any,
	newIndex: number,
	key: string,
): (dispatch: Dispatch, getState: () => IRootState) => void {
	return async (dispatch, getState) => {
		try {
			const {
				navigation: { selectedContent, editorContent },
			} = getState();

			const updatedContent: any = { ...editorContent };
			const contentElements = [...selectedContent[key]];
			const { element: selectedModule } = findByEditorID(updatedContent, selectedContent.editorID);
			selectedModule[key] = moveElement(elementID, contentElements, newIndex);

			dispatch(setEditorContent(updatedContent));
			dispatch(setSelectedDefaultContent(selectedContent));
		} catch {
			console.log("error in moveModule");
		}
	};
}

function copyModule(editorID: number[]): (dispatch: Dispatch, getState: () => IRootState) => boolean | number {
	return (dispatch, getState) => {
		const {
			navigation: { editorContent },
		} = getState();

		const modulesToCopy: IModule[] = [];

		editorID.forEach((id) => {
			const { element: originalElement } = findByEditorID(editorContent, id);
			if (originalElement) {
				// biome-ignore lint/correctness/noUnusedVariables: TODO: fix this
				const { editorID, parentEditorID, ...element } = originalElement;
				modulesToCopy.push(element);
			}
		});

		if (modulesToCopy.length) {
			const payload = {
				date: new Date(),
				elements: modulesToCopy,
			};
			dispatch(setCopyModule(payload));
			return payload.elements.length;
		} else {
			return false;
		}
	};
}

function pasteModule(
	editorID: number,
	key: string,
	modulesToPaste: IModule[],
): (dispatch: Dispatch, getState: () => IRootState) => Promise<{ error?: INotification }> {
	return async (dispatch, getState) => {
		const {
			navigation: { editorContent },
		}: IRootState = getState();

		if (!editorContent) {
			return { error: undefined };
		}

		const { element: originalElement } = findByEditorID(editorContent, editorID);
		const itemsArr = originalElement[key];

		modulesToPaste.forEach((element) => {
			const validatedModuleCopy: any = deepClone(element);
			itemsArr.push(validatedModuleCopy);
		});

		const updatedPageContent = {
			...editorContent,
		};

		generateContent(updatedPageContent)(dispatch, getState);

		return { error: undefined };
	};
}

function replaceElementsInCollection(
	newValue: string,
	reference: string,
): (dispatch: Dispatch, getState: () => IRootState) => void {
	return async (dispatch, getState) => {
		const { selectedContent } = getStateValues(getState);
		const key = reference ? reference : "elements";

		const updatedContent = replaceElements(selectedContent[key], newValue);

		updateEditorContent(selectedContent.editorID, key, updatedContent)(dispatch, getState);

		const { editorContent } = getStateValues(getState);
		if (!editorContent) return;
		generateContent(editorContent)(dispatch, getState);
	};
}

function generateContent(editorContent: INavigation): (dispatch: Dispatch, getState: () => IRootState) => void {
	return async (dispatch, getState) => {
		const {
			navigation: { selectedEditorID },
		} = getState();
		const { pageContent } = generateEditorIDs(editorContent);
		const { element: selectedContent, parent: selectedParent } = findByEditorID(pageContent, selectedEditorID || 0);
		const { component } = selectedContent;
		const schema = selectedContent && getSchema(component);

		dispatch(setSchema(schema));
		dispatch(setEditorContent(pageContent));
		dispatch(updateBreadcrumb(selectedEditorID || 0));
		dispatch(setSelectedDefaultContent(selectedContent));
		dispatch(setSelectedDefaultParentContent(selectedParent));
		dispatch(setCurrentNavigationLanguages(editorContent.navigationLanguages));
		dispatch(setIsLoading(false));
	};
}

function updateEditorContent(
	selectedEditorID: number,
	key: string,
	value: any,
): (dispatch: Dispatch, getState: () => IRootState) => void {
	return (dispatch, getState) => {
		const {
			navigation: { selectedContent, editorContent },
		} = getState();

		const isFormPage = selectedContent.component === "FormPage";
		const clonedSelected = deepClone(selectedContent);
		const clonedEditor = deepClone(editorContent);

		if (isFormPage) {
			protectFormKeys(clonedEditor, key, selectedEditorID);
		}

		const updatedSelectedContent = updateByEditorID(clonedSelected, selectedEditorID, key, value);
		const updatedEditorContent = updateByEditorID(clonedEditor, selectedEditorID, key, value);
		dispatch(setSelectedDefaultContent(updatedSelectedContent));
		dispatch(setEditorContent(updatedEditorContent));

		const isObjectValue = value !== null && typeof value === "object";

		if (isObjectValue) {
			generateContent(updatedEditorContent)(dispatch, getState);
		}
	};
}

function setSelectedContent(editorID: number): (dispatch: Dispatch, getState: () => IRootState) => Promise<void> {
	return async (dispatch, getState) => {
		dispatch(setIsLoading(true));
		/*
      We set selectedID in localStorage to have it retrieved
      by DCX
    */

		localStorage.setItem("selectedID", `${editorID}`);
		const {
			navigation: { editorContent },
		} = getState();
		const { element: selectedContent, parent: selectedParent } = findByEditorID(editorContent, editorID);

		const { component } = selectedContent;

		const schema = getSchema(component);
		if (!schema) return;
		dispatch(setSchema(schema));
		dispatch(setSelectedEditorID(editorID));
		dispatch(setSelectedDefaultContent({ ...selectedContent }));
		dispatch(setSelectedDefaultParentContent(selectedParent));
		dispatch(updateBreadcrumb(editorID));
		dispatch(setIsLoading(false));
	};
}

function updateBreadcrumb(editorID: number): any {
	return async (dispatch: Dispatch, getState: () => IRootState) => {
		const {
			navigation: { editorContent },
		} = getState();

		const newBreadcrumb = getNewBreadcrumb(editorContent, editorID);

		dispatch(setBreadcrumb(newBreadcrumb));
	};
}

function setSelectedTab(tab: string): (dispatch: Dispatch) => Promise<void> {
	return async (dispatch) => {
		try {
			dispatch(setTab(tab));
		} catch (e) {
			console.log("Error", e);
		}
	};
}

function resetDefaultsValues(): (dispatch: Dispatch) => Promise<void> {
	return async (dispatch) => {
		try {
			dispatch(setEditorContent(null));
			dispatch(setBreadcrumb([]));
			dispatch(setSchema({}));
			dispatch(setSelectedDefaultContent({}));
			dispatch(setSelectedEditorID(0));
			dispatch(setTab("content"));
			dispatch(setHeader(null));
			dispatch(setFooter(null));
			dispatch(setCurrentNavigationLanguages([]));
			dispatch(setIsNewTranslation(false));
			dispatch(setCurrentDefaultsContent([]));
		} catch (e) {
			console.log("Error", e); //TODO
		}
	};
}

function getPagesByNavigation(
	type: string,
	navID: number | number[],
	siteID: number,
): (dispatch: Dispatch) => Promise<void> {
	return async (dispatch) => {
		try {
			const responseActions = {
				handleSuccess: async (response: any) => dispatch(setNavPages(response)),
				handleError: (response: any) => appActions.handleError(response)(dispatch),
			};

			const callback = async () =>
				type === "header" ? navigation.getPagesByHeader(navID, siteID) : navigation.getPagesByFooter(navID, siteID);

			await handleRequest(callback, responseActions, [appActions.setIsLoading])(dispatch);
		} catch (e) {
			console.log(e);
		}
	};
}

function updatePageNavigation(data: IUpdateNavigationParam): (dispatch: Dispatch) => Promise<boolean> {
	return async (dispatch) => {
		try {
			const responseActions = {
				handleSuccess: async () => dispatch(appActions.resetError()),
				handleError: (response: any) => appActions.handleError(response)(dispatch),
			};

			const callback = async () => navigation.updatePageNavigation(data);

			return await handleRequest(callback, responseActions, [])(dispatch);
		} catch (e) {
			console.log(e);
			return false;
		}
	};
}

export {
	setEditorContent,
	setBreadcrumb,
	setSchema,
	setSelectedDefaultContent,
	setSelectedEditorID,
	setTab,
	setSelectedDefault,
	setHeader,
	setFooter,
	createNewTranslation,
	getValues,
	addModule,
	addComponent,
	replaceElementsInCollection,
	generateContent,
	updateEditorContent,
	setSelectedContent,
	updateBreadcrumb,
	setSelectedTab,
	resetDefaultsValues,
	getHeaders,
	getFooters,
	getDefaults,
	createNavigation,
	updateNavigation,
	deleteNavigation,
	restoreNavigation,
	setDefaultNavigation,
	setCurrentNavigationLanguages,
	replaceModule,
	duplicateModule,
	deleteModule,
	moveModule,
	copyModule,
	pasteModule,
	getPagesByNavigation,
	updatePageNavigation,
};
