"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.PROMPT_ME_DESIGNATOR = void 0;
const tslib_1 = require("tslib");
require("reflect-metadata");
const core_1 = require("@oclif/core");
const _ = require("lodash");
const contracts_1 = require("../../apis/codiac-api/contracts");
const command_base_exec_1 = require("../../command-base-exec");
const codiac_utils_1 = require("../../codiac-utils");
/** The value provided as input that indicates
 * a user's desire to be prompted with a list from which choose the value(s),
 * instead of entering them manually or taking a default. */
exports.PROMPT_ME_DESIGNATOR = "?";
class ConfigDeploy extends command_base_exec_1.CommandBaseExec {
    captureArgs() {
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            const { flags } = yield this.parse(ConfigDeploy);
            let partial = {};
            if (flags.enterpriseCode)
                partial.enterprise = flags.enterpriseCode;
            if (flags.asset)
                partial.assetName = flags.asset;
            if (flags.cabinetName)
                partial.cabinetName = flags.cabinetName;
            return [partial, false];
        });
    }
    buildCandidate_silently(partial, rootMap) {
        var _a;
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            if (!partial.cabinetName)
                throw new Error("Cabinet name (--cabinetName or -c) is required in silent mode.");
            if (!partial.assetName)
                throw new Error("Asset name (--asset or -a) is required in silent mode.");
            const entRoot = rootMap.enterprises.find(e => e.name === partial.enterprise);
            if (!entRoot)
                throw new Error(`Enterprise "${partial.enterprise}" not found.`);
            if (!entRoot.assets || entRoot.assets.length === 0)
                throw new Error(`Enterprise "${partial.enterprise}" has no assets.`);
            const helmFilter = (a) => { var _a; return ((_a = a.type) === null || _a === void 0 ? void 0 : _a.toString()) !== contracts_1.AssetTypesEnum.helm.toString(); };
            const asset = entRoot.assets.filter(helmFilter).find(a => a.name === partial.assetName);
            if (!asset)
                throw new Error(`Asset "${partial.assetName}" not found or not eligible for config deploy.`);
            const assetVersions = [{ a: asset.name }];
            let cabinet;
            for (const env of entRoot.environments) {
                const cab = env.cabinets.find(c => c.name === partial.cabinetName);
                if (cab) {
                    cabinet = cab;
                    break;
                }
            }
            if (!cabinet)
                throw new Error(`Cabinet "${partial.cabinetName}" not found in enterprise "${partial.enterprise}".`);
            const cabCntx = { enterprise: cabinet.enterprise, environment: cabinet.environment, cabinet: cabinet.name };
            const cluster = (_a = cabinet.cluster) !== null && _a !== void 0 ? _a : (rootMap.clusters.length === 1 ? rootMap.clusters[0].name : undefined);
            if (cluster) {
                yield this.establishCloudProviderCreds(rootMap, cluster, true);
            }
            else {
                this.logger.warn(`Cannot confirm cloud provider credentials: cabinet has no cluster specified.`);
            }
            return { enterprise: partial.enterprise, assetName: partial.assetName, cabinetName: partial.cabinetName, assetVersions, cabinet, cabCntx };
        });
    }
    buildCandidate_interactively(partial, rootMap, takeDefaults) {
        var _a;
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            const entRoot = rootMap.enterprises.find(e => e.name === partial.enterprise);
            if (!entRoot)
                throw new Error(`Enterprise "${partial.enterprise}" not found.`);
            if (!entRoot.assets || entRoot.assets.length === 0)
                throw new Error(`Enterprise "${partial.enterprise}" has no assets.`);
            const helmFilter = (a) => { var _a; return ((_a = a.type) === null || _a === void 0 ? void 0 : _a.toString()) !== contracts_1.AssetTypesEnum.helm.toString(); };
            // Select asset
            let assetVersions;
            if (partial.assetName === undefined || partial.assetName === exports.PROMPT_ME_DESIGNATOR) {
                const asset = yield this.promptForAsset(entRoot, undefined, helmFilter);
                assetVersions = [{ a: asset.name }];
            }
            else {
                const asset = yield this.promptForAsset(entRoot, partial.assetName, helmFilter);
                assetVersions = [{ a: asset.name }];
            }
            // Select cabinet
            const cabinet = yield this.selectCabinet(entRoot, rootMap, partial.cabinetName, undefined, assetVersions.map(a => a.a));
            const cabCntx = { enterprise: cabinet.enterprise, environment: cabinet.environment, cabinet: cabinet.name };
            // CSP credentials
            const cluster = (_a = cabinet.cluster) !== null && _a !== void 0 ? _a : (rootMap.clusters.length === 1 ? rootMap.clusters[0].name : undefined);
            if (cluster) {
                yield this.establishCloudProviderCreds(rootMap, cluster, false);
            }
            else {
                this.logger.warn(`Cannot confirm cloud provider credentials: cabinet has no cluster specified.`);
            }
            return { enterprise: partial.enterprise, assetName: partial.assetName, cabinetName: partial.cabinetName, assetVersions, cabinet, cabCntx };
        });
    }
    buildCommandString(candidate, cmd) {
        cmd.push("-e", candidate.enterprise);
        if (candidate.assetVersions.length > 0)
            cmd.push("-a", candidate.assetVersions[0].a);
        cmd.push("-c", candidate.cabinet.name);
        cmd.push("--silent");
        return cmd;
    }
    executeTask(candidate) {
        var _a;
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            const isInfrxEnterprise = codiac_utils_1.CodiacUtils.isInfrxEnterprise(candidate.enterprise);
            const target = isInfrxEnterprise
                ? "cluster ".concat(candidate.cabCntx.environment)
                : candidate.cabCntx.environment.concat("/", candidate.cabCntx.cabinet);
            yield this._ui.startSpinner("Deploying configs to ".concat(target, "..."));
            const response = yield this.genRelayClient.Call.postExecuteDeployConfigsFor({ destination: candidate.cabCntx, assets: candidate.assetVersions });
            yield this._ui.stopSpinner("done.");
            if (!response.success) {
                if (response.error)
                    throw response.error;
                throw new Error((_a = response.message) !== null && _a !== void 0 ? _a : "Config deploy failed.");
            }
        });
    }
    selectCabinet(enterprise, rootMap, userProvidedCabinetName, originalDefaultCabinet, assets) {
        var _a, _b, _c;
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            let embellisher;
            const deployedStateResult = yield this.genRelayClient.Call.getDeployedState({ enterprise: enterprise.name });
            if (deployedStateResult.output) {
                const isInfrxEnterprise = codiac_utils_1.CodiacUtils.isInfrxEnterprise(enterprise.name);
                embellisher = this.createEmbellisherForConfigDeploy(enterprise.name, deployedStateResult.output, isInfrxEnterprise, assets);
            }
            else {
                this.logger.debug("Failed loading deployed state: ".concat((_c = (_a = deployedStateResult.message) !== null && _a !== void 0 ? _a : (_b = deployedStateResult.error) === null || _b === void 0 ? void 0 : _b.message) !== null && _c !== void 0 ? _c : "(check logs for details)"), deployedStateResult.error);
            }
            const [cabinet] = yield this.rootMapUtils.promptForCabinet(enterprise.name, rootMap, originalDefaultCabinet, undefined, embellisher, userProvidedCabinetName);
            return cabinet;
        });
    }
    createEmbellisherForConfigDeploy(name, deployedState, isInfrxEnterprise, assets) {
        var _a;
        try {
            return {
                eachEnterprise: (entBranch, ent, rootMap) => { },
                eachEnviro: (envBranch, env, ent, rootMap) => { },
                eachCabinet: (cabBranch, cab, env, ent, rootMap) => {
                    var _a;
                    if (!assets || _.isEmpty(assets)) {
                        cabBranch.text = this._styler.dim(cabBranch.text);
                    }
                    else {
                        const envState = deployedState.environmentsAsArray.find(e => e.code === env.code);
                        const cabState = (_a = envState === null || envState === void 0 ? void 0 : envState.cabinets) === null || _a === void 0 ? void 0 : _a.find(c => c.name === cab.name);
                        const entVersion = (cabState === null || cabState === void 0 ? void 0 : cabState.version)
                            ? deployedState.enterpriseVersions.find(v => v.version === cabState.version)
                            : undefined;
                        if (entVersion === null || entVersion === void 0 ? void 0 : entVersion.contents) {
                            const found = this.extractAssetVersions(entVersion, assets);
                            if (found.length === 0) {
                                cabBranch.text = this._styler.dim(cabBranch.text);
                            }
                            else if (found.length === 1) {
                                const av = found[0];
                                cabBranch.text = cabBranch.text.concat(" (", av.a, av.v ? `:${av.v}` : "", ")");
                            }
                            else {
                                cabBranch.text = cabBranch.text.concat(" (", found.length.toString(), " assets)");
                            }
                        }
                    }
                }
            };
        }
        catch (err) {
            this.logger.warn("Error assembling cabinet tree embellisher for config deploy command: ".concat((_a = err.message) !== null && _a !== void 0 ? _a : "(no details)"), err);
            return undefined;
        }
    }
    extractAssetVersions(versionRef, assets) {
        const found = [];
        if (versionRef.contents) {
            const foundHere = versionRef.contents
                .filter(v => v.entityType === "asset" && assets.includes(v.entityName))
                .map(v => ({ a: v.entityName, v: v.version }));
            if (foundHere)
                found.push(...foundHere);
            for (const nonAsset of versionRef.contents.filter(v => v.entityType !== "asset")) {
                found.push(...this.extractAssetVersions(nonAsset, assets));
            }
        }
        return found;
    }
    promptForAsset(enterprise, assetName, assetSearch) {
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            if (!enterprise.assets || enterprise.assets.length === 0) {
                throw new Error(`The current enterprise [${enterprise.name}] has no assets defined.`);
            }
            let assets = enterprise.assets;
            if (assetName && assetName !== exports.PROMPT_ME_DESIGNATOR) {
                assets = assets.filter(a => a.name.startsWith(assetName));
                if (assets.length === 0)
                    throw new Error("No assets found with the given name(s).");
            }
            if (assetSearch) {
                assets = assets.filter(a => assetSearch(a));
                if (assets.length === 0) {
                    throw new Error(assetName ? "That asset does not qualify for deploying configs." : "No assets found that qualify for deploying configs.");
                }
            }
            if (assets.length === 1) {
                this._ui.userMessage("Asset: ".concat(this._styler.hilite(assets[0].name)));
                return assets[0];
            }
            const choices = assets.map(a => ({ name: a.name, value: a.name }));
            const assName = yield this._ui.promptSelectFuzzy('asset', 'Select an Asset:', () => tslib_1.__awaiter(this, void 0, void 0, function* () { return choices; }));
            if (!assName)
                throw new Error(`User failed to select a valid asset for a config deploy.`);
            return assets.find(a => a.name === assName);
        });
    }
}
exports.default = ConfigDeploy;
ConfigDeploy.description = 'Deploys a given asset config to a given cabinet.';
ConfigDeploy.flags = {
    help: core_1.Flags.help({ char: 'h' }),
    enterpriseCode: core_1.Flags.string({ char: 'e', required: false, description: 'Code name of the enterprise to target.' }),
    cabinetName: core_1.Flags.string({ char: 'c', required: false, description: 'Target cabinet name to deploy configs to.' }),
    asset: core_1.Flags.string({ char: 'a', required: false, description: `Name of the asset to deploy configs for (use ${exports.PROMPT_ME_DESIGNATOR} to be prompted).` }),
    version: core_1.Flags.string({ char: 'v', required: false, description: 'Version of the asset to deploy configs for.' }),
    silent: core_1.Flags.boolean({ exclusive: ["echo", "to-script"], description: 'Executes without any user interaction; fails on missing or invalid arguments.' }),
    echo: core_1.Flags.boolean({ exclusive: ["to-script", "silent"], description: 'Renders the equivalent non-interactive command for future use before executing.' }),
    "to-script": core_1.Flags.boolean({ exclusive: ["echo", "silent"], description: 'Renders the equivalent non-interactive command without executing it.' }),
};
ConfigDeploy.args = {};
ConfigDeploy.examples = [
    { description: "Deploy configs interactively.", command: "<%= config.bin %> <%= command.id %>" },
    { description: "Deploy configs for a specific asset.", command: "<%= config.bin %> <%= command.id %> -e myEnterprise -a myApi -c dev" },
    { description: "Non-interactive (scripted) invocation.", command: "<%= config.bin %> <%= command.id %> -e myEnterprise -a myApi -c dev --silent" },
    { description: "Renders the equivalent silent command without executing.", command: "<%= config.bin %> <%= command.id %> -e myEnterprise -a myApi -c dev --to-script" },
];
//# sourceMappingURL=deploy.js.map