"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 inquirer = require("inquirer");
const _ = require("lodash");
const command_base_exec_1 = require("../../command-base-exec");
const config_ui_utils_1 = require("../../uilogic/config-ui-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 AssetRecycle extends command_base_exec_1.CommandBaseExec {
    captureArgs() {
        var _a, _b;
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            const { args, flags } = yield this.parse(AssetRecycle);
            const partial = {};
            if (flags.enterpriseCode) {
                partial.enterprise = flags.enterpriseCode;
            }
            if (flags.asset) {
                partial.assetNames = [flags.asset];
            }
            if (flags.cabinetName) {
                partial.cabinetName = flags.cabinetName;
            }
            // ---------------------------------------------------------------
            // TODO: move everything from here down into the base class
            // ---------------------------------------------------------------
            const interactionMode = (this.pipedInput) ? config_ui_utils_1.InteractionModes.silent : (_b = (_a = undefined !== null && undefined !== void 0 ? undefined : (flags.echo ? config_ui_utils_1.InteractionModes.echo : undefined)) !== null && _a !== void 0 ? _a : (flags["to-script"] ? config_ui_utils_1.InteractionModes.toscript : undefined)) !== null && _b !== void 0 ? _b : (flags.silent ? config_ui_utils_1.InteractionModes.silent : undefined);
            const takeDefaults = flags['take-defaults'];
            return [partial, interactionMode, takeDefaults];
        });
    }
    buildCandidate_silently(partial, rootMap) {
        var _a;
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            try {
                const ent = rootMap.enterprises.find(e => e.name == partial.enterprise); // assertion operator ok bc base class ensures it (or throws)
                if (partial.assetNames == undefined || _.isEmpty(partial.assetNames)) {
                    throw new Error("You must specify asset.");
                }
                else {
                    for (let asset of partial.assetNames) {
                        if (!ent.assets.map(a => a.name).includes(asset)) {
                            throw new Error("No asset was found with that name.");
                        }
                    }
                }
                if (partial.cabinetName == undefined || _.isEmpty(partial.cabinetName)) {
                    throw new Error("You must specify the cabinet.");
                }
                const env = this.rootMapUtils.deriveEnvironmentFrom(partial.enterprise, partial.cabinetName, rootMap);
                partial.environment = env.name;
                let candidate = {
                    enterprise: partial.enterprise,
                    environment: env.name,
                    cabinetName: partial.cabinetName,
                    assetNames: partial.assetNames
                };
                return candidate;
            }
            catch (err) {
                err.message = "Invalid command input: ".concat((_a = err.message) !== null && _a !== void 0 ? _a : "(messageless error)");
                throw err;
            }
        });
    }
    buildCandidate_interactively(partial, rootMap, takeDefaults) {
        var _a;
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            const ent = rootMap.enterprises.find(e => e.name == partial.enterprise); // assertion operator ok bc base class ensures it (or throws)
            // ASSET
            if (partial.assetNames != undefined && !_.isEmpty(partial.assetNames)) {
                // validate
                const requestedAsset = partial.assetNames[0];
                if (!ent.assets.find(a => a.name == requestedAsset)) {
                    delete partial.assetNames;
                }
            }
            if (partial.assetNames == undefined || _.isEmpty(partial.assetNames)) {
                // Select if missing
                let asset = yield this.promptForAsset(partial.enterprise);
                partial.assetNames = [asset.name];
            }
            // CABINET/ENV
            let cab;
            if (partial.cabinetName != undefined && !_.isEmpty(partial.cabinetName)) {
                // validate
                try {
                    const env = this.rootMapUtils.deriveEnvironmentFrom(partial.enterprise, partial.cabinetName, rootMap);
                    partial.environment = env.name;
                    cab = env.cabinets.find(c => c.name == partial.cabinetName); // assertion ok bc the stmt above ensures it (or throws).
                }
                catch (err) {
                    // we dont want to throw here - tell em we coudnt find it and prompt for another instead.
                    delete partial.cabinetName;
                    this._ui.userMessage(err.message);
                }
            }
            if (cab == undefined || partial.cabinetName == undefined || partial.environment == undefined) {
                // Select if missing
                [cab] = yield this.rootMapUtils.promptForCabinet(partial.enterprise, rootMap, partial.cabinetName, false, undefined);
                partial.environment = cab.environment;
                partial.cabinetName = cab.name;
            }
            // Get the clusterdef so we can get cloud pvd creds
            let cluster = (_a = cab.cluster) !== null && _a !== void 0 ? _a : ((rootMap.clusters.length === 1) ? rootMap.clusters[0].name : undefined); // fall back to the one and only.
            if (cluster === undefined) {
                this.logger.warn(`Cannot confirm cloud provider credentials: cabinet has no cluster specified. Process will rely on default credentials.`);
            }
            else {
                yield this.establishCloudProviderCreds(rootMap, cluster);
            }
            let candidate = {
                enterprise: partial.enterprise,
                environment: partial.environment,
                cabinetName: partial.cabinetName,
                assetNames: partial.assetNames
            };
            return candidate;
        });
    }
    buildCommandString(candidate, cmd) {
        cmd.push("-e", candidate.enterprise);
        cmd.push("-c", candidate.cabinetName);
        // This should only ever be a single asset in this command 
        // (Later we can still add support for multiple assets by allowing multi on these args)
        cmd.push("-a", candidate.assetNames[0]);
        cmd.push("--silent");
        return cmd;
    }
    executeTask(candidate) {
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            this._ui.startSpinner(`Recycling assets in ${candidate.cabinetName}...`);
            // let result = await this.relayClient.recycleAssets(assets);
            const req = Object.assign(Object.assign({}, _.omit(candidate, "enterprise")), { enterpriseCode: candidate.enterprise });
            let result = yield this.genRelayClient.Call.postExecuteRecycleAssets(req);
            this._ui.stopSpinner((result.success === true) ? "SUCCESS" : (result.error) ? "ERROR" : "FAILED");
            if (result.error) {
                this.logger.error(`Error recycling assets in ${candidate.cabinetName}: ${result.message}`, result.error);
                this._ui.userMessage(`Error recycling assets in ${candidate.cabinetName}: ${result.message}`);
            }
            else if (result.success) {
                this.logger.notice(`Recycled assets in ${candidate.cabinetName}`);
                // this._ui.userMessage(`Recycled assets in ${assets.cabinetName}`);  // The spinner is enough for the user message here.
            }
            else {
                this.logger.warn(`Failed recycling assets in ${candidate.cabinetName}: ${result.message}`, result);
                this._ui.userMessage(`Failed recycling assets in ${candidate.cabinetName}: ${result.message}`);
            }
            return;
        });
    }
    // async run() {
    //     return new Promise<void>(async (resolve, reject) => {
    //         try {
    //             this.bootstrap();
    //             const { args, flags } = await this.parse(AssetRecycle);
    //             this._relayContainer?.ready$
    //                 .subscribe(async () => {
    //                     this._userCntx = await this.establishTenantContext(this._userCntx);
    //                     let rootMap = await this.getRootMap();
    //                     const interactionMode = this.getInteractionMode(flags.silent, flags.echo, flags['to-script']);
    //                     let enterprise = (await this.establishEnterpriseContext(rootMap, flags.enterpriseCode, interactionMode)).enterprise;
    //                     _.remove(rootMap.enterprises, e => e.name != enterprise.name);  // trim down to match legacy conditions
    //                     let enterpriseCode = enterprise.name;
    //                     // // Select the asset(s) to deploy
    //                     // let assetVersions: AssetVersion[];
    //                     // if (flags.asset == PROMPT_ME_DESIGNATOR) {
    //                     //     let asset = await this.promptForAsset(enterpriseCode);
    //                     //     // let selected = await this.promptForVersion(enterpriseCode, asset, flags.version);
    //                     //     assetVersions = [{ a: asset.name }];
    //                     // } else if (flags.asset) {
    //                     //     let asset = await this.promptForAsset(enterpriseCode, flags.asset);
    //                     //     // let selected = await this.promptForVersion(enterpriseCode, asset, flags.version);
    //                     //     assetVersions = [{ a: asset.name }]
    //                     //     // if (!flags.version || flags.version == PROMPT_ME_DESIGNATOR) {
    //                     //     // }
    //                     // } else {
    //                     //     // Update sync codiac with what we've got here in the project.
    //                     //     let requestedAssets = await this.establishAssetContext(enterprise, flags.silent);
    //                     //     assetVersions = requestedAssets.map(a => { return { a: a } });
    //                     //     // let versions = await this.declareAssetVersions(requestedAssets, enterpriseCode);
    //                     //     // // We've updated Codiac.  Now trigger the deploy.
    //                     //     // assetVersions = requestedAssets.map(a => {
    //                     //     //   let version = versions.find(v => v.assetName == a);
    //                     //     //   return { a: a, v: version ? version.assetVersion : undefined };
    //                     //     // });
    //                     // }
    //                     // let cab: CabinetClusterRoot;
    //                     // [cab] = await this.rootMapUtils.promptForCabinet(enterpriseCode, rootMap, flags.cabinetName, false, undefined);
    //                     // // let cabinet = await this.establishCabinet(flags.cabinetName);
    //                     // let cabCntx: CabinetScope = { enterprise: cab.enterprise, environment: cab.environment, cabinet: cab.name };
    //                     // // Get the clusterdef so we can get cloud pvd creds
    //                     // let cluster = cab.cluster ?? ((rootMap.clusters.length === 1) ? rootMap.clusters[0].name : undefined);  // fall back to the one and only.
    //                     // if (cluster === undefined) {
    //                     //     this.logger.warn(`Cannot confirm cloud provider credentials: cabinet has no cluster specified. Process will rely on cached credentials.`);
    //                     //     // throw new Error(`Cannot determine cloud provider: cabinet has no cluster specified.`);
    //                     // } else {
    //                     //     await this.establishCloudProviderCreds(rootMap, cluster);
    //                     // }
    //                     // let assets: CabinetAssetList = {
    //                     //     enterpriseCode: cabCntx.enterprise,
    //                     //     environment: cabCntx.environment,
    //                     //     cabinetName: cabCntx.cabinet,
    //                     //     assetNames: assetVersions.map(a => a.a)
    //                     // };
    //                     // this._ui.startSpinner(`Recycling assets in ${assets.cabinetName}...`);
    //                     // // let result = await this.relayClient.recycleAssets(assets);
    //                     // let result = await this.genRelayClient.Call.postExecuteRecycleAssets(assets);
    //                     // this._ui.stopSpinner((result.success === true) ? "SUCCESS" : (result.error) ? "ERROR" : "FAILED");
    //                     // if (result.error) {
    //                     //     this.logger.error(`Error recycling assets in ${assets.cabinetName}: ${result.message}`, result.error);
    //                     //     this._ui.userMessage(`Error recycling assets in ${assets.cabinetName}: ${result.message}`);
    //                     // } else if (result.success) {
    //                     //     this.logger.notice(`Recycled assets in ${assets.cabinetName}`);
    //                     //     // this._ui.userMessage(`Recycled assets in ${assets.cabinetName}`);  // The spinner is enough for the user message here.
    //                     // } else {
    //                     //     this.logger.warn(`Failed recycling assets in ${assets.cabinetName}: ${result.message}`, result);
    //                     //     this._ui.userMessage(`Failed recycling assets in ${assets.cabinetName}: ${result.message}`);
    //                     // }
    //                     resolve();
    //                 })
    //         } catch (err) {
    //             this.handleError(err);
    //             reject(err);
    //         } finally {
    //             await this.closeConnections();
    //         }
    //     });
    // }
    promptForAsset(enterprise, assetSearch) {
        return new Promise((resolve, reject) => tslib_1.__awaiter(this, void 0, void 0, function* () {
            try {
                // let ecrit = new EnterpriseCriteria();
                // ecrit.code = new StringFilter();
                // ecrit.code.$eq = enterprise;
                let ecrit = { code: enterprise };
                let readResult = yield this._codiacApi.Enterprise.tryReadMany(ecrit /* { code: { $eq: enterprise } }  */);
                if (!readResult.output || readResult.output.length == 0) {
                    throw new Error(`Could not find enterprise [${enterprise}].`);
                }
                else if (!readResult.output[0].assets || readResult.output[0].assets.length == 0) {
                    throw new Error(`The current enterprise [${enterprise}] has no assets defined.`);
                }
                // console.log(`${readResult.output[0].assets.length} assets found`)
                // Select from assets in the current enterprise
                let assets = readResult.output[0].assets;
                if (assetSearch && assetSearch != exports.PROMPT_ME_DESIGNATOR) {
                    assets = assets.filter(a => a.name.startsWith(assetSearch));
                }
                // console.log(`${assets.length} assets found`)
                let assSelect = yield inquirer.prompt({ type: 'list', name: 'asset', message: 'Select an Asset:', choices: assets.map(a => a.name) });
                let selected = assets.find(a => a.name == assSelect.asset);
                if (!selected)
                    throw new Error(`User failed to select a valid asset to deploy.`);
                resolve(selected);
            }
            catch (err) {
                reject(err);
            }
        }));
    }
}
exports.default = AssetRecycle;
AssetRecycle.description = 'Deploys a given asset to a given cabinet.  Defaults to current version.';
AssetRecycle.dependsOn = [/* "enterpriseCode", */ "cabinetName"];
AssetRecycle.flags = {
    enterpriseCode: core_1.Flags.string({ char: 'e', required: false, description: `Code (name) of the enterprise to deploy` }),
    cabinetName: core_1.Flags.string({ char: 'c', required: false, description: `Target cabinet name you want deploy` }),
    asset: core_1.Flags.string({ char: 'a', required: false, description: `Search string for the name of the asset to deploy (use ${exports.PROMPT_ME_DESIGNATOR} to be prompted).` }),
    // TODO: Move these into the base class
    silent: core_1.Flags.boolean({ dependsOn: AssetRecycle.dependsOn, exclusive: ["echo", "to-script"], description: 'Non-Interactive mode; executes without any user interaction and fails on any missing or invalid arguments.' }),
    echo: core_1.Flags.boolean({ exclusive: ["to-script", "silent"], description: 'Echo interaction mode; renders the equivalent non-interactive cli command for future use before final execution.' }),
    "to-script": core_1.Flags.boolean({ exclusive: ["echo", "silent"], description: 'Toscript interaction mode; renders the equivalent non-interactive cli command WITHOUT any execution.' }),
    "take-defaults": core_1.Flags.boolean({ /* char: 'y', */ exclusive: ["silent"], description: "Setting this to true prevents prompting for confirmation on parameters that were passed in or were automatically set to default values (Irrelevant in --silent mode).  This behavior does NOT apply to scenarios that the command needs to override invalid user input; such cases will always prompt for confirmation.  eg: when no argument was provided for file or type, and only one exists, the system will render the autoselected file unless this flag is set." })
};
AssetRecycle.args = {};
// type FuzzyResult = {
//     /** The rendered string (eg: '<b>lah'). */
//     string: string,
//     /** The index of the element in `arr` (eg: 2). */
//     index: number,
//     /** The original element in `arr`. */
//     original: 'blah'
// }
//# sourceMappingURL=recycle.js.map