import type { Dispatch } from "redux";
import {
	SET_USERS,
	SET_USER_FORM,
	SET_CURRENT_USER,
	SET_ROLES,
	SET_CURRENT_PERMISSIONS,
	SET_GLOBAL_PERMISSIONS,
} from "./constants";

import type {
	ISetUsers,
	ISetCurrentUser,
	ISetUserForm,
	ISetRoles,
	ISetCurrentPermissions,
	ISetGlobalPermissions,
} from "./interfaces";
import type { ICreatePasswordParams, IActivateRole, IGetRoles, IRole, IUser, IRootState } from "@ax/types";
import { global, users, roles } from "@ax/api";
import { appActions } from "@ax/containers/App";
import { handleRequest, isReqOk } from "@ax/helpers";

function setUsers(users: IUser[]): ISetUsers {
	return { type: SET_USERS, payload: { users } };
}

function setCurrentUser(currentUser: IUser): ISetCurrentUser {
	return { type: SET_CURRENT_USER, payload: { currentUser } };
}

function setUserForm(userForm: IUser): ISetUserForm {
	return { type: SET_USER_FORM, payload: { userForm } };
}

function setRoles(roles: IRole[]): ISetRoles {
	return { type: SET_ROLES, payload: { roles } };
}

function setCurrentPermissions(currentPermissions: string[]): ISetCurrentPermissions {
	return { type: SET_CURRENT_PERMISSIONS, payload: { currentPermissions } };
}

function setGlobalPermissions(globalPermissions: string[]): ISetGlobalPermissions {
	return { type: SET_GLOBAL_PERMISSIONS, payload: { globalPermissions } };
}

function getUsers(params: any, siteID?: number | null): (dispatch: Dispatch) => Promise<void> {
	return async (dispatch) => {
		try {
			const callback = async () => (siteID ? users.getSiteUsers(params, siteID) : users.getUsers(params));

			const responseActions = {
				handleSuccess: (response: IUser[]) => {
					dispatch(setUsers(response));
				},
				handleError: (response: any) => appActions.handleError(response)(dispatch),
			};

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

function getUser(
	id: number,
	token?: string,
	hasLoading = true,
): (dispatch: Dispatch, getState: () => IRootState) => Promise<void> {
	return async (dispatch, getState) => {
		try {
			const callback = async () => users.getUser(id, token);

			const responseActions = {
				handleSuccess: (response: IUser) => {
					if (token) {
						dispatch(appActions.setToken(token));
						dispatch(setCurrentUser(response));
						getUserCurrentPermissions()(dispatch, getState);
					}
					dispatch(setUserForm(response));
				},
				handleError: (response: any) => appActions.handleError(response)(dispatch),
			};

			const loading = hasLoading === false ? [] : [appActions.setIsLoading];

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

function updateUser(
	id: number,
	data: IUser,
	isProfile: boolean,
	isList?: boolean,
): (dispatch: Dispatch, getState: () => IRootState) => Promise<boolean> {
	return async (dispatch, getState) => {
		try {
			const {
				sites: { currentSiteInfo },
			} = getState();

			const callback = async () => users.updateUser(id, data);

			const responseActions = {
				handleSuccess: (response: any) => {
					if (isProfile) {
						dispatch(setCurrentUser(response));
					} else if (isList) {
						const params = { filterQuery: "?order=dateCreated" };
						const siteID = currentSiteInfo ? currentSiteInfo.id : null;
						getUsers(params, siteID)(dispatch);
					} else {
						dispatch(setUserForm(response));
					}
				},
				handleError: (response: any) => appActions.handleError(response)(dispatch),
			};

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

function updatePassword(id: number | null, data: any): (dispatch: Dispatch) => Promise<boolean> {
	return async (dispatch) => {
		try {
			if (!id) return false;
			const callback = async () => users.updatePassword(id, data);

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

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

function createUser(data: { name: string; email: string }): (dispatch: Dispatch) => Promise<boolean> {
	return async (dispatch) => {
		try {
			const callback = async () => users.createUser(data);

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

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

function createPassword(
	id: number,
	params: ICreatePasswordParams,
): (dispatch: Dispatch, getState: () => IRootState) => Promise<boolean> {
	return async (dispatch, getState) => {
		try {
			const callback = async () => global.createPassword(id, params);

			const responseActions = {
				handleSuccess: async (response: any) => {
					dispatch(appActions.setToken(response.token));
					await getRoles({ siteId: "global" }, response.token, false)(dispatch);
					await getUser(id, response.token, false)(dispatch, getState);
				},
				handleError: (response: any) => appActions.handleError(response)(dispatch),
			};

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

function deleteUser(id: number | number[]): (dispatch: Dispatch, getState: () => IRootState) => Promise<boolean> {
	return async (dispatch, getState) => {
		try {
			const {
				sites: { currentSiteInfo },
			} = getState();

			const params = { filterQuery: "?order=dateCreated" };

			const responseActions = {
				handleSuccess: () => {
					const siteID = currentSiteInfo ? currentSiteInfo.id : null;
					getUsers(params, siteID)(dispatch);
				},
				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 () => (Array.isArray(id) ? users.deleteUserBulk(id) : users.deleteUser(id));

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

function resendInvitation(id: number): (dispatch: Dispatch) => Promise<boolean> {
	return async (dispatch) => {
		try {
			const responseActions = {
				handleSuccess: () => null,
				handleError: (response: any) => appActions.handleError(response)(dispatch),
			};

			const callback = async () => users.resendInvitation(id);

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

function removeUserFromSite(userID: number, siteID: number): (dispatch: Dispatch) => Promise<boolean> {
	return async (dispatch) => {
		try {
			const responseActions = {
				handleSuccess: () => null,
				handleError: (response: any) => appActions.handleError(response)(dispatch),
			};

			const callback = async () => users.removeUserFromSite(userID, siteID);

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

function getRoles(params: IGetRoles, token?: string, hasLoading?: boolean): (dispatch: Dispatch) => Promise<void> {
	return async (dispatch) => {
		try {
			const callback = async () => roles.getRoles(params, token);

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

			const loading = hasLoading === false ? [] : [appActions.setIsLoading];

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

function activateRoles(params: IActivateRole): (dispatch: Dispatch) => Promise<boolean> {
	return async (dispatch) => {
		try {
			const { id, siteId, active } = params;
			const getRolesParams = { siteId };
			const responseActions = {
				handleSuccess: () => getRoles(getRolesParams)(dispatch),
				handleError: (response: any) => {
					const {
						data: { message },
					} = response;
					const isMultiple = Array.isArray(message) && message.length > 1;
					const msg = isMultiple ? `The activate action failed due to ${message.length} errors.` : undefined;
					appActions.handleError(response, isMultiple, msg)(dispatch);
				},
			};

			const callback = async () =>
				Array.isArray(id)
					? roles.activateRolesBulk({ id, siteId, active })
					: roles.activateRole({ id, siteId, active });

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

function getUserCurrentPermissions(): (dispatch: Dispatch, getState: () => IRootState) => Promise<void> {
	return async (dispatch, getState) => {
		const {
			users: { roles, currentUser },
			sites: { currentSiteInfo },
		} = getState();

		if (!currentUser) {
			return;
		}

		const userRoles = currentSiteInfo
			? currentUser.roles.find((roleSite) => roleSite.siteId === currentSiteInfo.id || roleSite.siteId === "all")?.roles
			: currentUser.roles.find((roleSite) => roleSite.siteId === "global")?.roles;

		let permissions: string[] = [];
		if (userRoles?.length) {
			userRoles.forEach((roleID: number) => {
				const rolePerms = roles?.reduce((acc: string[], curr: IRole) => {
					if (curr.id === roleID) {
						const permissions = currentSiteInfo
							? curr.permissions.sitePermissions
							: [...curr.permissions.globalPermissions, ...curr.permissions.sitePermissions];
						const keys = permissions.map((perm) => perm.key);
						acc.push(...keys);
					}
					return acc;
				}, []);
				permissions = [...permissions, ...rolePerms];
			});
		}
		dispatch(setCurrentPermissions(permissions));
		if (!currentSiteInfo) {
			dispatch(setGlobalPermissions(permissions));
		}
	};
}

export {
	getUsers,
	updateUser,
	updatePassword,
	getUser,
	setUserForm,
	createUser,
	setCurrentUser,
	deleteUser,
	resendInvitation,
	getRoles,
	activateRoles,
	createPassword,
	getUserCurrentPermissions,
	removeUserFromSite,
};
