import { Dispatch } from "redux";
import { IFile, IFilesFolder, IFolder, IFolderTree, IGetFolderParams } from "@ax/types";
import { files } from "@ax/api";
import { handleRequest, isReqOk, getNewBreadcrumb } from "@ax/helpers";
import { appActions } from "@ax/containers/App";
import {
	SET_BREADCRUMB,
	SET_CURRENT_FOLDER,
	SET_CURRENT_FOLDER_CONTENT,
	SET_DISPLAY_MODE,
	SET_FOLDERS_TREE,
	SET_IS_UPLOADING,
	SET_SELECTED_TAB,
	SET_UPLOAD_ERROR,
	SET_UPLOAD_SUCCESS,
} from "./constants";
import {
	ISetBreadcrumb,
	ISetCurrentFolder,
	ISetCurrentFolderContent,
	ISetDisplayMode,
	ISetFoldersTree,
	ISetIsUploading,
	ISetSelectedTab,
	ISetUploadError,
	ISetUploadSuccess,
} from "./interfaces";

function setCurrentFolderContent(currentFolderContent: IFilesFolder | null): ISetCurrentFolderContent {
	return { type: SET_CURRENT_FOLDER_CONTENT, payload: { currentFolderContent } };
}

function setCurrentFolder(currentFolderID: number | null): ISetCurrentFolder {
	return { type: SET_CURRENT_FOLDER, payload: { currentFolderID } };
}

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

function setFoldersTree(foldersTree: IFolderTree[]): ISetFoldersTree {
	return { type: SET_FOLDERS_TREE, payload: { foldersTree } };
}

function setIsUploading(isUploading: boolean): ISetIsUploading {
	return { type: SET_IS_UPLOADING, payload: { isUploading } };
}

function setUploadSuccess(isSuccess: boolean): ISetUploadSuccess {
	return { type: SET_UPLOAD_SUCCESS, payload: { isUploading: false, isSuccess } };
}

function setUploadError(isError: boolean, errorMsg = ""): ISetUploadError {
	return { type: SET_UPLOAD_ERROR, payload: { isUploading: false, isError, errorMsg } };
}

function setDisplayMode(displayMode: "grid" | "list"): ISetDisplayMode {
	return { type: SET_DISPLAY_MODE, payload: { displayMode } };
}

function setSelectedTab(selectedTab: "site" | "global"): ISetSelectedTab {
	return { type: SET_SELECTED_TAB, payload: { selectedTab } };
}

function getFolderContent(params: IGetFolderParams): (dispatch: Dispatch, getState: any) => Promise<void> {
	return async (dispatch, getState) => {
		try {
			const { folderID, loading } = params;
			const responseActions = {
				handleSuccess: (data: IFilesFolder) => {
					dispatch(setCurrentFolderContent(data));
					updateBreadcrumb(folderID)(dispatch, getState);
					dispatch(setUploadSuccess(false));
				},
				handleError: (response: any) => appActions.handleError(response)(dispatch),
			};
			const callback = async () => files.getFolderContent(params);

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

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

function getFoldersTree(siteID: number | "global"): (dispatch: Dispatch) => Promise<void> {
	return async (dispatch) => {
		try {
			const responseActions = {
				handleSuccess: (data: IFolderTree[]) => dispatch(setFoldersTree(data)),
				handleError: (response: any) => appActions.handleError(response)(dispatch),
			};
			const callback = async () => files.getFoldersTree(siteID);

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

function updateCurrentFolder(currentFolderID: number | null): (dispatch: Dispatch) => void {
	return async (dispatch) => {
		try {
			dispatch(setCurrentFolder(currentFolderID));
		} catch (e) {
			console.log("Error", e);
		}
	};
}

function updateBreadcrumb(folderID: number | null): (dispatch: Dispatch, getState: any) => void {
	return (dispatch, getState) => {
		const {
			fileDrive: { foldersTree },
		} = getState();

		const newBreadcrumb = getNewBreadcrumb(foldersTree, folderID);
		dispatch(setBreadcrumb(newBreadcrumb));
	};
}

function createNewFolder(data: {
	folderName: string;
	site: number | "global";
	parentId?: number;
}): (dispatch: Dispatch, getState: any) => Promise<boolean> {
	return async (dispatch, getState) => {
		try {
			const responseActions = {
				handleSuccess: async () => {
					await getFoldersTree(data.site)(dispatch);
					await getFolderContent({ siteID: data.site, folderID: data.parentId || null })(dispatch, getState);
				},
				handleError: (response: any) => appActions.handleError(response)(dispatch),
			};
			const callback = async () => files.createFolder(data);

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

function updateFolder(
	folderID: number,
	data: { parentId: number; folderName: string },
	siteID: number | "global",
): (dispatch: Dispatch, getState: any) => Promise<boolean> {
	return async (dispatch, getState) => {
		try {
			const {
				fileDrive: { currentFolderID },
			} = getState();

			const responseActions = {
				handleSuccess: async () => {
					await getFolderContent({ siteID, folderID: currentFolderID })(dispatch, getState);
					await getFoldersTree(siteID)(dispatch);
				},
				handleError: (response: any) => appActions.handleError(response)(dispatch),
			};
			const callback = async () => files.updateFolder(folderID, data);

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

function deleteFolder(folder: IFolder): (dispatch: Dispatch, getState: any) => Promise<boolean> {
	return async (dispatch, getState) => {
		try {
			const responseActions = {
				handleSuccess: async () => {
					await getFoldersTree(folder.site || "global")(dispatch);
					await getFolderContent({ siteID: folder.site || "global", folderID: folder.parentId })(dispatch, getState);
				},
				handleError: (response: any) => appActions.handleError(response)(dispatch),
			};
			const callback = async () => files.deleteFolder(folder.id);

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

function uploadFile(
	docFiles: File | File[],
	folderID: number | null,
	siteID: number | "global",
	setProgress?: (progress: number) => void,
): (dispatch: Dispatch) => Promise<IFile | null> {
	return async (dispatch) => {
		try {
			dispatch(setIsUploading(true));

			const filesArr = Array.isArray(docFiles) ? docFiles : [docFiles];
			const uploadPromises = filesArr.map(async (file: File) => {
				const form = new FormData();
				form.append("file", file);
				form.append("site", JSON.stringify(siteID));
				folderID && form.append("folder", JSON.stringify(folderID));
				const uploadedFile = await files.uploadFile(form, setProgress);
				return { ...uploadedFile, file };
			});
			const results = await Promise.all(uploadPromises);
			const lastUploadedFile = results.reduce((lastResult, result) =>
				result.data.id > lastResult.data.id ? result : lastResult,
			);

			if (isReqOk(lastUploadedFile?.status)) {
				dispatch(setIsUploading(false));
				dispatch(setUploadSuccess(true));
				return lastUploadedFile.data;
			} else {
				dispatch(setUploadError(true, "Error uploading file"));
				return null;
			}
		} catch (e) {
			console.log(e);
			return null;
		}
	};
}

function uploadError(error: boolean, msg?: string): (dispatch: Dispatch) => Promise<void> {
	return async (dispatch) => {
		dispatch(setUploadError(error, msg));
	};
}

function updateFile(
	fileID: number,
	data: { title: string; alt: string; tags: string[] },
	siteID: number | "global",
	loading = true,
): (dispatch: Dispatch, getState: any) => Promise<boolean> {
	return async (dispatch, getState) => {
		try {
			const {
				fileDrive: { currentFolderID },
			} = getState();

			const responseActions = {
				handleSuccess: async () => {
					await getFolderContent({ siteID, folderID: currentFolderID, loading })(dispatch, getState);
				},
				handleError: (response: any) => appActions.handleError(response)(dispatch),
			};
			const callback = async () => files.updateFile(fileID, data);

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

function deleteFile(
	fileID: number | number[],
	siteID: number | "global",
): (dispatch: Dispatch, getState: any) => Promise<boolean> {
	return async (dispatch, getState) => {
		try {
			const {
				fileDrive: { currentFolderID },
			} = getState();

			const responseActions = {
				handleSuccess: async () => {
					await getFolderContent({ siteID, folderID: currentFolderID })(dispatch, getState);
				},
				handleError: (response: any) => appActions.handleError(response)(dispatch),
			};
			const callback = async () => (Array.isArray(fileID) ? files.deleteFilesBulk(fileID) : files.deleteFile(fileID));

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

function moveFile(
	fileID: number | number[],
	folderID: number,
	siteID: number | "global",
): (dispatch: Dispatch, getState: any) => Promise<boolean> {
	return async (dispatch, getState) => {
		try {
			const {
				fileDrive: { currentFolderID },
			} = getState();

			const responseActions = {
				handleSuccess: async () => {
					await getFolderContent({ siteID, folderID: currentFolderID })(dispatch, getState);
				},
				handleError: (response: any) => appActions.handleError(response)(dispatch),
			};
			const callback = async () =>
				Array.isArray(fileID) ? files.moveFilesBulk(fileID, folderID) : files.moveFile(fileID, folderID);

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

function updateDisplayMode(displayMode: "grid" | "list"): (dispatch: Dispatch) => Promise<void> {
	return async (dispatch) => {
		dispatch(setDisplayMode(displayMode));
	};
}

function updateTab(tab: "site" | "global"): (dispatch: Dispatch) => Promise<void> {
	return async (dispatch) => {
		dispatch(setSelectedTab(tab));
	};
}

function replaceFile(
	docFile: File,
	fileID: number,
	keepUrl: boolean,
	siteID: number | "global",
	setProgress?: (progress: number) => void,
): (dispatch: Dispatch, getState: any) => Promise<IFile | null> {
	return async (dispatch, getState) => {
		try {
			dispatch(setIsUploading(true));

			const {
				fileDrive: { currentFolderID },
			} = getState();

			const form = new FormData();
			form.append("file", docFile);

			const result = await files.replaceFile(form, fileID, keepUrl, setProgress);
			if (isReqOk(result.status)) {
				dispatch(setIsUploading(false));
				dispatch(setUploadSuccess(true));
				await getFolderContent({ siteID, folderID: currentFolderID })(dispatch, getState);
				return result.data;
			} else {
				dispatch(setUploadError(true, "Error uploading file"));
				return null;
			}
		} catch (e) {
			console.log(e);
			return null;
		}
	};
}

function downloadFiles(
	fileID: number | number[],
	zip: boolean,
	fileName?: string,
): (dispatch: Dispatch) => Promise<void> {
	return async (dispatch) => {
		try {
			const responseActions = {
				handleSuccess: (response: any) => {
					const url = window.URL.createObjectURL(new Blob([response]));
					const a = document.createElement("a");
					a.href = url;
					a.download = fileName ? fileName : "download.zip";
					document.body.appendChild(a);
					a.click();
					window.URL.revokeObjectURL(url);
				},
				handleError: (response: any) => appActions.handleError(response)(dispatch),
			};

			const callback = async () => files.downloadFiles(fileID, zip);

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

function resetError(): (dispatch: Dispatch) => Promise<void> {
	return async (dispatch) => {
		dispatch(setUploadError(false, ""));
	};
}

export {
	getFolderContent,
	updateCurrentFolder,
	getFoldersTree,
	updateBreadcrumb,
	createNewFolder,
	updateFolder,
	deleteFolder,
	uploadFile,
	uploadError,
	updateFile,
	deleteFile,
	moveFile,
	updateDisplayMode,
	updateTab,
	replaceFile,
	downloadFiles,
	resetError,
};
