/**
 * Copyright (c) 2020, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
 *
 * WSO2 Inc. licenses this file to you under the Apache License,
 * Version 2.0 (the "License"); you may not use this file except
 * in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied. See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

import {
    AUTHORIZATION_CODE,
    AsgardeoAuthClient,
    AsgardeoAuthException,
    AuthClientConfig,
    AuthenticationUtils,
    BasicUserInfo,
    CryptoHelper,
    CustomGrantConfig,
    DecodedIDTokenPayload,
    FetchResponse,
    GetAuthURLConfig,
    OIDCEndpoints,
    ResponseMode,
    SESSION_STATE,
    STATE,
    Store
} from "@asgardeo/auth-js";
import {
    DISABLE_HTTP_HANDLER,
    ENABLE_HTTP_HANDLER,
    GET_AUTH_URL,
    GET_BASIC_USER_INFO,
    GET_CONFIG_DATA,
    GET_CRYPTO_HELPER,
    GET_DECODED_IDP_ID_TOKEN,
    GET_DECODED_ID_TOKEN,
    GET_ID_TOKEN,
    GET_OIDC_SERVICE_ENDPOINTS,
    GET_SIGN_OUT_URL,
    HTTP_REQUEST,
    HTTP_REQUEST_ALL,
    INIT,
    IS_AUTHENTICATED,
    REFRESH_ACCESS_TOKEN,
    REQUEST_ACCESS_TOKEN,
    REQUEST_CUSTOM_GRANT,
    REQUEST_FINISH,
    REQUEST_START,
    REQUEST_SUCCESS,
    REVOKE_ACCESS_TOKEN,
    SET_SESSION_STATE,
    SIGN_OUT,
    SILENT_SIGN_IN_STATE,
    START_AUTO_REFRESH_TOKEN,
    Storage,
    UPDATE_CONFIG
} from "../constants";
import { AuthenticationHelper, SPAHelper, SessionManagementHelper } from "../helpers";
import {
    AuthorizationInfo,
    AuthorizationResponse,
    HttpClient,
    HttpError,
    HttpRequestConfig,
    HttpResponse,
    Message,
    ResponseMessage,
    WebWorkerClientConfig,
    WebWorkerClientInterface
} from "../models";
import { SPACustomGrantConfig } from "../models/request-custom-grant";
import { LocalStore, MemoryStore, SessionStore } from "../stores";
import { SPAUtils } from "../utils";
import { SPACryptoUtils } from "../utils/crypto-utils";

const initiateStore = (store: Storage | undefined): Store => {
    switch (store) {
        case Storage.LocalStorage:
            return new LocalStore();
        case Storage.SessionStorage:
            return new SessionStore();
        case Storage.BrowserMemory:
            return new MemoryStore();
        default:
            return new SessionStore();
    }
};

export const WebWorkerClient = async (
    instanceID: number,
    config: AuthClientConfig<WebWorkerClientConfig>,
    webWorker: new () => Worker,
    getAuthHelper: (
        authClient: AsgardeoAuthClient<WebWorkerClientConfig>,
        spaHelper: SPAHelper<WebWorkerClientConfig>
    ) => AuthenticationHelper<WebWorkerClientConfig>
): Promise<WebWorkerClientInterface> => {
    /**
     * HttpClient handlers
     */
    let httpClientHandlers: HttpClient;
    /**
     * API request time out.
     */
    const _requestTimeout: number = config?.requestTimeout ?? 60000;
    let _isHttpHandlerEnabled: boolean = true;
    let _getSignOutURLFromSessionStorage: boolean = false;

    const _store: Store = initiateStore(config.storage);
    const _cryptoUtils: SPACryptoUtils = new SPACryptoUtils();
    const _authenticationClient = new AsgardeoAuthClient<WebWorkerClientConfig>();
    await _authenticationClient.initialize(config, _store, _cryptoUtils, instanceID);
    const _spaHelper = new SPAHelper<WebWorkerClientConfig>(_authenticationClient);

    const _sessionManagementHelper = await SessionManagementHelper(
        async () => {
            const message: Message<string> = {
                type: SIGN_OUT
            };

            try {
                const signOutURL = await communicate<string, string>(message);

                return signOutURL;
            } catch {
                return SPAUtils.getSignOutURL(config.clientID, instanceID);
            }
        },
        config.storage,
        (sessionState: string) => setSessionState(sessionState)
    );

    const _authenticationHelper: AuthenticationHelper<WebWorkerClientConfig> =
        getAuthHelper(_authenticationClient, _spaHelper);

    const worker: Worker = new webWorker();

    const communicate = <T, R>(message: Message<T>): Promise<R> => {
        const channel = new MessageChannel();

        worker.postMessage(message, [ channel.port2 ]);

        return new Promise((resolve, reject) => {
            const timer = setTimeout(() => {
                reject(
                    new AsgardeoAuthException(
                        "SPA-WEB_WORKER_CLIENT-COM-TO01",
                        "Operation timed out.",
                        "No response was received from the web worker for " +
                        _requestTimeout / 1000 +
                        " since dispatching the request"
                    )
                );
            }, _requestTimeout);

            return (channel.port1.onmessage = ({ data }: { data: ResponseMessage<string>; }) => {
                clearTimeout(timer);
                channel.port1.close();
                channel.port2.close();

                if (data?.success) {
                    const responseData = data?.data ? JSON.parse(data?.data) : null;
                    if (data?.blob) {
                        responseData.data = data?.blob;
                    }

                    resolve(responseData);
                } else {
                    reject(data.error ? JSON.parse(data.error) : null);
                }
            });
        });
    };

    /**
     * Allows using custom grant types.
     *
     * @param {CustomGrantRequestParams} requestParams Request Parameters.
     *
     * @returns {Promise<HttpResponse|boolean>} A promise that resolves with a boolean value or the request
     * response if the the `returnResponse` attribute in the `requestParams` object is set to `true`.
     */
    const requestCustomGrant = (requestParams: SPACustomGrantConfig): Promise<FetchResponse | BasicUserInfo> => {
        const message: Message<CustomGrantConfig> = {
            data: requestParams,
            type: REQUEST_CUSTOM_GRANT
        };

        return communicate<CustomGrantConfig, FetchResponse | BasicUserInfo>(message)
            .then((response) => {
                if (requestParams.preventSignOutURLUpdate) {
                    _getSignOutURLFromSessionStorage = true;
                }

                return Promise.resolve(response);
            })
            .catch((error) => {
                return Promise.reject(error);
            });
    };

    /**
     *
     * Send the API request to the web worker and returns the response.
     *
     * @param {HttpRequestConfig} config The Http Request Config object
     *
     * @returns {Promise<HttpResponse>} A promise that resolves with the response data.
     */
    const httpRequest = <T = any>(config: HttpRequestConfig): Promise<HttpResponse<T>> => {
        /**
         *
         * Currently FormData is not supported to send to a web worker
         *
         * Below workaround will represent FormData object as a JSON.
         * This workaround will not be needed once FormData object is made cloneable
         * Reference: https://github.com/whatwg/xhr/issues/55
         */
        if(config?.data && config?.data instanceof FormData) {
            config.data = { ...Object.fromEntries(config?.data.entries()), formData: true };
        }

        const message: Message<HttpRequestConfig> = {
            data: config,
            type: HTTP_REQUEST
        };

        return communicate<HttpRequestConfig, HttpResponse<T>>(message)
            .then((response) => {
                return Promise.resolve(response);
            })
            .catch(async (error) => {
                if (_isHttpHandlerEnabled) {
                    if (typeof httpClientHandlers.requestErrorCallback === "function") {
                        await httpClientHandlers.requestErrorCallback(error);
                    }
                    if (typeof httpClientHandlers.requestFinishCallback === "function") {
                        httpClientHandlers.requestFinishCallback();
                    }
                }

                return Promise.reject(error);
            });
    };

    /**
     *
     * Send multiple API requests to the web worker and returns the response.
     * Similar `axios.spread` in functionality.
     *
     * @param {HttpRequestConfig[]} configs - The Http Request Config object
     *
     * @returns {Promise<HttpResponse<T>[]>} A promise that resolves with the response data.
     */
    const httpRequestAll = <T = any>(configs: HttpRequestConfig[]): Promise<HttpResponse<T>[]> => {
        const message: Message<HttpRequestConfig[]> = {
            data: configs,
            type: HTTP_REQUEST_ALL
        };

        return communicate<HttpRequestConfig[], HttpResponse<T>[]>(message)
            .then((response) => {
                return Promise.resolve(response);
            })
            .catch(async (error) => {
                if (_isHttpHandlerEnabled) {
                    if (typeof httpClientHandlers.requestErrorCallback === "function") {
                        await httpClientHandlers.requestErrorCallback(error);
                    }
                    if (typeof httpClientHandlers.requestFinishCallback === "function") {
                        httpClientHandlers.requestFinishCallback();
                    }
                }

                return Promise.reject(error);
            });
    };

    const enableHttpHandler = (): Promise<boolean> => {
        const message: Message<null> = {
            type: ENABLE_HTTP_HANDLER
        };
        return communicate<null, null>(message)
            .then(() => {
                _isHttpHandlerEnabled = true;

                return Promise.resolve(true);
            })
            .catch((error) => {
                return Promise.reject(error);
            });
    };

    const disableHttpHandler = (): Promise<boolean> => {
        const message: Message<null> = {
            type: DISABLE_HTTP_HANDLER
        };
        return communicate<null, null>(message)
            .then(() => {
                _isHttpHandlerEnabled = false;

                return Promise.resolve(true);
            })
            .catch((error) => {
                return Promise.reject(error);
            });
    };

    /**
     * Initializes the object with authentication parameters.
     *
     * @param {ConfigInterface} config The configuration object.
     *
     * @returns {Promise<boolean>} Promise that resolves when initialization is successful.
     *
     */
    const initialize = (): Promise<boolean> => {
        if (!httpClientHandlers) {
            httpClientHandlers = {
                requestErrorCallback: () => Promise.resolve(),
                requestFinishCallback: () => null,
                requestStartCallback: () => null,
                requestSuccessCallback: () => null
            };
        }

        worker.onmessage = ({ data }) => {
            switch (data.type) {
                case REQUEST_FINISH:
                    httpClientHandlers?.requestFinishCallback && httpClientHandlers?.requestFinishCallback();
                    break;
                case REQUEST_START:
                    httpClientHandlers?.requestStartCallback && httpClientHandlers?.requestStartCallback();
                    break;
                case REQUEST_SUCCESS:
                    httpClientHandlers?.requestSuccessCallback &&
                        httpClientHandlers?.requestSuccessCallback(data.data ? JSON.parse(data.data) : null);
                    break;
            }
        };

        const message: Message<AuthClientConfig<WebWorkerClientConfig>> = {
            data: config,
            type: INIT
        };

        return communicate<AuthClientConfig<WebWorkerClientConfig>, null>(message)
            .then(() => {
                return Promise.resolve(true);
            })
            .catch((error) => {
                return Promise.reject(error);
            });
    };

    const setSessionState = (sessionState: string | null): Promise<void> => {
        const message: Message<string | null> = {
            data: sessionState,
            type: SET_SESSION_STATE
        };

        return communicate<string | null, void>(message);
    };

    const startAutoRefreshToken = (): Promise<void> => {
        const message: Message<null> = {
            type: START_AUTO_REFRESH_TOKEN
        };

        return communicate<null, void>(message);
    };

    const checkSession = async (): Promise<void> => {
        const oidcEndpoints: OIDCEndpoints = await getOIDCServiceEndpoints();
        const config: AuthClientConfig<WebWorkerClientConfig> = await getConfigData();

        _authenticationHelper.initializeSessionManger(
            config,
            oidcEndpoints,
            async () => (await getBasicUserInfo()).sessionState,
            async (params?: GetAuthURLConfig): Promise<string> => (await getAuthorizationURL(params)).authorizationURL,
            _sessionManagementHelper
        );
    };

    const constructSilentSignInUrl = async (
        additionalParams: Record<string, string | boolean> = {}
    ): Promise<string> => {
        const config: AuthClientConfig<WebWorkerClientConfig> = await getConfigData();
        const message: Message<GetAuthURLConfig> = {
            data: {
                prompt: "none",
                state: SILENT_SIGN_IN_STATE,
                ...additionalParams
            },
            type: GET_AUTH_URL
        };

        const response: AuthorizationResponse = await communicate<GetAuthURLConfig, AuthorizationResponse>(message);

        const pkceKey: string = AuthenticationUtils.extractPKCEKeyFromStateParam(
            new URL(response.authorizationURL).searchParams.get(STATE) ?? ""
        );

        response.pkce && config.enablePKCE && SPAUtils.setPKCE(pkceKey, response.pkce);

        const urlString: string = response.authorizationURL;

        // Replace form_post with query
        const urlObject = new URL(urlString);
        urlObject.searchParams.set("response_mode", "query");
        const url: string = urlObject.toString();

        return url;
    }

    /**
     * This method checks if there is an active user session in the server by sending a prompt none request.
     * If the user is signed in, this method sends a token request. Returns false otherwise.
     *
     * @return {Promise<BasicUserInfo|boolean} Returns a Promise that resolves with the BasicUserInfo
     * if the user is signed in or with `false` if there is no active user session in the server.
     */
    const trySignInSilently = async (
        additionalParams?: Record<string, string | boolean>,
        tokenRequestConfig?: { params: Record<string, unknown> }
    ): Promise<BasicUserInfo | boolean> => {
        return await _authenticationHelper.trySignInSilently(
            constructSilentSignInUrl,
            requestAccessToken,
            _sessionManagementHelper,
            additionalParams,
            tokenRequestConfig
        );
    };

    /**
     * Generates an authorization URL.
     *
     * @param {GetAuthURLConfig} params Authorization URL params.
     * @returns {Promise<string>} Authorization URL.
     */
    const getAuthorizationURL = async (params?: GetAuthURLConfig): Promise<AuthorizationResponse> => {
        const config: AuthClientConfig<WebWorkerClientConfig> = await getConfigData();

        const message: Message<GetAuthURLConfig> = {
            data: params,
            type: GET_AUTH_URL
        };

        return communicate<GetAuthURLConfig, AuthorizationResponse>(message).then(
            async (response: AuthorizationResponse) => {
                if (response.pkce && config.enablePKCE) {
                    const pkceKey: string = AuthenticationUtils.extractPKCEKeyFromStateParam(
                        new URL(response.authorizationURL).searchParams.get(STATE) ?? ""
                    );

                    SPAUtils.setPKCE(pkceKey, response.pkce);
                }

                return Promise.resolve(response);
            }
        );
    };

    const requestAccessToken = async (
        resolvedAuthorizationCode: string,
        resolvedSessionState: string,
        resolvedState: string,
        tokenRequestConfig?: {
            params: Record<string, unknown>
        }
    ): Promise<BasicUserInfo> => {
        const config: AuthClientConfig<WebWorkerClientConfig> = await getConfigData();
        const pkceKey: string = AuthenticationUtils.extractPKCEKeyFromStateParam(resolvedState);

        const message: Message<AuthorizationInfo> = {
            data: {
                code: resolvedAuthorizationCode,
                pkce: config.enablePKCE ? SPAUtils.getPKCE(pkceKey) : undefined,
                sessionState: resolvedSessionState,
                state: resolvedState,
                tokenRequestConfig
            },
            type: REQUEST_ACCESS_TOKEN
        };

        config.enablePKCE && SPAUtils.removePKCE(pkceKey);

        return communicate<AuthorizationInfo, BasicUserInfo>(message)
            .then((response) => {
                const message: Message<null> = {
                    type: GET_SIGN_OUT_URL
                };

                return communicate<null, string>(message)
                    .then((url: string) => {
                        SPAUtils.setSignOutURL(url, config.clientID, instanceID);

                        // Enable OIDC Sessions Management only if it is set to true in the config.
                        if (config.enableOIDCSessionManagement) {
                            checkSession();
                        }

                        startAutoRefreshToken();

                        return Promise.resolve(response);
                    })
                    .catch((error) => {
                        return Promise.reject(error);
                    });
            })
            .catch((error) => {
                return Promise.reject(error);
            });
    };

    const shouldStopAuthn = async (): Promise<boolean> => {
        return await _sessionManagementHelper.receivePromptNoneResponse(
            async (sessionState: string | null) => {
                return setSessionState(sessionState);
            }
        );
    }

    const tryRetrievingUserInfo = async (): Promise<BasicUserInfo | undefined> => {
        if (await isAuthenticated()) {
            await startAutoRefreshToken();

            // Enable OIDC Sessions Management only if it is set to true in the config.
            if (config.enableOIDCSessionManagement) {
                checkSession();
            }

            return getBasicUserInfo();
        }
    }

    /**
     * Initiates the authentication flow.
     *
     * @returns {Promise<UserInfo>} A promise that resolves when authentication is successful.
     */
    const signIn = async (
        params?: GetAuthURLConfig,
        authorizationCode?: string,
        sessionState?: string,
        state?: string,
        tokenRequestConfig?: {
            params: Record<string, unknown>
        }
    ): Promise<BasicUserInfo> => {

        const basicUserInfo =  await _authenticationHelper.handleSignIn(
            shouldStopAuthn,
            checkSession,
            tryRetrievingUserInfo
        );

        if(basicUserInfo) {
            return basicUserInfo;
        } else {
            let resolvedAuthorizationCode: string;
            let resolvedSessionState: string;
            let resolvedState: string;

            if (config?.responseMode === ResponseMode.formPost && authorizationCode) {
                resolvedAuthorizationCode = authorizationCode;
                resolvedSessionState = sessionState ?? "";
                resolvedState = state ?? "";
            } else {
                resolvedAuthorizationCode = new URL(window.location.href).searchParams.get(AUTHORIZATION_CODE) ?? "";
                resolvedSessionState = new URL(window.location.href).searchParams.get(SESSION_STATE) ?? "";
                resolvedState = new URL(window.location.href).searchParams.get(STATE) ?? "";

                SPAUtils.removeAuthorizationCode();
            }

            if (resolvedAuthorizationCode && resolvedState) {
                return requestAccessToken(resolvedAuthorizationCode, resolvedSessionState,
                    resolvedState, tokenRequestConfig);
            }

            return getAuthorizationURL(params)
                .then(async (response: AuthorizationResponse) => {
                    location.href = response.authorizationURL;

                    await SPAUtils.waitTillPageRedirect();

                    return Promise.resolve({
                        allowedScopes: "",
                        displayName: "",
                        email: "",
                        sessionState: "",
                        sub: "",
                        tenantDomain: "",
                        username: ""
                    });
                })
                .catch((error) => {
                    return Promise.reject(error);
                });
        }
    };

    /**
     * Initiates the sign out flow.
     *
     * @returns {Promise<boolean>} A promise that resolves when sign out is completed.
     */
    const signOut = (): Promise<boolean> => {
        return new Promise((resolve, reject) => {
            if (!_getSignOutURLFromSessionStorage) {
                const message: Message<null> = {
                    type: SIGN_OUT
                };

                return communicate<null, string>(message)
                    .then(async (response) => {
                        window.location.href = response;

                        await SPAUtils.waitTillPageRedirect();

                        return resolve(true);
                    })
                    .catch((error) => {
                        return reject(error);
                    });
            } else {
                window.location.href = SPAUtils.getSignOutURL(config.clientID, instanceID);

                SPAUtils.waitTillPageRedirect().then(() => {
                    return Promise.resolve(true);
                });
            }
        })
    };

    /**
     * Revokes token.
     *
     * @returns {Promise<boolean>} A promise that resolves when revoking is completed.
     */
    const revokeAccessToken = (): Promise<boolean> => {
        const message: Message<null> = {
            type: REVOKE_ACCESS_TOKEN
        };

        return communicate<null, boolean>(message)
            .then((response) => {
                _sessionManagementHelper.reset();
                return Promise.resolve(response);
            })
            .catch((error) => {
                return Promise.reject(error);
            });
    };

    const getOIDCServiceEndpoints = (): Promise<OIDCEndpoints> => {
        const message: Message<null> = {
            type: GET_OIDC_SERVICE_ENDPOINTS
        };

        return communicate<null, OIDCEndpoints>(message)
            .then((response) => {
                return Promise.resolve(response);
            })
            .catch((error) => {
                return Promise.reject(error);
            });
    };

    const getConfigData = (): Promise<AuthClientConfig<WebWorkerClientConfig>> => {
        const message: Message<null> = {
            type: GET_CONFIG_DATA
        };

        return communicate<null, AuthClientConfig<WebWorkerClientConfig>>(message)
            .then((response) => {
                return Promise.resolve(response);
            })
            .catch((error) => {
                return Promise.reject(error);
            });
    };

    const getBasicUserInfo = (): Promise<BasicUserInfo> => {
        const message: Message<null> = {
            type: GET_BASIC_USER_INFO
        };

        return communicate<null, BasicUserInfo>(message)
            .then((response) => {
                return Promise.resolve(response);
            })
            .catch((error) => {
                return Promise.reject(error);
            });
    };

    const getDecodedIDToken = (): Promise<DecodedIDTokenPayload> => {
        const message: Message<null> = {
            type: GET_DECODED_ID_TOKEN
        };

        return communicate<null, DecodedIDTokenPayload>(message)
            .then((response) => {
                return Promise.resolve(response);
            })
            .catch((error) => {
                return Promise.reject(error);
            });
    };

    const getDecodedIDPIDToken = (): Promise<DecodedIDTokenPayload> => {
        const message: Message<null> = {
            type: GET_DECODED_IDP_ID_TOKEN
        };

        return communicate<null, DecodedIDTokenPayload>(message)
            .then((response) => {
                return Promise.resolve(response);
            })
            .catch((error) => {
                return Promise.reject(error);
            });
    };

    const getCryptoHelper = (): Promise<CryptoHelper> => {
        const message: Message<null> = {
            type: GET_CRYPTO_HELPER
        };

        return communicate<null, CryptoHelper>(message)
            .then((response) => {
                return Promise.resolve(response);
            })
            .catch((error) => {
                return Promise.reject(error);
            });
    };

    const getIDToken = (): Promise<string> => {
        const message: Message<null> = {
            type: GET_ID_TOKEN
        };

        return communicate<null, string>(message)
            .then((response) => {
                return Promise.resolve(response);
            })
            .catch((error) => {
                return Promise.reject(error);
            });
    };

    const isAuthenticated = (): Promise<boolean> => {
        const message: Message<null> = {
            type: IS_AUTHENTICATED
        };

        return communicate<null, boolean>(message)
            .then((response) => {
                return Promise.resolve(response);
            })
            .catch((error) => {
                return Promise.reject(error);
            });
    };

    const refreshAccessToken = (): Promise<BasicUserInfo> => {
        const message: Message<null> = {
            type: REFRESH_ACCESS_TOKEN
        };

        return communicate<null, BasicUserInfo>(message);
    };

    const setHttpRequestSuccessCallback = (callback: (response: HttpResponse) => void): void => {
        if (callback && typeof callback === "function") {
            httpClientHandlers.requestSuccessCallback = callback;
        }
    };

    const setHttpRequestErrorCallback = (callback: (response: HttpError) => void | Promise<void>): void => {
        if (callback && typeof callback === "function") {
            httpClientHandlers.requestErrorCallback = callback;
        }
    };

    const setHttpRequestStartCallback = (callback: () => void): void => {
        if (callback && typeof callback === "function") {
            httpClientHandlers.requestStartCallback = callback;
        }
    };

    const setHttpRequestFinishCallback = (callback: () => void): void => {
        if (callback && typeof callback === "function") {
            httpClientHandlers.requestFinishCallback = callback;
        }
    };

    const updateConfig = async (newConfig: Partial<AuthClientConfig<WebWorkerClientConfig>>): Promise<void> => {
        const existingConfig = await getConfigData();
        const isCheckSessionIframeDifferent: boolean = !(
            existingConfig &&
            existingConfig.endpoints &&
            existingConfig.endpoints.checkSessionIframe &&
            newConfig &&
            newConfig.endpoints &&
            newConfig.endpoints.checkSessionIframe &&
            existingConfig.endpoints.checkSessionIframe === newConfig.endpoints.checkSessionIframe
        );
        const config = { ...existingConfig, ...newConfig };

        const message: Message<Partial<AuthClientConfig<WebWorkerClientConfig>>> = {
            data: config,
            type: UPDATE_CONFIG
        };

        await communicate<Partial<AuthClientConfig<WebWorkerClientConfig>>, void>(message);

        // Re-initiates check session if the check session endpoint is updated.
        if (config.enableOIDCSessionManagement && isCheckSessionIframeDifferent) {
            _sessionManagementHelper.reset();

            checkSession();
        }
    };

    return {
        disableHttpHandler,
        enableHttpHandler,
        getBasicUserInfo,
        getConfigData,
        getCryptoHelper,
        getDecodedIDPIDToken,
        getDecodedIDToken,
        getIDToken,
        getOIDCServiceEndpoints,
        httpRequest,
        httpRequestAll,
        initialize,
        isAuthenticated,
        refreshAccessToken,
        requestCustomGrant,
        revokeAccessToken,
        setHttpRequestErrorCallback,
        setHttpRequestFinishCallback,
        setHttpRequestStartCallback,
        setHttpRequestSuccessCallback,
        signIn,
        signOut,
        trySignInSilently,
        updateConfig
    };
};
