(function (global, factory) {
    typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@angular/core'), require('@angular/common'), require('@angular/router'), require('@wm/core'), require('lodash-es')) :
    typeof define === 'function' && define.amd ? define(['exports', '@angular/core', '@angular/common', '@angular/router', '@wm/core', 'lodash-es'], factory) :
    (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory((global.wm = global.wm || {}, global.wm.security = {}), global.ng.core, global.ng.common, global.ng.router, global.wm.core, global._));
})(this, (function (exports, i0, i3, i2, i1, lodashEs) { 'use strict';

    function _interopNamespaceDefault(e) {
        var n = Object.create(null);
        if (e) {
            Object.keys(e).forEach(function (k) {
                if (k !== 'default') {
                    var d = Object.getOwnPropertyDescriptor(e, k);
                    Object.defineProperty(n, k, d.get ? d : {
                        enumerable: true,
                        get: function () { return e[k]; }
                    });
                }
            });
        }
        n.default = e;
        return Object.freeze(n);
    }

    var i0__namespace = /*#__PURE__*/_interopNamespaceDefault(i0);
    var i3__namespace = /*#__PURE__*/_interopNamespaceDefault(i3);
    var i2__namespace = /*#__PURE__*/_interopNamespaceDefault(i2);
    var i1__namespace = /*#__PURE__*/_interopNamespaceDefault(i1);

    // Todo[Shubham]: Move below constants to a common file
    const XSRF_COOKIE_NAME = 'wm_xsrf_token';
    class SecurityService {
        constructor(injector, $http, routerService, activatedRoute, _location) {
            this.injector = injector;
            this.$http = $http;
            this.routerService = routerService;
            this.activatedRoute = activatedRoute;
            this._location = _location;
            this.requestQueue = {};
        }
        isLoaded() {
            return this.config;
        }
        get() {
            return this.config;
        }
        load(forceFlag) {
            if (this.loadPromise) {
                return this.loadPromise;
            }
            if (!forceFlag && this.config) {
                return Promise.resolve(this.config);
            }
            this.loadPromise = new Promise((resolve, reject) => {
                this.$http.send({ 'url': './services/security/info', 'method': 'GET' }).then((response) => {
                    this.config = response.body;
                    this.lastLoggedInUser = i1.getClonedObject(this.loggedInUser);
                    this.loggedInUser = this.config.userInfo;
                    resolve(response.body);
                }).catch((err) => {
                    reject(err);
                }).finally(() => {
                    this.loadPromise = null;
                });
            });
            return this.loadPromise;
        }
        /**
         * gets the security config from the deployed app (backend call)
         * @param success
         * @param error
         */
        getWebConfig(success, error) {
            if (this.get()) {
                // if already fetched, return it
                i1.triggerFn(success, this.get());
                return;
            }
            this.load()
                .then(config => {
                i1.triggerFn(success, config);
            }, error);
        }
        /**
         * Returns security config
         * @param successCallback
         * @param failureCallback
         */
        getConfig(successCallback, failureCallback) {
            function invokeQueuedCallbacks(id, method, data) {
                lodashEs.forEach(this.requestQueue[id], fn => i1.triggerFn(fn[method], data));
                this.requestQueue[id] = null;
            }
            function onSuccess(config) {
                config.homePage = i1.getWmProjectProperties().homePage;
                if (config.userInfo) ;
                this.config = config;
                this.lastLoggedInUser = i1.getClonedObject(this.loggedInUser);
                this.loggedInUser = config.userInfo;
                invokeQueuedCallbacks.call(this, 'config', 'success', this.get());
            }
            function onError(error) {
                /*if ($rootScope.isMobileApplicationType) {
                 this.config = {
                 'securityEnabled': false,
                 'authenticated': false,
                 'homePage': _WM_APP_PROPERTIES.homePage,
                 'userInfo': null,
                 'login': null
                 };
                 invokeQueuedCallbacks('config', 'success', this.get());
                 } else {*/
                invokeQueuedCallbacks.call(this, 'config', 'error', error);
                // }
            }
            if (this.get()) {
                // if already fetched, return it
                i1.triggerFn(successCallback, this.get());
                return;
            }
            // Queue check, if same queue is already in progress, do not send another request
            this.requestQueue.config = this.requestQueue.config || [];
            this.requestQueue.config.push({
                success: successCallback,
                error: failureCallback
            });
            if (this.requestQueue.config.length > 1) {
                return;
            }
            if (!i1.hasCordova()) {
                // for web project, return config returned from backend API call.
                this.getWebConfig(onSuccess.bind(this), onError.bind(this));
            }
            /* else {
             /!*
             * for mobile app, first get the mobile config (saved in the apk)
             * - if security not enabled, just return mobile config (no backend call required)
             * - else, get Web config (will be  the same API hit for login) and merge the config with _mobileconfig
             *!/
             getMobileConfig(function (mobileconfig) {
             if (!mobileconfig.securityEnabled) {
             onSuccess(mobileconfig);
             } else {
             getWebConfig(function (config) {
             config = mergeWebAndMobileConfig(config);
             onSuccess(config);
             }, function () {onSuccess(mobileconfig); });
             }
             }, onError);
             }*/
        }
        getLastLoggedInUsername() {
            return this.lastLoggedInUser && this.lastLoggedInUser.userName;
        }
        /**
         * Returns the current page name
         * @returns {string}
         */
        getCurrentRoutePage() {
            const p = this._location.path();
            let lIndex = p.indexOf('?');
            lIndex = lIndex === -1 ? p.length : lIndex - 1;
            return p.substr(1, lIndex); // ignore the query params
        }
        /**
         * Returns Query params for specified param name in current Route
         * @param paramName, the param name whose query param value is to be retrieved
         * @returns {any}
         */
        getCurrentRouteQueryParam(paramName) {
            let paramVal;
            this.activatedRoute.queryParams.subscribe(params => {
                paramVal = params[paramName];
            });
            return paramVal;
        }
        isNoPageLoaded() {
            return !lodashEs.isEmpty(this.getCurrentRoutePage());
        }
        getPageByLoggedInUser() {
            const that = this;
            return new Promise((resolve) => {
                let page;
                {
                    that.getConfig((config) => {
                        if (config.securityEnabled && config.authenticated) {
                            page = config.userInfo.landingPage || i1.getWmProjectProperties().homePage;
                            // override the default xsrf cookie name and xsrf header names with WaveMaker specific values
                            if (that.isXsrfEnabled()) ;
                        }
                        else {
                            page = i1.getWmProjectProperties().homePage;
                        }
                        resolve(page);
                    }, function () {
                        resolve(i1.getWmProjectProperties().homePage);
                    });
                }
            });
        }
        /**
         * Loads the App page as follows:
         * Security disabled:
         *      - Home page
         * Security enabled:
         *      - User is logged in, respective landing page is loaded
         *      - Not logged in:
         *          - Home page is public, loads the home page
         *          - Home page not public, Login page(in config) is loaded
         * @param forcePageLoad
         * @returns {Promise<T>}
         */
        loadPageByUserRole(forcePageLoad) {
            const that = this;
            return this.getPageByLoggedInUser().then(page => {
                if (that.isNoPageLoaded() || forcePageLoad) {
                    // Reload the page when current page and post login landing page are same
                    if (that.getCurrentRoutePage() === page) {
                        window.location.reload();
                    }
                    else {
                        that.routerService.navigate([`/${page}`]);
                    }
                }
            });
        }
        /**
         * Navigates to the current user's homePage based on the config in SecurityService
         * Assumption is the SecurityService is updated with the latest security config before making call to this function
         */
        navigateOnLogin() {
            this.loadPageByUserRole(true);
        }
        /**
         * Gets the page which needs to be redirected to on successful login
         * @param config,
         * @param page, page name for redirection
         * @returns {any|string}
         */
        getRedirectPage(config, page) {
            const homePage = i1.getWmProjectProperties().homePage, loginPage = lodashEs.get(config, 'loginConfig.pageName');
            let prevRedirectPage, redirectPage = page || this.getCurrentRoutePage();
            // if user is already on Home page or Login page, they should not be redirected to that page, hence return undefined
            if (redirectPage === homePage || redirectPage === loginPage) {
                /*
                 * find previous redirect page from URL, if exists, user should redirect to that page.
                 * USE CASE:
                 *  user is on http://localhost:8080/app/#/Login?redirectTo=page
                 *  a variable call fails resulting 401
                 *  in this case, redirectTo page should be 'page' and not undefined
                 */
                prevRedirectPage = this.getCurrentRouteQueryParam('redirectTo');
                redirectPage = !lodashEs.isEmpty(prevRedirectPage) ? prevRedirectPage : undefined;
            }
            return redirectPage;
        }
        /**
         * Returns all the query params(including page params and redirect to params) associated with redirected page
         */
        getRedirectedRouteQueryParams() {
            let queryParams = {};
            this.activatedRoute.queryParams.subscribe((paramVal) => {
                lodashEs.forEach(paramVal, (val, key) => {
                    queryParams[key] = val;
                });
            });
            return queryParams;
        }
        // accepts query object like {a:1, b:2} and returns a=1&b=2 string
        getQueryString(queryObject) {
            const params = [];
            lodashEs.forEach(queryObject, function (value, key) {
                params.push(key + '=' + value);
            });
            return lodashEs.join(params, '&');
        }
        appLogin(params, successCallback, failureCallback) {
            let payload = '';
            // encode all parameters
            lodashEs.each(params, function (value, name) {
                payload += (payload ? '&' : '') + encodeURIComponent(name) + '=' + encodeURIComponent(value);
            });
            return this.$http.send({
                method: 'POST',
                headers: {
                    'Content-Type': 'application/x-www-form-urlencoded'
                },
                url: 'j_spring_security_check',
                'data': payload
            }).then((response) => {
                const xsrfCookieValue = response.body ? response.body[XSRF_COOKIE_NAME] : '';
                // override the default xsrf cookie name and xsrf header names with WaveMaker specific values
                if (xsrfCookieValue) {
                    if (i1.hasCordova()) {
                        localStorage.setItem(XSRF_COOKIE_NAME, xsrfCookieValue || '');
                    }
                }
                // After the successful login in device, this function triggers the pending onLoginCallbacks.
                this.injector.get(i1.App).notify('userLoggedIn', {});
                i1.triggerFn(successCallback, response);
            }, failureCallback);
        }
        /**
         * The API is used to check if the user is authenticated in the RUN mode.
         *
         * @param {function} successCallback to be called on success
         * @param {function} failureCallback to be called on failure
         */
        isAuthenticated(successCallback, failureCallback) {
            this.getConfig(function (config) {
                i1.triggerFn(successCallback, config.authenticated);
            }, failureCallback);
        }
        /**
         * The API is used to logout of the app.
         *
         * @param {function} successCallback to be called on success
         * @param {function} failureCallback to be called on failure
         */
        appLogout(successCallback, failureCallback) {
            return this.$http.send({
                target: 'Security',
                url: 'j_spring_security_logout',
                method: 'POST',
                responseType: 'text',
                byPassResult: true
            }).then((response) => {
                lodashEs.set(this.get(), 'authenticated', false);
                lodashEs.set(this.get(), 'userInfo', null);
                /*if (CONSTANTS.hasCordova) {
                    localStorage.setItem(CONSTANTS.XSRF_COOKIE_NAME, '');
                }*/
                this.injector.get(i1.App).notify('userLoggedOut', {});
                i1.triggerFn(successCallback, response);
            }, failureCallback);
        }
        /**
         * Checks and return the cookie
         * @param name, cookie key
         * @returns {string}
         */
        getCookieByName(name) {
            // Todo: Shubham Implement cookie native js
            return 'cookie';
        }
        /**
         * This function returns the cookieValue if xsrf is enabled.
         * In device, xsrf cookie is stored in localStorage.
         * @returns xsrf cookie value
         */
        isXsrfEnabled() {
            if (i1.hasCordova()) {
                return localStorage.getItem(XSRF_COOKIE_NAME);
            }
            return this.getCookieByName(XSRF_COOKIE_NAME);
        }
        /**
         * This function returns a promise. Promise is resolved when security is
         * 1. disabled
         * 2. enabled and user is authenticated
         * 3. enabled and user is not authenticated, then promise is resolved on user login
         * @returns {*} promise
         */
        onUserLogin() {
            return new Promise((resolve, reject) => {
                this.getConfig(config => {
                    if (config.securityEnabled) {
                        if (config.authenticated) {
                            resolve();
                        }
                        else {
                            const unsubscribe = this.injector.get(i1.App).subscribe('userLoggedIn', () => {
                                resolve();
                                unsubscribe();
                            });
                        }
                    }
                    else {
                        resolve();
                    }
                }, reject);
            });
        }
        /**
         * @returns a promise that is resolved with logged-in-user
         */
        getLoggedInUser() {
            return new Promise((resolve, reject) => {
                this.getConfig((config) => {
                    if (config && config.userInfo) {
                        resolve(config.userInfo);
                    }
                    else {
                        reject();
                    }
                }, reject);
            });
        }
        /**
         * This is for mobile apps to authenticate via browser.
         *
         * @returns a promise that is resolved after login
         */
        authInBrowser() {
            return Promise.reject('This authInBrowser should not be called');
        }
        static { this.ɵfac = function SecurityService_Factory(t) { return new (t || SecurityService)(i0__namespace.ɵɵinject(i0__namespace.Injector), i0__namespace.ɵɵinject(i1__namespace.AbstractHttpService), i0__namespace.ɵɵinject(i2__namespace.Router), i0__namespace.ɵɵinject(i2__namespace.ActivatedRoute), i0__namespace.ɵɵinject(i3__namespace.Location)); }; }
        static { this.ɵprov = /*@__PURE__*/ i0__namespace.ɵɵdefineInjectable({ token: SecurityService, factory: SecurityService.ɵfac }); }
    }
    (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0__namespace.ɵsetClassMetadata(SecurityService, [{
            type: i0.Injectable
        }], () => [{ type: i0__namespace.Injector }, { type: i1__namespace.AbstractHttpService }, { type: i2__namespace.Router }, { type: i2__namespace.ActivatedRoute }, { type: i3__namespace.Location }], null); })();

    class SecurityModule {
        static forRoot() {
            return {
                ngModule: SecurityModule,
                providers: [SecurityService]
            };
        }
        static { this.ɵfac = function SecurityModule_Factory(t) { return new (t || SecurityModule)(); }; }
        static { this.ɵmod = /*@__PURE__*/ i0__namespace.ɵɵdefineNgModule({ type: SecurityModule }); }
        static { this.ɵinj = /*@__PURE__*/ i0__namespace.ɵɵdefineInjector({}); }
    }
    (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0__namespace.ɵsetClassMetadata(SecurityModule, [{
            type: i0.NgModule,
            args: [{}]
        }], null, null); })();

    exports.SecurityModule = SecurityModule;
    exports.SecurityService = SecurityService;

}));
