import { __awaiter, __decorate } from "tslib";
import { ConnectConfig, defaultConnectConfig, Session } from '@genesislcap/foundation-comms';
import { customElement, FASTElement, observable } from '@microsoft/fast-element';
import { Container, inject, Registration } from '@microsoft/fast-foundation';
import { DefaultRouteRecognizer } from '@microsoft/fast-router';
import * as Components from '../components';
import { MainRouterConfig } from '../routes';
import { definition, standaloneDefinition } from './main.definition';
/**
 * Defines the login class which handles account authentication from the front-end
 *
 * @remarks
 *
 * Add the Login class as a router element, and it will handle the account authentication for you.
 * Requires use of `@genesislcap/foundation-comms` for the {@link @genesislcap/foundation-comms#Auth | Auth} and
 * {@link @genesislcap/foundation#Session | Session} classes.
 *
 * There are a lot of configuration options available, and different authentication types (such as login via SSO). Use
 * the modules exported `configure` and `define` functions for more power.
 *
 * @example
 * The following is an example of using it in your app, setting it up in the router configuration. This isn't a complete
 * routes confutation, but it contains all required configuration in regard to adding Login functionality.
 * ```ts
 * // Import required dependencies from the foundation-login package
 * import { Login } from '@genesislcap/foundation-login';
 * // Import required dependencies from the foundation-comms package
 * // You could also import analytics events and set them up in the NavigationContributor
 * import { Auth, Session } from '@genesislcap/foundation-comms';
 *
 * type RouterSettings = {
 *   public?: boolean;
 *   autoAuth?: boolean;
 * }
 *
 * // Define your router config
 * export class MainRouterConfig extends RouterConfiguration<RouterSettings> {
 *   // Ensure you inject in required dependencies
 *   constructor(
 *     @Auth private auth: Auth,
 *     @Session private session: Session
 *   ) {
 *     super();
 *   }
 *
 *   // Add Login as a route
 *   public configure() {
 *     ...
 *     this.routes.map(
 *       { path: '', redirect: 'login' },
 *       {
 *         path: 'login',
 *         element: Login,
 *         title: 'Login',
 *         name: 'login',
 *         layout: loginLayout,
 *         settings: { public: true },
 *         childRouters: true,
 *       },
 *       ... // Other routes config here
 *     );
 *
 *     const session = this.session;
 *     const auth = this.auth;
 *
 *     // Example of a FallbackRouteDefinition
 *     this.routes.fallback(() =>
 *       auth.isLoggedIn ? { redirect: 'not-found' } : { redirect: 'login' }
 *     );
 *
 *      // Example of a NavigationContributor
 *     this.contributors.push({
 *       navigate: async (phase) => {
 *         const settings = phase.route.settings;
 *
 *         // Could add in processes such as analytics here
 *
 *         // If public route don't block
 *         if (settings && settings.public) {
 *           return;
 *         }
 *
 *         // If logged in don't block
 *         if (auth.isLoggedIn) {
 *           return;
 *         }
 *
 *         // If autoAuth and session is valid try to connect+auto-login
 *         if (settings && settings.autoAuth && (await auth.reAuthFromSession())) {
 *           return;
 *         }
 *
 *         // Otherwise route them to login
 *         phase.cancel(() => {
 *           session.captureReturnUrl();
 *           Route.name.replace(phase.router, 'login');
 *         });
 *       },
 *     });
 *   }
 *
 *   ... // Other configuration/methods
 *
 * }
 * ```
 *
 * @public
 */
let Login = class Login extends FASTElement {
    constructor() {
        super(...arguments);
        /**
         * Marked true when remote components are loaded, currently unused.
         * @internal
         */
        this.ready = false;
    }
    /**
     * @internal
     */
    connectedCallback() {
        const _super = Object.create(null, {
            connectedCallback: { get: () => super.connectedCallback }
        });
        return __awaiter(this, void 0, void 0, function* () {
            this.registerDIDependencies();
            _super.connectedCallback.call(this);
            yield this.registerCommonComponents();
            this.checkForSSOToken();
        });
    }
    /**
     * Used to forward the user onto a logged-in session during an SSO journey
     *
     * @remarks
     *
     * Called when loading the login page via the connectedCallback() method.
     * During normal flows there will be no SSO tokens set and hence this function
     * will quit execution.
     * During an SSO journey the SSO provider will set the SSO token as a URLSearchParams
     * parameter and redirect back to us. In this case we will take this and forward the
     * user on, with their logged in session.
     *
     * There are two paths the user can take here - if they are in a popup
     * (window.opener exists) then we need to set the session and get the parent window
     * (the one which the user wants their session on) to refresh, and close the popup.
     *
     * Or if they are not in a popup then we simply set the ssoToken in the session.
     * When this component is loaded with the ssoToken then it will redirect the
     * user to their app (the `defaultRedirectUrl`).
     *
     * @internal
     */
    checkForSSOToken() {
        const queryParams = new URLSearchParams(window.location.search);
        const ssoToken = queryParams.get('SSO_TOKEN');
        if (ssoToken) {
            if (window.opener) {
                window.opener.sessionStorage.setItem('ssoToken', ssoToken);
                window.opener.location.reload();
                window.close();
            }
            else {
                this.session.setSessionStorageItem('ssoToken', ssoToken);
            }
        }
    }
    /**
     * Called from the connectedCallback
     * @internal
     */
    registerCommonComponents() {
        return __awaiter(this, void 0, void 0, function* () {
            /**
             * Send event to indicate some async work is happening. Will be picked up by overlay micro frontend.
             */
            yield Components.loadRemotes({});
            /**
             * Simulate loading delay, TODO: Remove
             *
             * await new Promise(resolve => setTimeout(resolve, 3000));
             */
            this.ready = true;
        });
    }
    /**
     * @internal
     */
    registerDIDependencies() {
        this.container.register(Registration.transient(DefaultRouteRecognizer, DefaultRouteRecognizer), 
        /**
         * Register custom configs for core services. Note this can also be done via provideDesignSystem().register(...)
         * if preferred. Here for example mainly as this will mostly occur at an app level.
         */
        Registration.instance(ConnectConfig, Object.assign(Object.assign({}, defaultConnectConfig), { connect: Object.assign(Object.assign({}, defaultConnectConfig.connect), { heartbeatInterval: 12000 }) })));
    }
};
__decorate([
    inject(MainRouterConfig)
], Login.prototype, "config", void 0);
__decorate([
    Container
], Login.prototype, "container", void 0);
__decorate([
    Session
], Login.prototype, "session", void 0);
__decorate([
    observable
], Login.prototype, "ready", void 0);
__decorate([
    observable
], Login.prototype, "provider", void 0);
Login = __decorate([
    customElement(standaloneDefinition)
], Login);
export { Login };
/**
 * @beta
 * @privateRemarks
 * A hosted version of the MF that doesn't set up a design system or components. This is what apps would use to truly
 * customise the MF for their needs via define.ts.
 */
let LoginHosted = class LoginHosted extends Login {
    registerCommonComponents() {
        return __awaiter(this, void 0, void 0, function* () {
            this.ready = true;
        });
    }
};
LoginHosted = __decorate([
    customElement(Object.assign(Object.assign({}, definition), { name: `${definition.name}-hosted` }))
], LoginHosted);
export { LoginHosted };
//# sourceMappingURL=main.js.map