import { pages, sites, structuredData } from "@ax/api";
import { appActions } from "@ax/containers/App";
import { navigationActions } from "@ax/containers/Navigation";
import { sitesActions } from "@ax/containers/Sites";
import { structuredDataActions } from "@ax/containers/StructuredData";
import { usersActions } from "@ax/containers/Users";
import {
	checkH1content,
	checkMaxModules,
	evaluateComputedFields,
	findByEditorID,
	findFieldsErrors,
	findPackagesActivationErrors,
	generateEditorIDs,
	getLastComponentEditorID,
	getLastModuleEditorID,
	getNewBreadcrumb,
	getParentKey,
	getUpdatedComponents,
	getUpdatedSections,
	moveModule,
	parseData,
	parseValidationErrors,
	replaceElements,
	updateByEditorID,
	updateComponent,
} from "@ax/forms";
import {
	deepClone,
	getCurrentPageStructuredData,
	getDefaultSchema,
	getDefaultTemplate,
	getNullValue,
	getSchema,
	getStructuredDataFromPage,
	handleRequest,
	isReqOk,
	protectFormKeys,
} from "@ax/helpers";
import type {
	IBreadcrumbItem,
	IComponent,
	IErrorItem,
	IGetSitePagesParams,
	IModule,
	INotification,
	IPage,
	IPageLanguage,
	IRootState,
	ISavePageParams,
	ISchema,
	IStructuredDataContent,
	IUserEditing,
} from "@ax/types";

import type { Dispatch } from "redux";

import {
	RESET_PAGE_EDITOR_STORE,
	SET_BREADCRUMB,
	SET_COPY_MODULE,
	SET_CURRENT_PAGE_ID,
	SET_CURRENT_PAGE_LANGUAGES,
	SET_CURRENT_PAGE_NAME,
	SET_CURRENT_PAGE_STATUS,
	SET_EDITOR_CONTENT,
	SET_ERRORS,
	SET_IS_IA_TRANSLATED,
	SET_IS_NEW_TRANSLATION,
	SET_LAST_ELEMENT_ADDED_ID,
	SET_LAST_TIMEOUT,
	SET_SCHEMA,
	SET_SCROLL_EDITOR_ID,
	SET_SELECTED_CONTENT,
	SET_SELECTED_EDITOR_ID,
	SET_SELECTED_PARENT,
	SET_SITE_PAGE_ID,
	SET_TAB,
	SET_TEMPLATE,
	SET_TEMPLATE_CONFIG,
	SET_USER_EDITING,
	SET_VALIDATED,
} from "./constants";
import type {
	IFieldProps,
	IResetPageEditorStore,
	ISetBreadcrumb,
	ISetCopyModule,
	ISetCurrentPageIDAction,
	ISetCurrentPageLanguagesAction,
	ISetCurrentPageNameAction,
	ISetCurrentPageStatusAction,
	ISetEditorContent,
	ISetErrors,
	ISetIsIATranslated,
	ISetIsNewTranslation,
	ISetLastElementAddedId,
	ISetLastTimeout,
	ISetSchema,
	ISetScrollEditorID,
	ISetSelectedContent,
	ISetSelectedEditorID,
	ISetSelectedParent,
	ISetSitePageID,
	ISetTab,
	ISetTemplate,
	ISetTemplateConfig,
	ISetUserEditing,
	ISetValidated,
} from "./interfaces";
import { pageStatus } from "./interfaces";
import {
	addElementToCollection,
	addElementToComponent,
	getDefaultIntegrations,
	getDefaultPageNavigation,
	getPageData,
	getPageNavigation,
	getStateValues,
} from "./utils";

const { setIsLoading, setIsSaving, handleError } = appActions;
const { getDefaults } = navigationActions;

// AUDIT: THIS FILE IS WAY TOO LONG - LOOK FOR A REFACTOR SOLUTION
// FIXME: CHECK EDITOR CONTENT STRUCTURE (editorContent.editorContent)

function setEditorContent(editorContent: IPage | Record<string, unknown>): ISetEditorContent {
	const iframe = document.querySelector("iframe");
	iframe?.contentWindow?.postMessage(
		{
			type: "content-update",
			message: editorContent,
		},
		"*",
	);

	return { type: SET_EDITOR_CONTENT, payload: { editorContent } };
}

function setTemplate(template: string): ISetTemplate {
	return { type: SET_TEMPLATE, payload: { template } };
}

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 setSelectedPageContent(selectedContent: any): ISetSelectedContent {
	return { type: SET_SELECTED_CONTENT, payload: { selectedContent } };
}

function setSelectedParent(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 setCurrentPageID(currentPageID: number | null): ISetCurrentPageIDAction {
	return { type: SET_CURRENT_PAGE_ID, payload: { currentPageID } };
}

function setCurrentPageStatus(currentPageStatus: string | null): ISetCurrentPageStatusAction {
	return { type: SET_CURRENT_PAGE_STATUS, payload: { currentPageStatus } };
}

function setCurrentPageName(currentPageName: string): ISetCurrentPageNameAction {
	return { type: SET_CURRENT_PAGE_NAME, payload: { currentPageName } };
}

function setCurrentPageLanguages(currentPageLanguages: any[]): ISetCurrentPageLanguagesAction {
	return { type: SET_CURRENT_PAGE_LANGUAGES, payload: { currentPageLanguages } };
}

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

function setTemplateConfig(templateConfig: any): ISetTemplateConfig {
	return { type: SET_TEMPLATE_CONFIG, payload: { templateConfig } };
}

function setErrors(errors: IErrorItem[]): ISetErrors {
	return { type: SET_ERRORS, payload: { errors } };
}

function setValidated(validated: boolean): ISetValidated {
	return { type: SET_VALIDATED, payload: { validated } };
}

function setSitePageID(sitePageID: number | null): ISetSitePageID {
	return { type: SET_SITE_PAGE_ID, payload: { sitePageID } };
}

function setUserEditing(userEditing: IUserEditing | null): ISetUserEditing {
	return { type: SET_USER_EDITING, payload: { userEditing } };
}

function setLastElementAddedId(lastElementAddedId: null | number): ISetLastElementAddedId {
	return { type: SET_LAST_ELEMENT_ADDED_ID, payload: { lastElementAddedId } };
}

function setCopyModule(moduleCopy: { date: Date; elements: Record<string, unknown>[] } | null): ISetCopyModule {
	return { type: SET_COPY_MODULE, payload: { moduleCopy } };
}

function setIsIATranslated(isIATranslated: boolean): ISetIsIATranslated {
	return { type: SET_IS_IA_TRANSLATED, payload: { isIATranslated } };
}

function setLastTimeout(lastTimeout: NodeJS.Timeout | null): ISetLastTimeout {
	return { type: SET_LAST_TIMEOUT, payload: { lastTimeout } };
}

function resetPageEditorStore(): IResetPageEditorStore {
	return { type: RESET_PAGE_EDITOR_STORE };
}

function setScrollEditorID(scrollEditorID: number | null): ISetScrollEditorID {
	return { type: SET_SCROLL_EDITOR_ID, payload: { scrollEditorID } };
}

function setTranslatedParent(): (dispatch: Dispatch, getState: any) => void {
	return async (dispatch, getState) => {
		try {
			const {
				app: { lang },
				pageEditor: { editorContent },
			} = getState();

			const { language, pageLanguages } = editorContent;

			const translatedParent = pageLanguages.find(
				(item: IPageLanguage) => (item.languageId !== lang.id && language !== lang.id && item.pageId) || null,
			);
			const translatedParentId = translatedParent ? translatedParent.pageId : null;
			const updatedContent = { ...editorContent, parent: translatedParentId };

			dispatch(setEditorContent(updatedContent));
		} catch (e) {
			console.log(e);
		}
	};
}

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

const setRootEditorID = (dispatch: Dispatch) => {
	const rootEditorID = 0;

	dispatch(setSelectedEditorID(rootEditorID));
};

function generateNewPage(dispatch: Dispatch, getState: () => IRootState, baseSchema: string) {
	const {
		structuredData: {
			structuredData: { site },
		},
		pageEditor: { template },
		app: { lang },
		dataPacks,
		integrations: { integrations },
	} = getState();

	let page = getDefaultSchema(baseSchema);
	const parsedPageData = parseData(page, true);
	const currentTemplate = getDefaultTemplate(template);
	const pageStructuredData = getStructuredDataFromPage(site);
	const structuredData = pageStructuredData && getCurrentPageStructuredData(template, pageStructuredData);

	page = { ...parsedPageData, template: currentTemplate, language: lang.id };

	page.integrations = getDefaultIntegrations(dataPacks, integrations, template);

	if (structuredData) {
		page.structuredData = structuredData.id;
	}

	if (baseSchema === "GlobalPage") {
		page.isGlobal = true;
		page.editable = true;
	}

	dispatch(setCurrentPageName(page.title));
	dispatch(setIsNewTranslation(false));
	dispatch(setCurrentPageLanguages([]));
	dispatch(setUserEditing(null));

	// SET PAGE AS ROOT WHEN NEW PAGE
	setRootEditorID(dispatch);
	generatePageContent(page)(dispatch, getState);
}

// API RELATED ACTIONS
function getPageBreadcrumb(pageId: number): (dispatch: Dispatch, getState: () => IRootState) => Promise<void> {
	return async (dispatch, getState) => {
		try {
			const { selectedEditorID } = getState().pageEditor;

			const response: { status: number; data: any } = await pages.getPageBreadcrumb(pageId);
			if (isReqOk(response.status) && selectedEditorID) {
				const { breadcrumb, path } = response.data;

				updateEditorContent(selectedEditorID, "breadcrumb", breadcrumb)(dispatch, getState);
				updateEditorContent(selectedEditorID, "path", path)(dispatch, getState);
			}
		} catch (e) {
			console.log(e); // TODO: capturar error bien
		}
	};
}

function getPage(
	pageID?: number,
	global?: boolean,
	preferDraft?: boolean,
): (dispatch: Dispatch, getState: () => IRootState) => Promise<void> {
	return async (dispatch, getState) => {
		try {
			dispatch(setIsLoading(true));
			const {
				sites: { currentSiteInfo },
				pageEditor: { isNewTranslation, template },
			} = getState();

			const baseSchema = global ? "GlobalPage" : "Page";

			if (pageID) {
				const response: { status: number; data: IPage } = await pages.getPageInfo(pageID);
				const page: IPage = parseData(response.data, true);

				if (preferDraft && page.haveDraftPage) {
					return getPage(page.haveDraftPage, global)(dispatch, getState);
				}

				if (currentSiteInfo) {
					await getPageLanguages(pageID, currentSiteInfo.id, page.entity)(dispatch);
				} else {
					await getPageLanguages(pageID, null, page.entity)(dispatch);
				}

				const { liveStatus, component } = getDefaultSchema(baseSchema);

				if (isNewTranslation) {
					page.liveStatus = liveStatus;
					page.canBeTranslated = true;
					page.originalLanguage = page.language;
				}

				if (global) page.component = component;

				const pageLiveStatus =
					page.draftFromPage && !page.publicationScheduled
						? pageStatus.MODIFIED
						: isNewTranslation
							? pageStatus.OFFLINE
							: page.liveStatus.status;

				if (isReqOk(response.status)) {
					addTemplate(page.templateId)(dispatch);
					setRootEditorID(dispatch);
					generatePageContent(page)(dispatch, getState);
					dispatch(setCurrentPageName(page.title));
					dispatch(setCurrentPageID(page.id));
					dispatch(setTemplateConfig(page.templateConfig));
					dispatch(setUserEditing(page.editing));
					dispatch(setCurrentPageStatus(pageLiveStatus));
				}
			} else {
				if (!isNewTranslation) {
					dispatch(setCurrentPageLanguages([]));
				}
				await getTemplateConfig(template)(dispatch, getState);
				generateNewPage(dispatch, getState, baseSchema);
			}
			dispatch(setIsLoading(false));
		} catch (e) {
			console.log(e); // TODO: capturar error bien
		}
	};
}

function getTemplatePage(template: string): (dispatch: Dispatch, getState: () => IRootState) => Promise<void> {
	return async (dispatch, getState) => {
		try {
			dispatch(setIsLoading(true));

			const baseSchema = "Page";

			await addTemplate(template)(dispatch);
			await getTemplateConfig(template)(dispatch, getState);
			await getDefaults()(dispatch, getState);
			generateNewPage(dispatch, getState, baseSchema);
			dispatch(setIsLoading(false));
		} catch (e) {
			console.log(e); // TODO: capturar error bien
		}
	};
}

function savePage(data?: {
	createDraft?: boolean;
	publishPage?: { status: string };
	publishDraft?: boolean;
	setToast?: (msg: string) => void;
}): (dispatch: Dispatch, getState: any) => Promise<boolean> {
	return async (dispatch, getState) => {
		try {
			dispatch(setIsSaving(true));
			const { createDraft = false, publishPage, publishDraft, setToast } = data || {};

			const {
				pageEditor: {
					selectedEditorID,
					isNewTranslation,
					editorContent: { header, footer, templateConfig, template },
					lastTimeout,
				},
				sites: { currentSiteInfo },
				app: { lang },
			} = getState();

			if (lastTimeout) {
				clearTimeout(lastTimeout);
			}

			const rootEditorID = 0;
			const { values: page, id } = getPageData(getState, false);
			const values = await evaluateComputedFields(page, "save");

			const isNewPage = !id;

			values.language = lang;

			if (isNewTranslation) {
				delete values.id;
			}

			if (!isNewPage && !isNewTranslation && !createDraft) {
				delete values.language;
			}

			if (createDraft) {
				values.draftFromPage = values.id;
				delete values.id;
			}

			/* remove header and footer if page should get the default  */
			const { defaultHeader, defaultFooter } = templateConfig?.templates?.[template.templateType] || {};

			if (header && ((header.setAsDefault && !defaultHeader) || header.id === defaultHeader)) {
				values.header = null;
			}

			if (footer && ((footer.setAsDefault && !defaultFooter) || footer.id === defaultFooter)) {
				values.footer = null;
			}

			const saveResponse =
				isNewPage || isNewTranslation || createDraft
					? await pages.createPage(values)
					: await pages.updatePage(id, values, publishDraft);

			if (isReqOk(saveResponse.status)) {
				const pageID = saveResponse.data.id;
				const updatedPage = await pages.getPageInfo(pageID);
				const mappedData = parseData(updatedPage.data, true);
				const { pageContent } = generateEditorIDs({ ...mappedData, header, footer });

				const isGlobalPage = saveResponse.data.component === "GlobalPage";
				!isGlobalPage &&
					(await getPageLanguages(pageID, currentSiteInfo?.id || null, saveResponse.data.entity)(dispatch));

				if (publishPage) {
					await pages.setPageStatus(publishPage.status, [pageID]);
					dispatch(setCurrentPageStatus(publishPage.status));
				}

				if (createDraft) {
					dispatch(setCurrentPageStatus(pageStatus.MODIFIED));
				}

				if (publishDraft) {
					dispatch(setCurrentPageStatus(saveResponse.data.liveStatus.status));
				}

				dispatch(setEditorContent(pageContent));
				dispatch(setCurrentPageID(pageID));
				dispatch(setIsNewTranslation(false));

				if (selectedEditorID === rootEditorID) {
					setSelectedContent(rootEditorID)(dispatch, getState);
				}
				dispatch(setIsSaving(false));
				return true;
			} else {
				if (typeof setToast === "function") {
					setToast(saveResponse.data.message);
				} else {
					appActions.handleError(saveResponse)(dispatch);
				}
				dispatch(setIsSaving(false));
				return false;
			}
		} catch (e) {
			console.log(e); // TODO: capturar error bien
			dispatch(setIsSaving(false));
			return false;
		}
	};
}

function deletePage(params?: ISavePageParams): (dispatch: Dispatch, getState: () => IRootState) => Promise<boolean> {
	return async (dispatch, getState) => {
		try {
			const page = getPageData(getState, true, params);
			const responseActions = {
				handleSuccess: () => true,
				handleError: (response: any) => appActions.handleError(response)(dispatch),
			};

			const callback = async () => pages.deletePage(page.id);

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

function deleteBulk(ids: any): (dispatch: Dispatch) => Promise<boolean> {
	return async (dispatch) => {
		try {
			const responseActions = {
				handleSuccess: () => true,
				handleError: (response: any) => {
					const {
						data: { message },
					} = response;
					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 () => pages.bulkDelete(ids);

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

function restorePage(id: number | number[]): (dispatch: Dispatch) => Promise<boolean> {
	return async (dispatch) => {
		try {
			const responseActions = {
				handleSuccess: () => true,
				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(id) ? pages.bulkRestore(id) : pages.restorePage(id));

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

function updatePageStatus(
	ids: number[],
	status: string,
	updatedFromList?: boolean,
): (dispatch: Dispatch, getState: () => IRootState) => Promise<boolean> {
	return async (dispatch, getState) => {
		try {
			dispatch(setIsSaving(true));
			const {
				app: {
					globalSettings: { skipReviewOnPublish },
				},
				pageEditor: {
					editorContent: { header, footer },
				},
				sites: { currentSiteInfo },
			} = getState();

			const pagesWithErrors: number[] = [];
			if (status === pageStatus.UPLOAD_PENDING && updatedFromList && !skipReviewOnPublish && ids.length > 1) {
				const getPagesParams: IGetSitePagesParams = {
					siteID: currentSiteInfo ? currentSiteInfo.id : "global",
					filterPages: ids,
					deleted: false,
				};
				const { data } = await sites.getSitePages(getPagesParams);

				data.items.forEach((page: any) => {
					const errors = findFieldsErrors(page);
					if (errors.length) pagesWithErrors.push(page.id);
				});

				dispatch(sitesActions.setCurrentSiteErrorPages(pagesWithErrors));
			}

			const validIds = ids.filter((id) => !pagesWithErrors.includes(id));
			if (!validIds.length) return false;

			const responseActions = {
				handleSuccess: async () => {
					if (!updatedFromList) {
						const currentPageID = ids[0];
						const updatedContent = await pages.getPageInfo(currentPageID);
						const mappedData = parseData(updatedContent.data, true);
						const { pageContent } = generateEditorIDs({ ...mappedData, header, footer });
						dispatch(setEditorContent(pageContent));
						dispatch(setCurrentPageStatus(status));
					}
				},
				handleError: (response: any) => {
					const {
						data: { message },
					} = response;
					const isMultiple = Array.isArray(message) && message.length > 1;
					const msg = isMultiple ? `This action failed due to ${message.length} errors.` : undefined;
					appActions.handleError(response, isMultiple, msg)(dispatch);
				},
			};

			const callback = async () => await pages.setPageStatus(status, validIds);

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

function getPageLanguages(
	pageID: number,
	siteID: number | null,
	entity?: string,
): (dispatch: Dispatch) => Promise<void> {
	return async (dispatch) => {
		try {
			const response: any = await pages.getPageLanguages(pageID, siteID, entity);

			if (isReqOk(response.status)) {
				dispatch(setCurrentPageLanguages(response.data.items));
			} else {
				console.log("Error en getPageLanguages"); // FIXME: capturar errores mejor
			}
		} catch (e) {
			console.log(e); // FIXME: capturar errores mejor
		}
	};
}

function duplicatePage(pageID: number, data: any, siteID?: number): (dispatch: Dispatch) => Promise<boolean> {
	return async (dispatch) => {
		try {
			const responseActions = {
				handleSuccess: (data: any) => {
					if (!siteID) {
						dispatch(setCurrentPageID(data.id));
						dispatch(setCurrentPageStatus(pageStatus.OFFLINE));
					}
					return true;
				},
				handleError: (response: any) => {
					appActions.handleError(response)(dispatch);
					return false;
				},
			};

			const callback = async () => pages.duplicatePage(pageID, data, siteID);

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

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

function addComponent(
	type: IComponent | string,
	key: string,
): (dispatch: Dispatch, getState: () => IRootState) => void {
	return (dispatch, getState) => {
		const { editorContent, sections, editorID } = getStateValues(getState);
		const component = {
			editorID,
			type,
		};

		let lastElementEditorID = null;

		if (sections.length) {
			const updatedObj = getUpdatedComponents(sections, component, key);
			const updatedSections = updatedObj.updatedSections;

			const updatedPageContent = {
				...editorContent,
				template: {
					...editorContent.template,
					...updatedSections,
				},
			};

			generatePageContent(updatedPageContent)(dispatch, getState);

			if (typeof type === "object" && Object.hasOwn(type, "editorID")) {
				setSelectedContent(type.editorID)(dispatch, getState);
			} else {
				const { sections: generatedSections } = getStateValues(getState);

				lastElementEditorID = getLastComponentEditorID(generatedSections, component.editorID, key);
				dispatch(setLastElementAddedId(lastElementEditorID));
				localStorage.setItem("selectedID", `${lastElementEditorID}`);
			}
		} else {
			lastElementEditorID = addElementToCollection(type, key, dispatch, getState);
		}

		if (lastElementEditorID) {
			dispatch(setLastElementAddedId(lastElementEditorID));
			localStorage.setItem("selectedID", `${lastElementEditorID}`);
		}
	};
}

function addComponentInArray(
	type: string,
	key: string,
	parentKey: string,
	index: number,
	isMultiple: boolean,
): (dispatch: Dispatch, getState: () => IRootState) => void {
	return (dispatch, getState) => {
		const { editorContent, selectedContent } = getStateValues(getState);
		const editorID = isMultiple
			? selectedContent[parentKey][index][key][type].editorID
			: selectedContent[parentKey][index][key].editorID;

		const clonedContent = deepClone(editorContent);
		const { element, parent } = findByEditorID(clonedContent, editorID);

		addElementToComponent(parent, element);

		generatePageContent(clonedContent)(dispatch, getState);
		setSelectedContent(element.editorID)(dispatch, getState);
	};
}

function addModule(
	type: string,
	key: string,
	selectedID: number,
	isComponentModule?: boolean,
): (dispatch: Dispatch, getState: () => IRootState) => void {
	return (dispatch, getState) => {
		const { editorContent, sections, editorID } = getStateValues(getState);

		const { isMaxModules, errorMessage } = checkMaxModules(editorContent, type);
		if (isMaxModules) {
			handleError({ text: errorMessage })(dispatch);
			return;
		}

		let lastModuleEditorID = null;

		if (sections.length) {
			const componentModule = {
				editorID,
				type,
			};

			let updatedSections: any,
				updatedSectionIndex = 0;

			if (isComponentModule) {
				const updatedObj = getUpdatedComponents(sections, componentModule, key);
				updatedSections = updatedObj.updatedSections;
				updatedSectionIndex = updatedObj.selectedIndex;
			} else {
				const updatedObj = getUpdatedSections(sections, selectedID, type);
				updatedSections = updatedObj.updatedSections;
				updatedSectionIndex = updatedObj.selectedIndex;
			}

			const updatedPageContent = {
				...editorContent,
				template: {
					...editorContent.template,
					...updatedSections,
				},
			};

			generatePageContent(updatedPageContent)(dispatch, getState);

			const { sections: generatedSections } = getStateValues(getState);
			lastModuleEditorID = getLastModuleEditorID(generatedSections, updatedSectionIndex);
		} else {
			lastModuleEditorID = addElementToCollection(type, key, dispatch, getState);
		}

		dispatch(setLastElementAddedId(lastModuleEditorID));
		localStorage.setItem("selectedID", `${lastModuleEditorID}`);
	};
}

function replaceModule(
	module: IComponent,
	parent: any,
	objKey: string,
): (dispatch: Dispatch, getState: () => IRootState) => void {
	return async (dispatch, getState) => {
		const { editorContent } = getStateValues(getState);

		const [parentKey, childKey] = objKey.split(".");
		const value = childKey ? parent[parentKey][childKey] : parent[parentKey];
		const updatedVal = {
			...getNullValue(value, 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);
		generatePageContent(updatedContent)(dispatch, getState);
	};
}

function replaceModuleInArray(
	module: string,
	parent: any,
	key: string,
	parentKey: string,
	index: number,
): (dispatch: Dispatch, getState: () => IRootState) => void {
	return async (dispatch, getState) => {
		const { editorContent } = getStateValues(getState);
		const clonedEditor = deepClone(editorContent);

		const value = parent[parentKey][index][key];
		const updatedVal = {
			...getNullValue(value, true),
		};

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

		const { element } = findByEditorID(clonedEditor, parent.editorID);
		element[parentKey][index][key] = updatedVal;
		const editorID = element[parentKey][index][key][module].editorID;

		generatePageContent(clonedEditor)(dispatch, getState);
		setSelectedContent(editorID)(dispatch, getState);
	};
}

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);
		generatePageContent(editorContent)(dispatch, getState);
	};
}

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

		let itemsArr: IModule[] = [];
		if (sections.length) {
			const updatedSections: any = [...sections];
			const { parent, grandParent } = findByEditorID(updatedSections, editorID[0]);
			const parentModule = Array.isArray(parent) ? grandParent : parent;

			const parentKey = key ? key : getParentKey(parentModule, editorID[0]);
			itemsArr = parentModule[parentKey];
		} else {
			itemsArr = editorContent.template[key as string];
		}

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

		const updatedPageContent = {
			...editorContent,
		};

		if (errors.length) {
			validatePage()(dispatch, getState);
		}

		generatePageContent(updatedPageContent)(dispatch, getState);

		dispatch(setLastElementAddedId(null));
	};
}

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

		const updatedSections: any[] = sections.length ? [...sections] : [editorContent.template];

		const { parent, grandParent } = findByEditorID(updatedSections, 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(updatedSections, id);
			const { isMaxModules, errorMessage } = checkMaxModules(editorContent, originalItem.component);

			if (isMaxModules) {
				handleError(errorMessage)(dispatch);
				return;
			} else {
				const originalItemIndex = itemsArr.findIndex((module: any) => module.editorID === id);
				duplicatedItemIndex = originalItemIndex + 1;
				itemsArr.splice(duplicatedItemIndex, 0, originalItem);
			}
		});

		const updatedPageContent = {
			...editorContent,
		};

		generatePageContent(updatedPageContent)(dispatch, getState);

		const { sections: generatedSections, editorContent: generatedEditor } = getStateValues(getState);
		const generatedContent = generatedSections.length ? generatedSections : generatedEditor.template;
		const { parent: generatedParent, grandParent: generatedGrandParent } = findByEditorID(
			generatedContent,
			editorID[0],
		);
		const section = Array.isArray(generatedParent) ? generatedGrandParent : generatedParent;
		const duplicatedEditorID: number = section[parentKey][duplicatedItemIndex].editorID;

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

		return duplicatedEditorID;
	};
}

function copyModule(editorID: number[]): (dispatch: Dispatch, getState: () => IRootState) => boolean | number {
	return (dispatch, getState) => {
		const { sections, editorContent } = getStateValues(getState);
		const modulesToCopy: Record<string, unknown>[] = [];

		const content = sections.length ? sections : editorContent.template;
		editorID.forEach((id) => {
			const { element: originalElement } = findByEditorID(content, 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 {
			sites: { currentSiteInfo },
		}: IRootState = getState();

		const { sections, editorContent } = getStateValues(getState);

		const updatedSections: any = [...sections];
		const { element: originalElement } = findByEditorID(updatedSections, editorID);
		const itemsArr = sections.length ? originalElement[key] : editorContent.template[key];

		let error: INotification | undefined;

		for (const element of modulesToPaste) {
			const { isMaxModules, errorMessage } = checkMaxModules(editorContent, element.component);
			if (isMaxModules && errorMessage) {
				error = {
					type: "error",
					text: errorMessage,
				};
			}

			const validatedModuleCopy: any = deepClone(element);

			if (element.hasDistributorData) {
				const { mode, fixed } = element.data;
				const isManualMode = mode === "manual" && !!fixed?.length;
				if (isManualMode) {
					const response = await structuredData.getDataContentBulk(fixed, currentSiteInfo?.id);
					if (isReqOk(response.status)) {
						const validDataIds: number[] = [];
						response.data.items.forEach((data: IStructuredDataContent) => {
							data.relatedSite && validDataIds.push(data.id);
						});
						const hasInvalidData = validDataIds.length < fixed.length;
						validatedModuleCopy.data.fixed = validDataIds;
						if (hasInvalidData) {
							error = {
								type: "warning",
								text: "Some distributor data were not copied because they are not imported into this site",
							};
						}
					}
				}
			}

			itemsArr.push(validatedModuleCopy);
		}

		const updatedPageContent = {
			...editorContent,
		};

		generatePageContent(updatedPageContent)(dispatch, getState);

		const { sections: generatedSections, editorContent: generatedEditorContent } = getStateValues(getState);
		const generatedContent = generatedSections.length ? generatedSections : generatedEditorContent;
		const { element: generatedElement } = findByEditorID(generatedContent, editorID);
		const pastedEditorID = generatedElement[key][itemsArr.length - 1].editorID;

		localStorage.setItem("selectedID", `${pastedEditorID}`);
		return { error };
	};
}

function overwriteHeaderConfig(params: IFieldProps): (dispatch: Dispatch, getState: () => IRootState) => void {
	return (dispatch, getState) => {
		const {
			pageEditor: { editorContent: content },
		} = getState();
		const { id, key, value } = params;

		const updatedContent = deepClone(content);
		const { headerConfig } = updatedContent.editorContent;

		if (headerConfig[id]) {
			headerConfig[id][key] = value;
		} else {
			headerConfig[id] = { [key]: value };
		}

		dispatch(setEditorContent({ ...updatedContent }));
	};
}

function generatePageContent(editorContent: IPage): (dispatch: Dispatch, getState: any) => void {
	return (dispatch, getState) => {
		const {
			pageEditor: { selectedEditorID, isNewTranslation, template },
			navigation: { currentDefaultsContent },
			dataPacks: { configFormData },
		} = getState();

		const { header, footer, isGlobal } = editorContent;
		const { defaultHeader, defaultFooter } = configFormData?.templates?.[template] || {};

		const headerID =
			header === null || header === undefined ? defaultHeader : typeof header === "object" ? header.id : header;
		const footerID =
			footer === null || footer === undefined ? defaultFooter : typeof footer === "object" ? footer.id : footer;

		const { header: pageHeader, footer: pageFooter } = getPageNavigation(
			headerID,
			footerID,
			currentDefaultsContent,
			isGlobal,
		);

		const { pageContent } = generateEditorIDs({ ...editorContent, header: pageHeader, footer: pageFooter });
		const { element: selectedContent, parent: selectedParent } = findByEditorID({ pageContent }, selectedEditorID);
		const { component } = selectedContent;
		const schema = getSchema(component);
		if (!schema) return;
		dispatch(setSchema(schema));
		dispatch(setEditorContent(pageContent));
		dispatch(updateBreadcrumb(selectedEditorID));
		dispatch(setSelectedPageContent(selectedContent));
		dispatch(setSelectedParent(selectedParent));
		dispatch(setIsLoading(false));
		if (isNewTranslation) setTranslatedParent()(dispatch, getState);
	};
}

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

		const clonedContent = deepClone(editorContent);
		const clonedSelected = deepClone(selectedContent);

		if (clonedSelected.component === "FormPage" && !Array.isArray(key)) {
			protectFormKeys(clonedContent, key, selectedEditorID);
		}

		const updatedSelectedContent = updateByEditorID(clonedSelected, selectedEditorID, key, value);
		let updatedEditorContent = updateByEditorID(clonedContent, selectedEditorID, key, value);

		dispatch(setSelectedPageContent(updatedSelectedContent));
		dispatch(setEditorContent(updatedEditorContent));

		if (lastTimeout) {
			clearTimeout(lastTimeout);
		}

		const timeoutId = setTimeout(async () => {
			updatedEditorContent = await evaluateComputedFields(updatedEditorContent, "refresh");
			generatePageContent(updatedEditorContent)(dispatch, getState);
		}, 1500);

		dispatch(setLastTimeout(timeoutId));

		const isNavigation = Array.isArray(key)
			? key.some((k) => ["header", "footer"].includes(k))
			: ["header", "footer"].includes(key);

		const isObjectValue = Array.isArray(value)
			? value.some((v) => v !== null && typeof v === "object")
			: value !== null && typeof value === "object";

		if (isNavigation || isObjectValue) {
			generatePageContent(updatedEditorContent)(dispatch, getState);
		}
	};
}

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

		const iframe = document.querySelector("iframe");
		iframe?.contentWindow?.postMessage(
			{
				type: "selected-content",
				message: editorID,
			},
			"*",
		);

		if (editorID > 0) {
			localStorage.setItem("selectedID", `${editorID}`);
		}

		dispatch(setIsLoading(true));
		const {
			pageEditor: { editorContent, tab },
			navigation: { header },
		} = getState();
		/*
      Footer cannot be overwritten - we ignore it in findByEditorID
    */

		const { element: selectedContent, parent: selectedParent } = findByEditorID({ header, editorContent }, editorID);

		const { component } = selectedContent;
		const defaultSchema = getDefaultSchema(component);

		// if schema default has new keys
		const isUpdatedComponent = Object.keys(defaultSchema).length + 2 !== Object.keys(selectedContent).length;

		isUpdatedComponent && updateComponent(selectedContent, defaultSchema);

		const schema = getSchema(component);
		if (!schema) return;
		const defaultTab = "content";
		dispatch(setTab(tab || defaultTab));
		dispatch(setSchema(schema));
		dispatch(setSelectedEditorID(editorID));
		dispatch(updateBreadcrumb(editorID));
		dispatch(setSelectedPageContent({ ...selectedContent }));
		dispatch(setSelectedParent(selectedParent));
		dispatch(setIsLoading(false));
	};
}

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

		const isFiltered = true;
		const newBreadcrumb = getNewBreadcrumb(editorContent, editorID, isFiltered);

		const { header, footer } = editorContent;
		const navigationIds = [header?.editorID, footer?.editorID];
		if (navigationIds.includes(editorID)) {
			const rootBreadcrumb = { editorID: 0, displayName: "Page", component: "Page" };
			const rootExists = newBreadcrumb.some(
				(breadcrumb: { editorID: number }) => breadcrumb.editorID === rootBreadcrumb.editorID,
			);
			!rootExists && newBreadcrumb.unshift(rootBreadcrumb);
		}

		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 resetPageEditor(): (dispatch: Dispatch) => Promise<void> {
	return async (dispatch) => {
		try {
			dispatch(resetPageEditorStore());
			localStorage.removeItem("selectedID");
		} catch (e) {
			console.log("Error", e); //TODO
		}
	};
}

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

			const newContent = moveModule({
				elementID,
				content,
				selectedContent,
				newIndex,
				page: editorContent,
				key,
			});

			generatePageContent(newContent)(dispatch, getState);
		} catch (e) {
			console.log(e);
		}
	};
}

function getTemplateConfig(template: string): (dispatch: Dispatch, getState: () => IRootState) => Promise<void> {
	return async (dispatch, getState) => {
		try {
			const {
				sites: { currentSiteInfo },
			} = getState();
			const currentSiteID = currentSiteInfo?.id || null;
			const responseActions = {
				handleSuccess: (data: any) => dispatch(setTemplateConfig(data)),
				handleError: () => console.log("Error en getTemplateConfig"),
			};

			const callback = async () => pages.getTemplateConfig(currentSiteID, template);

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

function validatePage(publish?: boolean): (dispatch: Dispatch, getState: () => IRootState) => Promise<boolean> {
	return async (dispatch, getState) => {
		try {
			const { editorContent } = getStateValues(getState);
			const { component } = editorContent;
			const isGlobalPage = component === "GlobalPage";
			const content = deepClone(editorContent);

			const {
				dataPacks: { modules, templates },
				pageEditor,
			} = getState();

			const page = getPageData(getState, false);
			const { values } = parseData(page, false);

			let errors: IErrorItem[] = [];

			const responseActions = {
				handleSuccess: (data: any) => {
					const apiErrors: IErrorItem[] = parseValidationErrors(data, content);
					errors = [...errors, ...apiErrors];
				},
				handleError: () => console.log("Error en page check"),
			};

			const callback = async () => pages.pageCheck(values);

			await handleRequest(callback, responseActions, [])(dispatch);

			const fieldErrors = findFieldsErrors(content);

			errors = [...errors, ...fieldErrors];
			let packagesActivationErrors: any;
			if (!isGlobalPage) {
				packagesActivationErrors = findPackagesActivationErrors(pageEditor, modules, templates);
			}
			errors = packagesActivationErrors ? [...errors, packagesActivationErrors] : errors;

			let warnings: IErrorItem[] = [];
			const h1Warning = checkH1content();
			warnings = h1Warning ? [...warnings, h1Warning] : warnings;

			const allErrors = [...errors, ...warnings];

			dispatch(setErrors(allErrors));
			if (!allErrors.length) {
				!publish && dispatch(setValidated(true));
				return true;
			} else if (publish && !errors.length) {
				return true;
			} else {
				dispatch(setValidated(false));
				return false;
			}
		} catch (e) {
			console.log(e);
			return false;
		}
	};
}

function deleteError(error: IErrorItem): (dispatch: Dispatch, getState: () => IRootState) => void {
	return async (dispatch, getState) => {
		try {
			const { errors } = getState().pageEditor;
			const updatedErrors = errors.filter(
				(err: IErrorItem) => !(error.editorID === err.editorID && error.key === err.key),
			);

			dispatch(setErrors(updatedErrors));
		} catch (e) {
			console.log(e);
		}
	};
}

function getGlobalFromLocalPage(): (dispatch: Dispatch, getState: any) => Promise<void> {
	return async (dispatch, getState) => {
		try {
			dispatch(setIsLoading(true));

			const {
				pageEditor: { editorContent, currentPageID },
			} = getState();

			const { originalGlobalPage, structuredData } = editorContent;

			dispatch(setSitePageID(currentPageID));
			dispatch(setCurrentPageID(originalGlobalPage));
			structuredDataActions.setSelectedStructuredData(structuredData, "global")(dispatch, getState);
			usersActions.getUserCurrentPermissions()(dispatch, getState);
			dispatch(setIsLoading(false));
		} catch (e) {
			console.log(e);
		}
	};
}

function sendPagePing(pageID: number): (dispatch: Dispatch, getState: () => IRootState) => Promise<boolean> {
	return async (dispatch, getState) => {
		try {
			const {
				pageEditor: { userEditing },
			} = getState();

			const responseActions = {
				handleSuccess: () => null,
				handleError: (response: any) => {
					const { data } = response;
					if (data.code === 400 && (!userEditing || userEditing.id !== data.user.id)) {
						dispatch(setUserEditing(data.user));
					}
				},
			};

			const callback = async () => pages.sendPagePing(pageID);

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

function discardDraft(): (dispatch: Dispatch, getState: () => IRootState) => Promise<void> {
	return async (dispatch, getState) => {
		try {
			const page = getPageData(getState, true);
			const responseActions = {
				handleSuccess: () => {
					dispatch(setCurrentPageID(page.values.draftFromPage));
					getPage(page.values.draftFromPage)(dispatch, getState);
				},
				handleError: (response: any) => appActions.handleError(response)(dispatch),
			};

			const callback = async () => pages.deletePage(page.id);

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

function removeNavigationFromPage(key: string): (dispatch: Dispatch, getState: () => IRootState) => void {
	return (dispatch, getState) => {
		const {
			pageEditor: { editorContent },
		} = getState();
		const updatedEditorContent = { ...editorContent, [key]: 0 };
		setSelectedContent(0)(dispatch, getState);
		dispatch(setEditorContent(updatedEditorContent));
	};
}

function restorePageNavigation(key: string): (dispatch: Dispatch, getState: () => IRootState) => void {
	return (dispatch, getState) => {
		const {
			pageEditor: { editorContent },
			navigation: { currentDefaultsContent },
		} = getState();

		const { header, footer } = editorContent;

		const navigation = getDefaultPageNavigation(currentDefaultsContent, key);

		const pageHeader = key === "header" ? navigation : header;
		const pageFooter = key === "footer" ? navigation : footer;

		const updatedEditorContent = { ...editorContent, [key]: navigation };

		const { pageContent } = generateEditorIDs({
			...updatedEditorContent,
			header: pageHeader,
			footer: pageFooter,
		});

		dispatch(setEditorContent(pageContent));
	};
}

function getPageSummary(): (dispatch: Dispatch, getState: () => IRootState) => Promise<boolean> {
	return async (dispatch, getState) => {
		try {
			const {
				pageEditor: { editorContent },
			} = getState();

			const responseActions = {
				handleSuccess: (data: { summary: string; keywords: string[] }) => {
					const content = deepClone(editorContent);
					content.metaDescription = data.summary;
					content.metaKeywords = data.keywords;
					generatePageContent(content)(dispatch, getState);
				},
				handleError: () => console.log("Error en GetPageSummary"),
			};

			const callback = async () => pages.getPageSummary(editorContent);

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

function getPageTranslation(langID: number): (dispatch: Dispatch, getState: () => IRootState) => Promise<boolean> {
	return async (dispatch, getState) => {
		try {
			const {
				pageEditor: { editorContent },
			} = getState();

			const responseActions = {
				handleSuccess: (data: IPage) => {
					data.canBeTranslated = false;
					generatePageContent(data)(dispatch, getState);
					dispatch(setCurrentPageName(data.title));
					dispatch(setIsIATranslated(true));
				},
				handleError: () => console.log("Error en GetPageTranslation"),
			};

			const callback = async () => pages.getPageTranslation(editorContent, langID);

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

function setIsTranslated(isTranslated: boolean): (dispatch: Dispatch) => Promise<void> {
	return async (dispatch) => {
		try {
			dispatch(setIsIATranslated(isTranslated));
		} catch (e) {
			console.log("Error", e);
		}
	};
}

function schedulePublication(
	date: string | null,
	isDraft: boolean,
): (dispatch: Dispatch, getState: () => IRootState) => Promise<boolean> {
	return async (dispatch, getState) => {
		try {
			const {
				pageEditor: { editorContent },
			} = getState();

			const status = date ? pageStatus.SCHEDULED : isDraft ? pageStatus.MODIFIED : pageStatus.OFFLINE;
			const updatedEditorContent = { ...editorContent, publicationScheduled: date };

			dispatch(setEditorContent(updatedEditorContent));
			return await savePage({ createDraft: false, publishPage: { status } })(dispatch, getState);
		} catch (e) {
			console.log("Error", e);
			return false;
		}
	};
}

export {
	setEditorContent,
	setTemplate,
	setBreadcrumb,
	setSchema,
	setSelectedPageContent,
	setSelectedEditorID,
	setTab,
	setCurrentPageID,
	setCurrentPageStatus,
	setCurrentPageName,
	setCurrentPageLanguages,
	setIsNewTranslation,
	createNewTranslation,
	getPageBreadcrumb,
	getPage,
	savePage,
	deletePage,
	updatePageStatus,
	getPageLanguages,
	addTemplate,
	addComponent,
	addModule,
	deleteModule,
	replaceModule,
	duplicateModule,
	replaceElementsInCollection,
	overwriteHeaderConfig,
	generatePageContent,
	updateEditorContent,
	setSelectedContent,
	setSelectedTab,
	resetPageEditor,
	moveElement,
	deleteBulk,
	duplicatePage,
	validatePage,
	deleteError,
	restorePage,
	getGlobalFromLocalPage,
	setSitePageID,
	sendPagePing,
	discardDraft,
	getTemplatePage,
	copyModule,
	pasteModule,
	removeNavigationFromPage,
	restorePageNavigation,
	getPageSummary,
	getPageTranslation,
	setIsTranslated,
	schedulePublication,
	setScrollEditorID,
	addComponentInArray,
	replaceModuleInArray,
};
