"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
    if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
var AdminUiPlugin_1;
Object.defineProperty(exports, "__esModule", { value: true });
exports.AdminUiPlugin = void 0;
const shared_constants_1 = require("@vendx/common/lib/shared-constants");
const core_1 = require("@vendx/core");
const express_1 = __importDefault(require("express"));
const fs_extra_1 = __importDefault(require("fs-extra"));
const path_1 = __importDefault(require("path"));
const constants_1 = require("./constants");
/**
 * @description
 * This plugin starts a static server for the Admin UI app, and proxies it via the `/admin/` path of the main Vend˟ server.
 *
 * The Admin UI allows you to administer all aspects of your store, from inventory management to order tracking. It is the tool used by
 * store administrators on a day-to-day basis for the management of the store.
 *
 * ## Installation
 *
 * `yarn add \@vendx/admin-ui-plugin`
 *
 * or
 *
 * `npm install \@vendx/admin-ui-plugin`
 *
 * @example
 * ```ts
 * import { AdminUiPlugin } from '\@vendx/admin-ui-plugin';
 *
 * const config: VendxConfig = {
 *   // Add an instance of the plugin to the plugins array
 *   plugins: [
 *     AdminUiPlugin.init({ port: 3002 }),
 *   ],
 * };
 * ```
 *
 * @docsCategory AdminUiPlugin
 */
let AdminUiPlugin = AdminUiPlugin_1 = class AdminUiPlugin {
    constructor(configService) {
        this.configService = configService;
    }
    /**
     * @description
     * Set the plugin options
     */
    static init(options) {
        this.options = options;
        return AdminUiPlugin_1;
    }
    /** @internal */
    static async configure(config) {
        const route = 'admin';
        const { app } = this.options;
        const appWatchMode = this.isDevModeApp(app);
        let port;
        if (this.isDevModeApp(app)) {
            port = app.port;
        }
        else {
            port = this.options.port;
        }
        config.apiOptions.middleware.push({
            handler: core_1.createProxyHandler({
                hostname: this.options.hostname,
                port,
                route: 'admin',
                label: 'Admin UI',
                basePath: appWatchMode ? 'admin' : undefined,
            }),
            route,
        });
        if (this.isDevModeApp(app)) {
            config.apiOptions.middleware.push({
                handler: core_1.createProxyHandler({
                    hostname: this.options.hostname,
                    port,
                    route: 'sockjs-node',
                    label: 'Admin UI live reload',
                    basePath: 'sockjs-node',
                }),
                route: 'sockjs-node',
            });
        }
        return config;
    }
    /** @internal */
    async onVendxBootstrap() {
        const { apiHost, apiPort, port, app, adminUiConfig } = AdminUiPlugin_1.options;
        // TODO: Remove in next minor version (0.11.0)
        if (apiHost || apiPort) {
            core_1.Logger.warn(`The "apiHost" and "apiPort" options are deprecated and will be removed in a future version.`, constants_1.loggerCtx);
            core_1.Logger.warn(`Use the "adminUiConfig.apiHost", "adminUiConfig.apiPort" properties instead.`, constants_1.loggerCtx);
        }
        const adminUiAppPath = AdminUiPlugin_1.isDevModeApp(app)
            ? path_1.default.join(app.sourcePath, 'src')
            : (app && app.path) || constants_1.DEFAULT_APP_PATH;
        const adminUiConfigPath = path_1.default.join(adminUiAppPath, 'vendx-ui-config.json');
        const overwriteConfig = () => {
            const uiConfig = this.getAdminUiConfig(adminUiConfig);
            return this.overwriteAdminUiConfig(adminUiConfigPath, uiConfig);
        };
        if (!AdminUiPlugin_1.isDevModeApp(app)) {
            // If not in dev mode, start a static server for the compiled app
            const adminUiServer = express_1.default();
            adminUiServer.use(express_1.default.static(adminUiAppPath));
            adminUiServer.use((req, res) => {
                res.sendFile(path_1.default.join(adminUiAppPath, 'index.html'));
            });
            this.server = adminUiServer.listen(AdminUiPlugin_1.options.port);
            if (app && typeof app.compile === 'function') {
                core_1.Logger.info(`Compiling Admin UI app in production mode...`, constants_1.loggerCtx);
                app.compile()
                    .then(overwriteConfig)
                    .then(() => {
                    core_1.Logger.info(`Admin UI successfully compiled`, constants_1.loggerCtx);
                }, (err) => {
                    core_1.Logger.error(`Failed to compile: ${err}`, constants_1.loggerCtx, err.stack);
                });
            }
            else {
                await overwriteConfig();
            }
        }
        else {
            core_1.Logger.info(`Compiling Admin UI app in development mode`, constants_1.loggerCtx);
            app.compile().then(() => {
                core_1.Logger.info(`Admin UI compiling and watching for changes...`, constants_1.loggerCtx);
            }, (err) => {
                core_1.Logger.error(`Failed to compile: ${err}`, constants_1.loggerCtx, err.stack);
            });
            await overwriteConfig();
        }
    }
    /** @internal */
    async onVendxClose() {
        if (this.server) {
            await new Promise(resolve => this.server.close(() => resolve()));
        }
    }
    /**
     * Takes an optional AdminUiConfig provided in the plugin options, and returns a complete
     * config object for writing to disk.
     */
    getAdminUiConfig(partialConfig) {
        var _a, _b, _c, _d;
        const { authOptions } = this.configService;
        const propOrDefault = (prop, defaultVal) => {
            return partialConfig ? partialConfig[prop] || defaultVal : defaultVal;
        };
        return {
            adminApiPath: propOrDefault('adminApiPath', this.configService.apiOptions.adminApiPath),
            apiHost: propOrDefault('apiHost', AdminUiPlugin_1.options.apiHost || 'auto'),
            apiPort: propOrDefault('apiPort', AdminUiPlugin_1.options.apiPort || 'auto'),
            tokenMethod: propOrDefault('tokenMethod', authOptions.tokenMethod || 'cookie'),
            authTokenHeaderKey: propOrDefault('authTokenHeaderKey', authOptions.authTokenHeaderKey || shared_constants_1.DEFAULT_AUTH_TOKEN_HEADER_KEY),
            defaultLanguage: propOrDefault('defaultLanguage', constants_1.defaultLanguage),
            availableLanguages: propOrDefault('availableLanguages', constants_1.defaultAvailableLanguages),
            loginUrl: (_a = AdminUiPlugin_1.options.adminUiConfig) === null || _a === void 0 ? void 0 : _a.loginUrl,
            brand: (_b = AdminUiPlugin_1.options.adminUiConfig) === null || _b === void 0 ? void 0 : _b.brand,
            hideVendxBranding: propOrDefault('hideVendxBranding', ((_c = AdminUiPlugin_1.options.adminUiConfig) === null || _c === void 0 ? void 0 : _c.hideVendxBranding) || false),
            hideVersion: propOrDefault('hideVersion', ((_d = AdminUiPlugin_1.options.adminUiConfig) === null || _d === void 0 ? void 0 : _d.hideVersion) || false),
        };
    }
    /**
     * Overwrites the parts of the admin-ui app's `vendx-ui-config.json` file relating to connecting to
     * the server admin API.
     */
    async overwriteAdminUiConfig(adminUiConfigPath, config) {
        try {
            const content = await this.pollForConfigFile(adminUiConfigPath);
        }
        catch (e) {
            core_1.Logger.error(e.message, constants_1.loggerCtx);
            throw e;
        }
        try {
            await fs_extra_1.default.writeFile(adminUiConfigPath, JSON.stringify(config, null, 2));
        }
        catch (e) {
            throw new Error('[AdminUiPlugin] Could not write vendx-ui-config.json file:\n' + e.message);
        }
        core_1.Logger.verbose(`Applied configuration to vendx-ui-config.json file`, constants_1.loggerCtx);
    }
    /**
     * It might be that the ui-devkit compiler has not yet copied the config
     * file to the expected location (particularly when running in watch mode),
     * so polling is used to check multiple times with a delay.
     */
    async pollForConfigFile(adminUiConfigPath) {
        const maxRetries = 10;
        const retryDelay = 200;
        let attempts = 0;
        const pause = () => new Promise(resolve => setTimeout(resolve, retryDelay));
        while (attempts < maxRetries) {
            try {
                core_1.Logger.verbose(`Checking for config file: ${adminUiConfigPath}`, constants_1.loggerCtx);
                const configFileContent = await fs_extra_1.default.readFile(adminUiConfigPath, 'utf-8');
                return configFileContent;
            }
            catch (e) {
                attempts++;
                core_1.Logger.verbose(`Unable to locate config file: ${adminUiConfigPath} (attempt ${attempts})`, constants_1.loggerCtx);
            }
            await pause();
        }
        throw new Error(`Unable to locate config file: ${adminUiConfigPath}`);
    }
    static isDevModeApp(app) {
        if (!app) {
            return false;
        }
        return !!app.sourcePath;
    }
};
AdminUiPlugin = AdminUiPlugin_1 = __decorate([
    core_1.VendxPlugin({
        imports: [core_1.PluginCommonModule],
        providers: [],
        configuration: config => AdminUiPlugin_1.configure(config),
    }),
    __metadata("design:paramtypes", [core_1.ConfigService])
], AdminUiPlugin);
exports.AdminUiPlugin = AdminUiPlugin;
//# sourceMappingURL=plugin.js.map