import { global, languages } from "@ax/api";
import { usersActions } from "@ax/containers/Users";
import { isReqOk } from "@ax/helpers";
import type { IRootState } from "@ax/types";

import type { AxiosResponse } from "axios";
import differenceInSeconds from "date-fns/differenceInSeconds";
import type { Dispatch } from "redux";

import history from "../../routes/history";
import {
	IS_LOGGING_IN,
	LOGOUT,
	RESET_LOADING,
	SET_ERROR,
	SET_GLOBAL_LANGUAGES,
	SET_GLOBAL_SETTINGS,
	SET_HAS_ANIMATION,
	SET_IS_LOADING,
	SET_IS_SAVING,
	SET_LANGUAGE,
	SET_SESSION_STARTED_AT,
	SET_TOKEN,
	SET_USER,
} from "./constants";
import type {
	IIsLoggingInAction,
	ILogoutAction,
	IResetLoading,
	ISetErrorAction,
	ISetGlobalLanguages,
	ISetGlobalSettings,
	ISetHasAnimation,
	ISetIsLoading,
	ISetIsSaving,
	ISetLanguageAction,
	ISetSessionStartedAtAction,
	ISetTokenAction,
	ISetUserAction,
} from "./interfaces";
import type { IError, IUser } from "./reducer";

function setIsLoading(isLoading: boolean): ISetIsLoading {
	return { type: SET_IS_LOADING, payload: { isLoading } };
}

function setIsSaving(isSaving: boolean): ISetIsSaving {
	return { type: SET_IS_SAVING, payload: { isSaving } };
}

function setHasAnimation(hasAnimation: boolean): ISetHasAnimation {
	return { type: SET_HAS_ANIMATION, payload: { hasAnimation } };
}

function resetLoading(): IResetLoading {
	return { type: RESET_LOADING };
}

const resetStateValues = (dispatch: Dispatch) => {
	dispatch(resetLoading());
	dispatch(setIsSaving(false));
};

function setHistoryPush(path: string, isEditor?: boolean): (dispatch: Dispatch) => Promise<void> {
	return async (dispatch) => {
		try {
			if (isEditor) {
				resetStateValues(dispatch);
			}
			history.push(path, isEditor ? { isFromEditor: true } : undefined);
		} catch (e) {
			console.log("Error", e);
		}
	};
}

function setIsLogging(isLoggingIn: boolean): IIsLoggingInAction {
	return { type: IS_LOGGING_IN, payload: { isLoggingIn } };
}

function setToken(token: string): ISetTokenAction {
	return { type: SET_TOKEN, payload: { token } };
}

function logout(): ILogoutAction {
	const listConfig = localStorage.getItem("sitesConfig") || "";
	localStorage.clear();
	localStorage.setItem("sitesConfig", listConfig);
	return { type: LOGOUT, payload: { token: "" } };
}

function logoutAndNavigate(): (dispatch: Dispatch) => Promise<void> {
	return async (dispatch) => {
		await setHistoryPush("/logout")(dispatch);
	};
}

function setLanguage(lang: { locale: string; id: number }): ISetLanguageAction {
	localStorage.setItem("langID", JSON.stringify(lang.id));
	return { type: SET_LANGUAGE, payload: { lang } };
}

function setGlobalLanguages(globalLangs: any[]): ISetGlobalLanguages {
	return { type: SET_GLOBAL_LANGUAGES, payload: { globalLangs } };
}

function setGlobalSettings(globalSettings: any[]): ISetGlobalSettings {
	return { type: SET_GLOBAL_SETTINGS, payload: { globalSettings } };
}

function setUser(user: IUser): ISetUserAction {
	return { type: SET_USER, payload: { user } };
}

function setError(error: IError): ISetErrorAction {
	return { type: SET_ERROR, payload: { ...error } };
}

function resetError(): ISetErrorAction {
	(window as any).handleErrorClick = undefined;
	return { type: SET_ERROR, payload: { code: undefined, text: "" } };
}

function setSessionStartedAt(sessionStartedAt: Date): ISetSessionStartedAtAction {
	return { type: SET_SESSION_STARTED_AT, payload: { sessionStartedAt } };
}

function handleError(response: any, isMultiple = false, msg?: string): (dispatch: Dispatch) => Promise<void> {
	return async (dispatch) => {
		if (typeof response === "string") {
			dispatch(setError({ text: response }));
			return;
		}

		const {
			data: { code, message },
			btnText,
			actionsBelow,
			text,
			btnAction,
		} = response;

		if (code === 500) {
			setHistoryPush("/500")(dispatch);
		} else {
			const firstMsg = Array.isArray(message) && message[0] ? message[0].error : message || text;

			const error = {
				code,
				text: msg ? msg : firstMsg,
				btnText,
				actionsBelow,
				btnAction,
				subErrors: isMultiple ? message : [],
			};

			dispatch(setError(error));
		}
	};
}

function login(loginData: {
	email?: string;
	password?: string;
	rememberMe?: boolean;
	petitionId?: string;
}): (dispatch: Dispatch, setState: () => IRootState) => Promise<boolean> {
	return async (dispatch, setState) => {
		dispatch(setIsLogging(true));
		const { email, password, rememberMe, petitionId } = loginData;
		const loginResponse: AxiosResponse<{ id: number; token: string }> = await global.login({
			username: email,
			password,
			petitionId,
		});
		dispatch(setIsLogging(false));
		switch (loginResponse?.status) {
			case 200: {
				const {
					data: { id, token },
				} = loginResponse;
				const langResponse = await languages.getLanguages(token);
				isReqOk(langResponse.status) && dispatch(setGlobalLanguages(langResponse.data.items));
				dispatch(setToken(loginResponse.data.token));
				await usersActions.getUser(id, token, false)(dispatch, setState);
				dispatch(setSessionStartedAt(new Date()));
				dispatch(setHasAnimation(true));
				if (rememberMe) {
					const user: any = { token: loginResponse.data.token };
					localStorage.setItem("user", JSON.stringify(user));
				}
				return true;
			}
			case 403: {
				const error = {
					...loginResponse,
					btnText: "Lost your password?",
					actionsBelow: true,
				};
				handleError(error)(dispatch);
				return false;
			}
			case undefined: {
				const error = {
					data: {
						code: 503,
						message: "An error has occurred. Please contact your system administrator.",
					},
				};
				handleError(error)(dispatch);
				return false;
			}
			default:
				handleError(loginResponse)(dispatch);
				return false;
		}
	};
}

function loginSSO(): (dispatch: Dispatch) => Promise<string | null> {
	return async (dispatch) => {
		try {
			dispatch(setIsLogging(true));
			const response: { status: number; data: any } = await global.login();

			if (isReqOk(response?.status)) {
				return response.data;
			} else {
				return null;
			}
		} catch (_error) {
			return null;
		}
	};
}

function getGlobalSettings(): (dispatch: Dispatch) => Promise<void> {
	return async (dispatch) => {
		try {
			const response: { status: number; data: any } = await global.getSettings();

			if (isReqOk(response?.status)) {
				dispatch(setGlobalSettings(response.data));
			} else {
				console.log("Error en getSettings");
			}
		} catch (e) {
			console.log(e);
		}
	};
}

function checkUserSession(): (dispatch: Dispatch, getState: () => IRootState) => Promise<void> {
	return async (dispatch, getState) => {
		const {
			app: { sessionStartedAt },
		} = getState();

		const timeSinceSessionStart = !!sessionStartedAt && differenceInSeconds(new Date(), new Date(sessionStartedAt));
		const eightHoursInSeconds = 8 * 60 * 60;
		const isSessionFinished = !!timeSinceSessionStart && timeSinceSessionStart > eightHoursInSeconds;
		if (!timeSinceSessionStart || isSessionFinished) {
			localStorage.removeItem("persist:root");
			dispatch(setSessionStartedAt(new Date()));
		}
	};
}

export {
	checkUserSession,
	getGlobalSettings,
	handleError,
	login,
	loginSSO,
	logout,
	logoutAndNavigate,
	resetError,
	setError,
	setGlobalLanguages,
	setGlobalSettings,
	setHasAnimation,
	setHistoryPush,
	setIsLoading,
	setIsLogging,
	setIsSaving,
	setLanguage,
	setToken,
	setUser,
};
