import type { Dispatch } from "redux";

import { filterThemeDatapacks, handleRequest, sortBy } from "@ax/helpers";
import { dataPack } from "@ax/api";
import { appActions } from "@ax/containers/App";
import { structuredDataActions } from "@ax/containers/StructuredData";

import {
	SET_ACTIVATED,
	SET_SELECTED,
	SET_CONFIG_FORM_DATA,
	SET_AVAILABLE,
	SET_CATEGORIES,
	SET_MODULES,
	SET_TEMPLATES,
	ERROR_CODE,
} from "./constants";

import type {
	ISetActivated,
	ISetSelected,
	ISetConfigFormData,
	ISetAvailable,
	ISetCategories,
	ISetModules,
	ISetTemplates,
} from "./interfaces";
import type { IDataPack, IDataPackCategory, IRootState } from "@ax/types";
import { dataPacksInitialState } from "./reducer";

function setActivated(activated: any): ISetActivated {
	return { type: SET_ACTIVATED, payload: { activated } };
}

function setSelected(selected: any | null): ISetSelected {
	return { type: SET_SELECTED, payload: { selected } };
}

function setAvailable(available: IDataPack[]): ISetAvailable {
	return { type: SET_AVAILABLE, payload: { available } };
}

function setCategories(categories: IDataPackCategory[]): ISetCategories {
	return { type: SET_CATEGORIES, payload: { categories } };
}

function setConfigFormData(configFormData: any | null): ISetConfigFormData {
	return { type: SET_CONFIG_FORM_DATA, payload: { configFormData } };
}

function setModules(modules: any): ISetModules {
	return { type: SET_MODULES, payload: { modules } };
}

function setTemplates(templates: any[]): ISetTemplates {
	return { type: SET_TEMPLATES, payload: { templates } };
}

function getAllDataPacks(queryParams?: string): (dispatch: Dispatch) => Promise<void> {
	return async (dispatch) => {
		try {
			const getDataPacks = (data: any) => {
				const orderedData = data.sort(sortBy("title", false));
				dispatch(setActivated(orderedData));
			};

			const responseActions = {
				handleSuccess: (data: any) => getDataPacks(data.items),
				handleError: () => console.log("Error en getAllDataPacks"),
			};

			const callback = async () => dataPack.getDataPacks(queryParams);

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

function getSiteDataPacks(queryParams?: string): (dispatch: Dispatch, getState: () => IRootState) => Promise<void> {
	return async (dispatch, getState) => {
		try {
			const {
				sites: { currentSiteInfo },
			} = getState();
			const currentSiteID = currentSiteInfo!.id;

			const getDataPacks = (data: any) => {
				const orderedData = data.sort(sortBy("title", false));
				dispatch(setActivated(orderedData));
				getSiteModules()(dispatch, getState);
				getSiteTemplates()(dispatch, getState);
			};

			const responseActions = {
				handleSuccess: (data: any) => getDataPacks(data.items),
				handleError: () => console.log("Error en getSiteDataPacks"),
			};

			const callback = async () => dataPack.getSiteDataPacks(currentSiteID, queryParams);

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

function getSiteDataPack(packID: string): (dispatch: Dispatch, getState: () => IRootState) => Promise<void> {
	return async (dispatch, getState) => {
		try {
			const {
				sites: { currentSiteInfo },
			} = getState();
			const currentSiteID = currentSiteInfo!.id;

			const setDataPack = (pack: IDataPack) => {
				dispatch(setSelected(pack));
				const config = pack?.config;
				const isEmptyConfig = !config || Object.keys(config).length === 0;

				if (isEmptyConfig) {
					resetForm()(dispatch);
				} else {
					dispatch(setConfigFormData(config));
				}
			};

			const responseActions = {
				handleSuccess: (data: any) => setDataPack(data),
				handleError: () => console.log("Error en getDataPack"),
			};

			const callback = async () => await dataPack.getSiteDataPack(currentSiteID, packID);

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

function getActivatedDataPack(
	packID: string | null,
): (dispatch: Dispatch, getState: () => IRootState) => Promise<void> {
	return async (dispatch, getState) => {
		try {
			const {
				dataPacks: { activated },
			} = getState();
			const selectedDataPack = packID ? activated?.find((pack: any) => pack.id === packID) : null;

			dispatch(setSelected(selectedDataPack));
			const config = selectedDataPack?.config;
			const isEmptyConfig = !config || Object.keys(config).length === 0;

			if (isEmptyConfig) {
				resetForm()(dispatch);
			} else {
				dispatch(setConfigFormData(config));
			}
		} catch (e) {
			console.log(e); // TODO: capturar error bien
		}
	};
}

function deleteSiteDataPack(
	dataPackID: string,
	errorAction: any,
	force?: boolean,
): (dispatch: Dispatch, getState: () => IRootState) => Promise<boolean> {
	return async (dispatch, getState) => {
		try {
			const {
				sites: { currentSiteInfo },
			} = getState();
			const currentSiteID = currentSiteInfo!.id;
			const getActivatedQuery = "?status=activated";

			const handleSuccess = () => {
				getSiteDataPacks(getActivatedQuery)(dispatch, getState);
				structuredDataActions.getStructuredData(null, currentSiteID)(dispatch, getState);
			};

			const handleError = (error: any) => {
				const { status } = error;

				status === ERROR_CODE.isBeingUsed ? errorAction() : appActions.handleError(error)(dispatch);
			};

			const responseActions = {
				handleSuccess,
				handleError,
			};

			const callback = async () => dataPack.deleteSiteDataPack(currentSiteID, dataPackID, force);

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

function updateDataPack(dataPackID: string): (dispatch: Dispatch, getState: () => IRootState) => Promise<boolean> {
	return async (dispatch, getState) => {
		try {
			const {
				sites: { currentSiteInfo },
				dataPacks: { configFormData },
			} = getState();
			const currentSiteID = currentSiteInfo!.id;

			const setUpdatedPack = (selectedPack: any) => {
				const config = selectedPack?.config;
				const getActivatedQuery = "?status=activated";

				dispatch(setSelected(selectedPack));
				dispatch(setConfigFormData(config));
				getSiteDataPacks(getActivatedQuery)(dispatch, getState);
			};

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

			delete configFormData.defaultHeader;
			delete configFormData.defaultFooter;

			const callback = async () => dataPack.updateDataPack(currentSiteID, dataPackID, configFormData);

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

function updateFormValue(valueObj: any): (dispatch: Dispatch, getState: () => IRootState) => void {
	return (dispatch, getState) => {
		const {
			dataPacks: { configFormData },
		} = getState();
		const updatedForm = { ...configFormData, ...valueObj };
		dispatch(setConfigFormData(updatedForm));
	};
}

function resetForm(): (dispatch: Dispatch) => void {
	return (dispatch) => {
		const { configFormData } = dataPacksInitialState;
		dispatch(setConfigFormData({ ...configFormData }));
	};
}

function getAvailableSiteDataPacks(
	queryParams: string | null,
	loading = true,
): (dispatch: Dispatch, getState: () => IRootState) => Promise<void> {
	return async (dispatch, getState) => {
		try {
			const {
				sites: { currentSiteInfo, themeElements },
			}: IRootState = getState();

			const query = queryParams ? `?status=deactivated${queryParams}` : `?status=deactivated`;

			const handleSuccess = (data: any) => {
				const { items } = data;
				const filteredDatapacks = filterThemeDatapacks(themeElements, items);
				dispatch(setAvailable(filteredDatapacks));
			};

			const responseActions = {
				handleSuccess,
				handleError: () => console.log("Error en getAvailableSiteDataPacks"),
			};

			const callback = async () => currentSiteInfo && dataPack.getSiteDataPack(currentSiteInfo.id, query);

			const setLoading = loading ? [appActions.setIsLoading] : [];

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

function addSiteDataPack(
	dataPackID: string,
	fromConfig?: boolean,
): (dispatch: Dispatch, getState: () => IRootState) => Promise<void> {
	return async (dispatch, getState) => {
		try {
			const {
				sites: { currentSiteInfo },
			} = getState();
			const currentSiteID = currentSiteInfo!.id;
			const getActivatedQuery = "?status=activated";

			const getPacks = () => {
				getSiteDataPacks(getActivatedQuery)(dispatch, getState);
				fromConfig && getSiteDataPack(dataPackID)(dispatch, getState);
				structuredDataActions.getStructuredData(null, currentSiteID)(dispatch, getState);
			};

			const responseActions = {
				handleSuccess: () => getPacks(),
				handleError: () => console.log("Error en addSiteDataPack"),
			};

			const callback = async () => dataPack.addDataPack(currentSiteID, dataPackID);

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

function getDataPacksCategories(): (dispatch: Dispatch) => Promise<void> {
	return async (dispatch) => {
		try {
			const responseActions = {
				handleSuccess: (data: any) => dispatch(setCategories(data)),
				handleError: () => console.log("Error en getDataPacksCategories"),
			};

			const callback = async () => dataPack.getCategories();

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

function getSiteModules(): (dispatch: Dispatch, getState: () => IRootState) => Promise<void> {
	return async (dispatch, getState) => {
		try {
			const {
				sites: { currentSiteInfo },
			} = getState();
			const currentSiteID = currentSiteInfo!.id;

			const modulesIds: string[] = [];
			const setModulesID = (data: any) => {
				data.forEach((module: any) => {
					modulesIds.push(module.id);
				});
				dispatch(setModules(modulesIds));
			};
			const responseActions = {
				handleSuccess: (data: any) => setModulesID(data),
				handleError: () => console.log("Error en getDataPack"),
			};

			const callback = async () => dataPack.getSiteModules(currentSiteID);

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

function getSiteTemplates(): (dispatch: Dispatch, getState: () => IRootState) => Promise<void> {
	return async (dispatch, getState) => {
		try {
			const {
				sites: { currentSiteInfo },
			} = getState();
			const currentSiteID = currentSiteInfo!.id;

			const responseActions = {
				handleSuccess: (data: any) => {
					dispatch(setTemplates(data));
				},
				handleError: () => console.log("Error en getSiteTemplates"),
			};

			const callback = async () => dataPack.getSiteTemplates(currentSiteID);

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

function getSiteDataPackbyTemplate(
	templateType: string,
): (dispatch: Dispatch, getState: () => IRootState) => Promise<void> {
	return async (dispatch, getState) => {
		try {
			const {
				sites: { currentSiteInfo },
			} = getState();
			const currentSiteID = currentSiteInfo!.id;
			const getQuery = "?status=deactivated";

			const setDataPack = (data: IDataPack[]) => {
				const pack = data.find((pack: IDataPack) => pack.templates.find((temp: any) => temp.id === templateType));
				if (pack) {
					dispatch(setSelected(pack));
					const config = pack && JSON.parse(pack.config);
					dispatch(setConfigFormData(config));
				}
			};

			const responseActions = {
				handleSuccess: (data: any) => setDataPack(data.items),
				handleError: () => console.log("Error en getSiteDataPackbyTemplate"),
			};

			const callback = async () => dataPack.getSiteDataPacks(currentSiteID, getQuery);

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

function getSiteDataPackbyModule(module: string): (dispatch: Dispatch, getState: () => IRootState) => Promise<void> {
	return async (dispatch, getState) => {
		try {
			const {
				sites: { currentSiteInfo },
			} = getState();
			const currentSiteID = currentSiteInfo!.id;
			const getQuery = "?status=deactivated";

			const setDataPack = (data: IDataPack[]) => {
				const pack = data.find((pack: IDataPack) => pack.modules.find((mod: any) => mod.id === module));
				if (pack) {
					dispatch(setSelected(pack));
					const config = pack && JSON.parse(pack.config);
					dispatch(setConfigFormData(config));
				}
			};

			const responseActions = {
				handleSuccess: (data: any) => setDataPack(data.items),
				handleError: () => console.log("Error en getSiteDataPackbyModule"),
			};

			const callback = async () => dataPack.getSiteDataPacks(currentSiteID, getQuery);

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

export {
	getSiteDataPacks,
	setActivated,
	setSelected,
	updateFormValue,
	updateDataPack,
	deleteSiteDataPack,
	getAvailableSiteDataPacks,
	addSiteDataPack,
	getDataPacksCategories,
	getActivatedDataPack,
	getSiteDataPack,
	getSiteModules,
	getSiteTemplates,
	getSiteDataPackbyTemplate,
	getSiteDataPackbyModule,
	getAllDataPacks,
};
