"use strict";
var _a;
Object.defineProperty(exports, "__esModule", { value: true });
const tslib_1 = require("tslib");
const core_1 = require("@oclif/core");
const _ = require("lodash");
const contracts_1 = require("@codiac.io/codiac-common/contracts");
const codiac_domain_1 = require("@codiac.io/codiac-domain");
const contracts_2 = require("../../apis/codiac-api/contracts");
// import { ApiCommandBase } from '../../api-command-base';
const command_base_enterprise_1 = require("../../command-base-enterprise");
const CodiacConstants = require("../../codiac-constants");
const config_ui_utils_1 = require("../../uilogic/config-ui-utils");
const type_safety_utils_1 = require("../../uilogic/type-safety-utils");
class ConfigDelete extends command_base_enterprise_1.CommandBaseEnterprise {
    constructor() {
        super(...arguments);
        /** CLI Command-line string to rerun this execution as a non-TTY script. */
        this.cmd = ["cod", "config", "delete", "--silent"];
    }
    // --------------------------------------------------------------------
    // Lazy-loaded properties
    get configUtils() {
        if (this._configUtils_backer === undefined) {
            this._configUtils_backer = this.ioc.get(config_ui_utils_1.ConfigUiUtils);
        }
        return this._configUtils_backer;
    }
    // --------------------------------------------------------------------
    bootstrap() {
        super.bootstrap();
        this.ioc.bind(config_ui_utils_1.ConfigUiUtils).toSelf();
    }
    run() {
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            this.bootstrap();
            yield this.readStdin();
            try {
                // 0. SET DEFAULTS
                let [partial, interactionMode, takeDefaults] = yield this.captureArgs();
                this._userCntx = yield this.establishTenantContext(this._userCntx);
                let rootMap = yield this.getRootMap();
                partial.enterprise = (yield this.establishEnterpriseContext(rootMap, partial.enterprise, interactionMode)).enterprise.name;
                _.remove(rootMap.enterprises, e => e.name != partial.enterprise); // trim down to match legacy conditions
                // 1. CAPTURE ARGS
                // 2. VALIDATE AND ACQUIRE INPUT
                let candidate;
                if (interactionMode == config_ui_utils_1.InteractionModes.silent) {
                    candidate = this.buildCandidate_silently(partial, rootMap);
                }
                else {
                    candidate = yield this.buildCandidate_interactively(partial, rootMap, takeDefaults);
                }
                // In case we're hanging waiting for piped-in data that doesnt exist... 
                if (!process.stdin.destroyed)
                    process.stdin.destroy();
                if (candidate == undefined) {
                    this.logger.notice(`Operation cancelled. No work was done.`);
                    this._ui.userMessage(`Operation cancelled. No work was done.`);
                    this.exit(0);
                }
                else {
                    // 3. RENDER COMMAND STRING
                    if (interactionMode != undefined && [config_ui_utils_1.InteractionModes.echo, config_ui_utils_1.InteractionModes.toscript].includes(interactionMode)) {
                        this.cmd = this.buildCommandString(candidate, this.cmd);
                        this._ui.userMessage(this.cmd.join(" "));
                    }
                    if (interactionMode != config_ui_utils_1.InteractionModes.toscript) {
                        // 4. EXECUTE THE MAIN TASK
                        yield this.executeTask(candidate);
                    }
                }
                return;
            }
            catch (err) {
                // throw err;
                this.handleError(err);
                return;
            }
            finally {
                yield this.closeConnections();
            }
        });
    }
    executeTask(candidate) {
        var _b;
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            // Description for user
            let forSettings = "";
            if (candidate.settings && candidate.settings.length == 1) {
                if (candidate.settings[0] == CodiacConstants.CliConstants.GLOB_FOR_ALL) {
                    forSettings = ` settings (all)`;
                }
                else {
                    forSettings = ` setting: ${this._styler.colorCritical(candidate.settings[0])}`;
                }
            }
            else if (candidate.settings && candidate.settings.length > 1) {
                forSettings = ` settings (${candidate.settings.length})`;
            }
            else {
                forSettings = " settings (all)";
            }
            let forScope = candidate.scope ? ` for ${this._styler.colorGood(candidate.scope + ' ' + candidate.target)}` : " everywhere";
            let thoseAffected = `${(_b = candidate.targetFile) !== null && _b !== void 0 ? _b : candidate.configType}${forSettings}${forScope}`;
            // Fire the delete...
            let crit = this.convertToCriteria(candidate);
            let deleteResult = yield this._codiacApi.ScopedSetting.tryDeleteWhere(crit, { thisMachine: candidate.thisMachine });
            if (deleteResult.error) {
                throw new contracts_1.CodiacError(`Error deleting: ${deleteResult.message || ""}; ${deleteResult.error.message}`, deleteResult.error);
            }
            else if (!deleteResult.success || deleteResult.successes == undefined || deleteResult.successes.length == 0) {
                this.logger.warn(`There are no config settings to delete with the given parameters.`, candidate);
                this._ui.userMessage(`There are no config settings to delete with the given parameters.`);
            }
            else {
                let removed = deleteResult.successes.map(s => s.setting);
                this.logger.notice(`Successfully removed ${thoseAffected}.`);
                this._ui.userMessage(`Successfully removed ${thoseAffected}.`);
            }
        });
    }
    convertToCriteria(candidate) {
        var _b;
        let crit = {};
        crit.enterprise = new contracts_1.StringFilter();
        crit.enterprise.$eq = candidate.enterprise;
        crit.asset = new contracts_1.StringFilter();
        crit.asset.$eq = candidate.asset;
        crit.targetFile = new contracts_1.StringFilter();
        crit.targetFile.$eq = (_b = (candidate.configType ? `[${candidate.configType}]` : undefined)) !== null && _b !== void 0 ? _b : candidate.targetFile;
        if (candidate.scope) {
            crit.scope = candidate.scope;
            crit.target = candidate.target;
            // crit.thisMachine = candidate.thisMachine;
        }
        // Setting name
        if (candidate.settings && candidate.settings.length > 0) {
            if (candidate.settings.length == 1) {
                if (candidate.settings[0] == CodiacConstants.CliConstants.GLOB_FOR_ALL) {
                    // they asked for all.  Nothing to add.
                }
                else {
                    crit.setting = candidate.settings[0];
                }
            }
            else {
                let setting = `+(${candidate.settings.join("|")})`; // <---- glob: matches one or more of the "|"-separated patterns.
                crit.setting = setting;
            }
        }
        return crit;
    }
    buildCommandString(ultimate, cmd) {
        cmd.push("--enterprise", ultimate.enterprise);
        cmd.push("-a", ultimate.asset);
        if (ultimate.configType) {
            cmd.push("-t", ultimate.configType);
        }
        else {
            cmd.push("-f", ultimate.targetFile); // assertion is ok bc we forced one or the other just above ^.
        }
        if (ultimate.scope) {
            if (ultimate.scope == contracts_2.ScopeLevel.enterprise) {
                cmd.push("--enterprise-scope");
            }
            else if (ultimate.target) {
                if (ultimate.scope == contracts_2.ScopeLevel.environment)
                    cmd.push("-e", ultimate.target);
                if (ultimate.scope == contracts_2.ScopeLevel.cabinet)
                    cmd.push("-c", ultimate.target);
            }
        }
        if (ultimate.settings && ultimate.settings.length > 0) {
            for (let setting of ultimate.settings) {
                this.cmd.push("--setting", `"${setting}"`); // <-- need quotes here to prevent the shell (or oclif?) from executing a glob value on the currentworking directory.  ie:  "--setting *" will set the flag value not to the literal "*" but to the list of files in the working directory.
            }
        }
        return cmd;
    }
    buildCandidate_interactively(partial, rootMap, takeDefaults = false) {
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            let output;
            // Enterprise
            let ent = yield this.configUtils.promptForEnterprise(partial, rootMap);
            partial.enterprise = ent.name; // This is just for TS context (the method above already set this).
            // Asset
            let assetType;
            let assetChanged;
            [partial.asset, assetChanged, assetType] = yield this.configUtils.promptForAsset(ent.assets, partial.asset);
            // Type/File
            // TODO:  Q: Can we include the configDefs in the RootMap? 
            //        A: ONLY if you omit the settings underneath them (otherwise youll get the entire enterprise of settings with every rootmap pull)
            let typeFileChanged;
            let existingSettings;
            [partial.configType, partial.targetFile, existingSettings, typeFileChanged] = yield this.configUtils.promptForTypeAndFile(partial.enterprise, partial.asset, assetType, partial.configType, partial.targetFile, undefined, undefined, takeDefaults);
            // Setting
            let [selected, instances, settingChanged] = yield this.configUtils.promptForManySettings(existingSettings !== null && existingSettings !== void 0 ? existingSettings : [], partial.settings);
            partial.settings = selected ? selected : undefined;
            // Scope
            let targetChanged = false;
            [partial.scope, partial.target, targetChanged, partial.thisMachine] = yield this.configUtils.validateAndPromptForScope(rootMap, partial.enterprise, partial.scope, partial.target, instances);
            // LEGACY BEHAVIOR
            // if (args.setting == "all") {
            //   // REMOVE ALL SETTINGS FOR THE GIVEN SCOPE
            //   partial.all = true;
            //   thoseAffected = `${this._styler.colorCritical("all")} ${this._styler.colorGood(configFile)} settings${forScope}.`
            // } else {
            //   // REMOVE A SINGLE SETTING FOR THE GIVEN SCOPE
            //   let setting = (args.setting) ? args.setting : undefined;
            //   // NO SETTING SPECIFIED:  Prompt for one
            //   if (!partial.setting) {
            //     let availResult = await this._codiacApi.ScopedSetting.tryReadMany(partial);
            //     if (availResult.error) {
            //       throw new CodiacError(`Error reading existing settings: ${availResult.message || ""}; ${availResult.error.message}`, availResult.error);
            //     } else if (!availResult.success || availResult.output == undefined || availResult.output.length == 0) {
            //       // this.logger.trace(`availResult`, availResult);
            //       this.logger.warn(`There are no config settings with the given parameters.`, partial);
            //       this._ui.userMessage(`There are no config settings with the given parameters.`);
            //       this.exit(0);
            //     } else {
            //       let existings: { name: string, value: any, short?: string }[] = availResult.output.map(s => {
            //         return { name: `${s.setting} (${s.value})`, value: s.setting };
            //       });
            //       this._ui.userMessage(`Settings in ${this._styler.colorGood(configFile)} scoped to ${this._styler.colorGood(partial.scope + ' ' + partial.target)}...`)
            //       let result = await inquirer.prompt({ type: 'list', name: 'setting', message: 'Setting to remove:', choices: existings });
            //       setting = result.setting;
            //     }
            //   }
            //   partial.setting = setting;
            // }
            output = partial;
            return output;
        });
    }
    buildCandidate_silently(partial, rootMap) {
        let output;
        if (partial.enterprise == undefined)
            throw new Error("Must specify the enterprise of the asset.");
        let ent = rootMap.enterprises.find(x => x.name === partial.enterprise);
        if (ent == undefined)
            throw new Error(`No enterprise was found with that name.`);
        if (partial.asset == undefined)
            throw new Error("Must specify the asset.");
        let asset = ent.assets.find(x => x.name === partial.asset);
        if (asset === undefined)
            throw new Error(`No asset was found with that name.`);
        if (partial.targetFile == undefined && partial.configType == undefined)
            throw new Error("Must specify the file or type.");
        partial.thisMachine = this.configUtils.validateScope(rootMap, partial.enterprise, partial.scope, partial.target);
        // output = {
        //   enterprise: partial.enterprise,
        //   asset: partial.asset,
        //   targetFile: partial.targetFile,
        //   scope: partial.scope,
        //   target: partial.target
        // };
        output = partial;
        return output;
    }
    captureArgs() {
        var _b, _c, _d, _e, _f, _g, _h;
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            // TODO: Move this into the base class
            const { args, flags } = yield this.parse(ConfigDelete);
            // TODO: Consider making this parse a separate abstract base method
            // stdin
            let inputDoc;
            if (this.pipedInput) {
                inputDoc = this.configUtils.parseInputString(this.pipedInput.trim());
                this._ui.userMessage(`Found ${inputDoc.length} record(s) to add in the stdin pipeline.`);
            }
            let candidate = {};
            if (flags.enterprise)
                candidate.enterprise = flags.enterprise;
            if (flags.asset)
                candidate.asset = flags.asset;
            if (flags.type) {
                candidate.configType = type_safety_utils_1.TypeSafetyUtils.toEnum(codiac_domain_1.SpecialConfigTypesEnum, flags.type);
                if (candidate.configType == undefined)
                    delete candidate.configType;
            }
            else if (flags.file) {
                candidate.targetFile = flags.file;
            }
            candidate.scope =
                (_c = (_b = (flags['enterprise-scope'] ? contracts_2.ScopeLevel.enterprise : undefined)) !== null && _b !== void 0 ? _b : (flags.enviro ? contracts_2.ScopeLevel.environment : undefined)) !== null && _c !== void 0 ? _c : (flags.cabinet ? contracts_2.ScopeLevel.cabinet : undefined);
            candidate.target =
                (_e = (_d = (flags['enterprise-scope'] ? candidate.enterprise : undefined)) !== null && _d !== void 0 ? _d : flags.enviro) !== null && _e !== void 0 ? _e : flags.cabinet;
            candidate.settings = flags.setting;
            // capture stdin
            if (inputDoc != undefined && inputDoc.length > 0) {
                candidate.settings = ((_f = candidate.settings) !== null && _f !== void 0 ? _f : []).concat(inputDoc.map(x => x.setting));
            }
            // TODO: move this into the base class
            const interactionMode = (_h = (_g = (flags.echo ? config_ui_utils_1.InteractionModes.echo : undefined)) !== null && _g !== void 0 ? _g : (flags["to-script"] ? config_ui_utils_1.InteractionModes.toscript : undefined)) !== null && _h !== void 0 ? _h : (flags.silent ? config_ui_utils_1.InteractionModes.silent : undefined);
            const takeDefaults = flags['take-defaults'];
            return [candidate, interactionMode, takeDefaults];
        });
    }
}
exports.default = ConfigDelete;
_a = ConfigDelete;
ConfigDelete.description = 'Removes the given keys from the app config';
ConfigDelete.aliases = ['cfg:delete'];
ConfigDelete.flags = {
    enterprise: core_1.Flags.string({ char: 'n', description: '(required only when more than one enterprise exists) The name of the enterprise containing the asset.' }),
    asset: core_1.Flags.string({ char: 'a', description: 'The name of the asset to which the settings belong.' }),
    file: core_1.Flags.string({ char: 'f', exclusive: ["type"], description: 'The path-name to the config file holding the settings, relative to the root of the Codiac project.' }),
    type: core_1.Flags.string({ char: 't', exclusive: ["file"], options: Object.keys(codiac_domain_1.SpecialConfigTypesEnum), description: 'Filters for the settings types that do not land in a config file (such as... env: OS environment variables, footprint: container footprint specs, helm: helm chart settings, label: container meta labels, annotation: container platform key-value pairs).' }),
    "enterprise-scope": core_1.Flags.boolean({ char: 's', exclusive: ["enviro", "cabinet"], description: 'Filters for settings scoped at the enterprise level.' }),
    enviro: core_1.Flags.string({ char: 'e', exclusive: ["enterprise-scope", "cabinet"], description: 'Filters for settings scoped to the given environment name.' }),
    cabinet: core_1.Flags.string({ char: 'c', exclusive: ["enterprise-scope", "enviro"], description: 'Filters for settings scoped to the given cabinet name.' }),
    setting: core_1.Flags.string({ char: 's', required: false, multiple: true, parse: (input) => tslib_1.__awaiter(void 0, void 0, void 0, function* () { return input; }), description: "The config key (in dotpath/JSONPath), relative to the root of the config. eg: `database.name` or `database.connectionString`" }),
    silent: core_1.Flags.boolean({ 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({ 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." }),
    help: core_1.Flags.help({ char: 'h', description: 'Displays the help document for this command.' })
};
ConfigDelete.args = {
    setting: core_1.Args.string({ description: '(optional) Specific config key to remove. Omit to select from a list. Enter "all" to clear the entire config.' })
};
//# sourceMappingURL=delete.js.map