"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
    if (mod && mod.__esModule) return mod;
    var result = {};
    if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
    __setModuleDefault(result, mod);
    return result;
};
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.VcdAuth = exports.UiEmulatorUtils = void 0;
const path_1 = __importDefault(require("path"));
const fs_1 = __importDefault(require("fs"));
const child_process_1 = require("child_process");
const node_client_1 = require("@vcd/node-client");
const inquirer_1 = __importDefault(require("inquirer"));
const cp = __importStar(require("child_process"));
const chokidar = __importStar(require("chokidar"));
class UiEmulatorUtils {
    static loadJsonConfig(rootDir, name) {
        const jsonPath = path_1.default.resolve(rootDir, name);
        if (!fs_1.default.existsSync(jsonPath)) {
            return null;
        }
        const fileContent = fs_1.default.readFileSync(jsonPath).toString();
        return JSON.parse(fileContent);
    }
    static storeJsonConfig(rootDir, name, content) {
        const jsonPath = path_1.default.resolve(rootDir, name);
        fs_1.default.writeFileSync(jsonPath, JSON.stringify(content, null, 2));
    }
    static discoverPluginModule(packageRoot, elementsRootRelativePath, elementFolderName) {
        const srcRoot = path_1.default.join(elementFolderName, 'src');
        return {
            elementsRootRelativePath,
            srcRoot,
            elementFolderName,
        };
    }
    /**
     * Configures and serves the ui-emulator angular application, hosting provided ui plugin elements.
     * @param uiEmulatorConfigRoot - absolute root path to ui plugin folder
     * @param elementsRootRelativePath - relative path to root dir containing all plugin folders
     * @param elementFolderNames - all ui plugin folders
     * @param vcdConfig - VCD Configuration object
     * @param serveArgs - Arguments passed to ng serve
     */
    static serve(uiEmulatorConfigRoot, elementsRootRelativePath, elementFolderNames, vcdConfig, serveArgs) {
        return __awaiter(this, void 0, void 0, function* () {
            const rootDir = path_1.default.join(uiEmulatorConfigRoot, '.env'); // Extract as optional parameter?
            const emulatorPath = "node_modules/@vcd/ui-emulator";
            const emulatorRoot = path_1.default.resolve(emulatorPath);
            const angularJson = this.loadJsonConfig(emulatorRoot, 'angular.json');
            const tsconfigJson = this.loadJsonConfig(emulatorRoot, 'tsconfig.app.json');
            const environment = this.loadJsonConfig(rootDir, 'environment.json');
            const proxyConfig = this.loadJsonConfig(rootDir, 'proxy.conf.json');
            const argIndexPort = serveArgs.findIndex((argument) => {
                return argument === "--port";
            });
            let defaultDevServerPort = "4200";
            if (argIndexPort !== -1) {
                const serverPort = serveArgs[argIndexPort + 1];
                defaultDevServerPort = serverPort;
            }
            const bootstrapDetails = {
                "devMode": false,
                "locale": vcdConfig.locale || "en",
                "scope": vcdConfig.scope,
                "builtInExtensions": [],
                "thirdPartyExtensions": {
                    "hyperplugin": "/assets/hyperplugin",
                    "hyperplugin2": "/assets/hyperplugin2",
                },
                "cell": {
                    "address": `http://localhost:${defaultDevServerPort}`,
                    "organization": vcdConfig.orgName,
                },
                "flexUrl": "",
            };
            let pluginModules = [];
            try {
                console.log('Setting auth token');
                environment.credentials = {
                    jwt: vcdConfig.jwt,
                };
                if (vcdConfig.authtoken) {
                    environment.credentials.authtoken = vcdConfig.authtoken;
                }
                console.log('Updating proxy config');
                Object.keys(proxyConfig).forEach(key => {
                    proxyConfig[key].target = vcdConfig.cellUrl;
                });
                console.log('Updating Plugins');
                if (vcdConfig.orgId) {
                    bootstrapDetails.cell["orgId"] = vcdConfig.orgId;
                }
                pluginModules = elementFolderNames.map((element) => {
                    bootstrapDetails.thirdPartyExtensions[element] = `/assets/${element}`;
                    return this.discoverPluginModule(uiEmulatorConfigRoot, elementsRootRelativePath, element);
                });
                console.log('Updating angular.json');
                angularJson.projects.emulator.sourceRoot = `${emulatorPath}/src`;
                angularJson.projects.emulator.architect.build.options.index = `${emulatorPath}/src/index.html`;
                angularJson.projects.emulator.architect.build.options.main = `${emulatorPath}/src/main.ts`;
                angularJson.projects.emulator.architect.build.options.tsConfig = "tsconfig.emulator.json";
                angularJson.projects.emulator.architect.build.options.styles = [
                    "node_modules/@clr/ui/clr-ui.min.css",
                    "node_modules/@clr/icons/clr-icons.min.css",
                    "node_modules/@vcd/ui-emulator/src/styles.scss"
                ];
                angularJson.projects.emulator.architect.serve.configurations.development = {
                    "browserTarget": "emulator:build",
                    "proxyConfig": ".env/proxy.conf.runtime.json"
                };
                angularJson.projects.emulator.architect.build.options.assets = [
                    `${emulatorPath}/src/favicon.ico`,
                    `${emulatorPath}/src/assets`,
                    ...pluginModules.map((pm) => {
                        return {
                            glob: '**/*',
                            input: `./${path_1.default.join(pm.elementsRootRelativePath, pm.srcRoot, "../dist")}`,
                            output: `assets/${pm.elementFolderName}`
                        };
                    })
                ];
                angularJson.projects.emulator.architect.build.options.fileReplacements = [
                    {
                        "replace": "node_modules/@vcd/ui-emulator/.env/environment.runtime.json",
                        "with": ".env/environment.runtime.json"
                    }
                ];
                console.log('Updating tsconfig.emulator.json');
                tsconfigJson.extends = `./${emulatorPath}/tsconfig.json`;
                tsconfigJson.files = [
                    `${emulatorPath}/src/main.ts`
                ];
                tsconfigJson.include = [
                    '.env/*.json',
                    `${emulatorPath}/src/**/*.ts`,
                ];
                tsconfigJson.exclude = [
                    `${emulatorPath}/src/assets`,
                ];
                const windowBootstrapDetails = `
                window["_bootstrapDetails"] = ${JSON.stringify(bootstrapDetails, null, 4)}
            `.trim();
                fs_1.default.writeFileSync(path_1.default.resolve(emulatorRoot, "src", "bootstraDetails.js"), windowBootstrapDetails);
            }
            catch (e) {
                console.log('Error configuring environment.', e);
            }
            finally {
                this.storeJsonConfig(rootDir, 'environment.runtime.json', environment);
                this.storeJsonConfig(rootDir, 'proxy.conf.runtime.json', proxyConfig);
                this.storeJsonConfig(uiEmulatorConfigRoot, 'angular.json', angularJson);
                this.storeJsonConfig(uiEmulatorConfigRoot, 'tsconfig.emulator.json', tsconfigJson);
            }
            const uiPluginBuilds = pluginModules.map((pluginModule) => {
                const pluginSrcRoot = path_1.default.resolve(path_1.default.join(pluginModule.elementsRootRelativePath, pluginModule.srcRoot));
                const pluginRootDir = path_1.default.resolve(path_1.default.join(pluginSrcRoot, "./../"));
                return this.buildUIPlugin(pluginRootDir);
            });
            Promise.all(uiPluginBuilds)
                .then(() => {
                (0, child_process_1.spawn)('ng', ['serve', ...(serveArgs ? serveArgs : [])], {
                    cwd: uiEmulatorConfigRoot,
                    stdio: 'inherit'
                });
                pluginModules.forEach((pluginModule) => {
                    const pluginSrcRoot = path_1.default.resolve(path_1.default.join(pluginModule.elementsRootRelativePath, pluginModule.srcRoot));
                    console.log(`Watching ${pluginSrcRoot} for changes`);
                    this.watchUIPluginsSources(pluginSrcRoot);
                });
            });
            return Promise.resolve();
        });
    }
    static watchUIPluginsSources(pluginSrcRoot, skipFirstEvent = true) {
        const watcher = chokidar.watch(pluginSrcRoot, { ignored: /^\./, persistent: true });
        const pluginRootDir = path_1.default.resolve(path_1.default.join(pluginSrcRoot, "./../"));
        let child_process = null;
        watcher.on("all", (event, path) => {
            if (child_process || skipFirstEvent) {
                skipFirstEvent = false;
                return;
            }
            child_process = cp.spawn("npm", ["run", "build"], { cwd: pluginRootDir, stdio: "inherit" });
            child_process.on('close', (code) => {
                console.log(`Build finished with code: ${code}`);
                child_process = null;
            });
        });
    }
    static buildUIPlugin(pluginRootDir) {
        return new Promise((resolve, reject) => {
            const child_process = cp.spawn("npm", ["run", "build"], { cwd: pluginRootDir, stdio: "inherit" });
            child_process.on('close', (code) => {
                console.log(`Build finished with code: ${code}`);
                resolve();
            });
        });
    }
}
exports.UiEmulatorUtils = UiEmulatorUtils;
class VcdAuth {
    /**
     * Prompts the user to select particular VCD auth configuration from the store
     * @param vcdAuthFile - path to the VCD authentication store file
     */
    static use(vcdAuthFile) {
        return __awaiter(this, void 0, void 0, function* () {
            const configs = node_client_1.CloudDirectorConfig.getConfigurations(vcdAuthFile);
            if (configs.configurations.length <= 0) {
                console.log("No VCD Authentication Configurations stored yet!");
                return;
            }
            const answers = yield inquirer_1.default.prompt({
                type: 'list',
                name: 'alias',
                message: 'Select configuration',
                default: configs.current,
                loop: false,
                choices: configs.configurations.map((element) => {
                    return {
                        name: `${element.key}: ${element.username}/${element.org} ${element.basePath}`,
                        value: element.key
                    };
                }),
            });
            node_client_1.CloudDirectorConfig.use(answers.alias, vcdAuthFile);
            return yield this.getCloudDirectorConfig(vcdAuthFile);
        });
    }
    /**
     * Returns current VCD Auth configuration
     * @param vcdAuthFile - path to the VCD authentication store file
     */
    static getCloudDirectorConfig(vcdAuthFile) {
        return __awaiter(this, void 0, void 0, function* () {
            let config = node_client_1.CloudDirectorConfig.fromFile(vcdAuthFile);
            if (!config.connectionAuth.authorized && config.connectionAuth.authorizationError === 'Token expired') {
                const answers = yield inquirer_1.default.prompt({
                    type: 'password',
                    name: 'password',
                    message: `Token has expired. Please enter the password for ${config.authentication.username}@${config.authentication.org} again: `,
                });
                const password = answers.password;
                config = yield this.loginAndStore(node_client_1.CloudDirectorConfig.getConfigurations().current, config.basePath, config.authentication.username, config.authentication.org, password, vcdAuthFile);
            }
            return config;
        });
    }
    /**
     * Creates a new VCD Auth configuration and stores it VCD authentication store file
     * @param alias - alias use for storing
     * @param vcdHost - Cloud director host
     * @param user - username
     * @param org - organization
     * @param password - password
     * @param vcdAuthFile - path to the VCD authentication store file
     */
    static loginAndStore(alias, vcdHost, user, org, password, vcdAuthFile) {
        return __awaiter(this, void 0, void 0, function* () {
            const config = yield node_client_1.CloudDirectorConfig.withUsernameAndPassword(vcdHost, user, org, password);
            if (!config.connectionAuth.authorized) {
                console.warn('Connection error: ' + config.connectionAuth.authorizationError);
                console.log(config.connectionAuth.certificate);
                const answers = yield inquirer_1.default.prompt({
                    type: 'confirm',
                    name: 'accept',
                    message: 'Do you accept the provided certificate?',
                });
                config.connectionAuth.authorized = answers.accept;
            }
            config.saveConfig(alias, vcdAuthFile);
            return config;
        });
    }
}
exports.VcdAuth = VcdAuth;
