"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const tslib_1 = require("tslib");
const core_1 = require("@oclif/core");
const _ = require("lodash");
const command_base_exec_1 = require("../../../command-base-exec");
const config_ui_utils_1 = require("../../../uilogic/config-ui-utils");
const non_empty_validator_1 = require("../../../uilogic/validators/non-empty-validator");
const semver_range_validator_1 = require("../../../uilogic/validators/semver-range-validator");
;
/**
 * @see [Dockerfile ref: ENTRYPOINT](https://docs.docker.com/reference/dockerfile/#entrypoint)
 * @see [Dockerfile ref: CMD](https://docs.docker.com/reference/dockerfile/#cmd)
 * @see [Dockerfile ref: SHELL](https://docs.docker.com/reference/dockerfile/#shell)
 * @see [Dockerfile ref: Shell and Exec form](https://docs.docker.com/reference/dockerfile/#shell-and-exec-form)
 */
class AssetEntrypointSet 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(AssetEntrypointSet);
            let partial = {};
            if (flags.enterprise)
                partial.enterprise = flags.enterprise;
            if (flags.asset)
                partial.asset = flags.asset;
            if (flags['version-range'])
                partial.versionRange = flags['version-range'];
            // ---------------------------------------------------------------
            // 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, false];
        });
    }
    buildCandidate_silently(partial, rootMap) {
        var _a, _b;
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            //  Enterprise
            partial.enterpriseObj = rootMap.enterprises.find(e => e.name == partial.enterprise);
            if (!partial.enterpriseObj)
                throw new Error("Enterprise not found."); // <-- todo: move this up to the base class and pass in the enterprise object.
            //  Asset
            if (partial.asset == undefined)
                throw new Error("Missing asset name.");
            partial.assetObj = partial.enterpriseObj.assets.find(a => a.name == partial.asset);
            if (partial.assetObj == undefined)
                throw new Error("Asset not found");
            // Entrypoint
            // Gotta grab the entrypoint by version range 
            // Q: then how would you change a version range without going interactive?
            // A: by offering a version selector arg (eg: --for 1.2.3) ...and then taking the first entrypoint that matches it.
            // Q: What if more than one matches?  
            // A: Can semver help us enforce mutually exclusive ranges?
            if ((_b = (_a = partial.assetObj.entryPoints) === null || _a === void 0 ? void 0 : _a.length) !== null && _b !== void 0 ? _b : 0 == 0) {
                this._ui.userMessage("Nothing to delete! Asset ".concat("[", partial.enterprise, "/", partial.asset, "] has no entrypoint overrides defined."));
                process.exit(0);
            }
            let existing;
            if (partial.assetObj.entryPoints) {
                let selected = _.findIndex(partial.assetObj.entryPoints, e => e.versionRange == partial.versionRange);
                // let selected = partial.assetObj.entryPoints.find(e => e.versionRange == partial.versionRange);
                if (selected >= 0) {
                    existing = partial.assetObj.entryPoints.splice(selected, 1)[0]; // Remove the one we're working on.  We'll add it back on the execute
                }
            }
            // Overlay the existing record with the given property values
            partial = _.merge({}, _.omit(existing, "created", "createdBy", "modified", "modifiedBy"), partial);
            let candidate = Object.assign({}, partial);
            return candidate;
        });
    }
    buildCandidate_interactively(partial, rootMap, takeDefaults) {
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            // Enterprise
            partial.enterpriseObj = rootMap.enterprises.find(e => e.name == partial.enterprise);
            if (!partial.enterpriseObj) {
                partial.enterpriseObj = yield this.rootMapUtils.promptForEnterprise(partial, rootMap);
            }
            partial.enterprise = partial.enterpriseObj.name;
            // Asset
            let assetChanged;
            let assetType;
            [partial.asset, assetChanged, assetType] = yield this.rootMapUtils.promptForAsset(partial.enterpriseObj.assets, partial.asset, true);
            partial.assetObj = (partial.enterpriseObj.assets.find(a => a.name == partial.asset)); // assertion ok, we wouldnt get here without a valid selection
            // Existing Entrypoint
            let existing;
            if (partial.assetObj.entryPoints && partial.assetObj.entryPoints.length > 0) {
                const CODE_FOR_NEW_RECORD = -1;
                let choices = partial.assetObj.entryPoints.map((e, index) => { return { name: this.buildEntryPointListItemDesc(e), value: index }; });
                // choices.push({ name: "** create new **", value: CODE_FOR_NEW_RECORD });
                let selected = (yield this._ui.promptSelect("selectExisting", "Entrypoint: ", choices, false, false));
                if (selected != CODE_FOR_NEW_RECORD) {
                    // existing = partial.assetObj.entryPoints[selected];
                    existing = partial.assetObj.entryPoints.splice(selected, 1)[0]; // Remove the one we're working on.  We'll add it back on the execute
                }
            }
            else {
                this._ui.userMessage("Nothing to delete! Asset ".concat("[", partial.enterprise, "/", partial.asset, "] has no entrypoint overrides defined."));
                process.exit(0);
            }
            // Overlay the existing record with the given property values
            partial = _.merge({}, _.omit(existing, "created", "createdBy", "modified", "modifiedBy"), partial);
            let candidate;
            let confirm = false;
            // do {
            // Lets wait till we like this command 
            // before bringing version ranges into it...
            // partial.versionRange = await this.promptForVersionRange(partial.versionRange);
            candidate = Object.assign({}, partial);
            this._ui.userMessage("\n".concat("Entrypoint override for ", candidate.asset, "... \n", this.buildConfirmPreview(candidate), "\n"));
            confirm = yield this._ui.promptConfirm("confirm", this._styler.colorCritical("Delete").concat(" this entrypoint (fall back to original image behavior)?"), false);
            if (!confirm) {
                this._ui.userMessage("Operation cancelled. No work was done.");
                process.exit(0);
            }
            // } while (!confirm)
            return candidate;
        });
    }
    // -----------------------------------------------------------
    //   UI SUPPORT
    // -----------------------------------------------------------
    buildEntryPointListItemDesc(ep) {
        var _a, _b, _c;
        let desc = ep.command.concat(" ", (_b = (_a = ep.args) === null || _a === void 0 ? void 0 : _a.join(" ")) !== null && _b !== void 0 ? _b : "");
        desc = desc.trim().concat(" ", this._styler.dim((_c = ep.versionRange) !== null && _c !== void 0 ? _c : "(any version)"));
        return desc;
    }
    buildConfirmPreview(ep) {
        var _a;
        let desc = "Asset: ".concat(this._styler.dim(ep.enterprise + "/"), this._styler.hilite(ep.asset), "\n");
        if (ep.workingDir != undefined && !_.isEmpty(ep.workingDir))
            desc = desc.concat("Working Dir: ", this._styler.hilite(ep.workingDir), "\n");
        desc = desc.concat("Command: ", this._styler.hilite(JSON.stringify([ep.command])), "\n");
        if (ep.args != undefined && ep.args.length > 0)
            desc = desc.concat("Args: ", this._styler.hilite(JSON.stringify(ep.args)), "\n");
        desc = desc.concat("Applicable to: ", this._styler.hilite((_a = ep.versionRange) !== null && _a !== void 0 ? _a : "(any version)"));
        return desc;
    }
    promptForWorkingDir(current) {
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            let keep = false;
            keep = yield this._ui.promptConfirm("workingDirConfirm", "Working Directory: ".concat(this._styler.hilite(current !== null && current !== void 0 ? current : "(use default)")), true);
            if (!keep) {
                let entry = current;
                while (!entry || entry.trim() == "") {
                    entry = (yield this._ui.prompt('workingDir', 'Working Directory: ', true, current === null || current === void 0 ? void 0 : current.trim(), new non_empty_validator_1.NonEmptyValidator()));
                }
                return entry.trim();
            }
            return current === null || current === void 0 ? void 0 : current.trim();
        });
    }
    promptForCommand(current) {
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            let entry = "";
            while (!entry || entry.trim() == "") {
                entry = (yield this._ui.prompt('command', 'Command: ', true, current === null || current === void 0 ? void 0 : current.trim(), new non_empty_validator_1.NonEmptyValidator()));
            }
            return entry.trim();
        });
    }
    promptForArgs(current) {
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            try {
                let ultimate;
                const existings = [];
                if (current) {
                    existings.push(...current);
                }
                // CONFIRM/EDIT EACH EXISTING
                let index = 0;
                for (let existing of existings) {
                    index++;
                    let arg = yield this.promptForSingleCommandArg(existing, index);
                    // Append to the ultimate
                    if (arg == undefined) {
                        this._ui.userMessage("Command arg removed.");
                        index--; // reuse this index for the next port assignment
                    }
                    else {
                        if (ultimate == undefined) {
                            ultimate = [arg];
                        }
                        else {
                            ultimate.push(arg);
                        }
                    }
                }
                // ADD NEW ONES
                // Automatically start adding when none are defined yet.  Otherwise, ask to add more.
                // while ((ultimate == undefined || (<[]>ultimate).length == 0) || await this._ui.promptConfirm("onemore", "Add another arg?", false)) {
                while (true) {
                    index++;
                    const thisArg = yield this.promptForSingleCommandArg(undefined, index);
                    if (thisArg == undefined) {
                        break; //index--;  // re-use on the next port number
                    }
                    else {
                        // Append to the ultimate
                        if (ultimate == undefined) {
                            ultimate = [thisArg];
                        }
                        else {
                            ultimate.push(thisArg);
                        }
                    }
                }
                return ultimate;
            }
            catch (err) {
                throw err;
            }
        });
    }
    promptForSingleCommandArg(current, index) {
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            let output;
            output = yield this._ui.prompt('arg', `Arg ${index}: ${this._styler.dim("(enter a blank to ".concat(current ? "remove" : "complete", ")"))}`, false, current === null || current === void 0 ? void 0 : current.trim());
            if (_.isEmpty(output === null || output === void 0 ? void 0 : output.trim())) {
                return undefined;
            }
            return output;
        });
    }
    promptForVersionRange(current) {
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            let keep = false;
            keep = yield this._ui.promptConfirm("versionConfirm", "Version Range: ".concat(this._styler.hilite(current !== null && current !== void 0 ? current : "(any)")), true);
            if (!keep) {
                let entry = current;
                entry = (yield this._ui.prompt('version', 'Version Range: ', true, current === null || current === void 0 ? void 0 : current.trim(), new semver_range_validator_1.SemverRangeValidator()));
                // TODO: Consider some better assistance... 
                // ? * from this version forward (>=1.2.3)
                // ? * from this version backward (<=1.2.3)
                // ? * on just this version (1.2.3)
                // ? * on any version like this (1.x)
                // ? * on this version and any future patches (^1.2.3)
                // ? * between inclusive (5.0.0 - 7.2.3)
                // ? * custom range spec:  1.x || >=2.5.0 || 5.0.0 - 7.2.3
                // ...like a builder interface...
                // ...and perhaps use with an asset version select list...?
                return entry.trim();
            }
            return current === null || current === void 0 ? void 0 : current.trim();
        });
    }
    // -----------------------------------------------------------
    //   EXECUTION
    // -----------------------------------------------------------
    buildCommandString(candidate, cmd) {
        cmd.push("--enterprise", candidate.enterprise);
        cmd.push("--asset", candidate.asset);
        // cmd.push("--id", candidate.id)
        if (candidate.versionRange)
            cmd.push("--version-range", candidate.versionRange); // HEY!!!! if there's spaces in it, we need to wrap with string chars
        cmd.push("--silent");
        return cmd;
    }
    executeTask(candidate) {
        var _a, _b;
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            let ep = _.omit(candidate, "enterpriseObj", "assetObj");
            if (candidate.assetObj.entryPoints == undefined)
                candidate.assetObj.entryPoints = [];
            // let result = await this._codiacApi.Asset.tryUpdate(candidate.assetObj);
            let patch = _.pick(candidate.assetObj, "id", "enterprise", "name", "entryPoints");
            let result = yield this._codiacApi.Asset.tryUpdatePartial(patch);
            if (result.error != undefined) {
                throw result.error;
            }
            else if (result.success == true) {
                this._ui.userMessage((_a = result.message) !== null && _a !== void 0 ? _a : "Successfully removed entrypoint from asset ".concat("[", candidate.asset, "]"));
            }
            else {
                this._ui.userMessage((_b = result.message) !== null && _b !== void 0 ? _b : "Failed without error updating entrypoint for ".concat("[", candidate.asset, "].  Check logs for details."));
            }
        });
    }
}
exports.default = AssetEntrypointSet;
AssetEntrypointSet.description = 'Removes an existing override of the image startup command for this asset (restores the original ENTRYPOINT set in the image itself, as well as any image elements specified using CMD)).';
AssetEntrypointSet.aliases = [];
AssetEntrypointSet.args = {};
AssetEntrypointSet.flags = {
    enterprise: core_1.Flags.string({ char: 'e', required: false, description: 'The name of the enterprise in which this asset resides (defaults to the current enterprise context).' }),
    asset: core_1.Flags.string({ char: 'a', required: false, description: "The name of the asset to adjust." }),
    "version-range": core_1.Flags.string({ char: 'v', required: false, description: '(optional, defaults to the entrypoint assigned to "any" version) Used to identify the entrypoint.  The semantic version rule describing which versions of the asset will get this entrypoint applied.  (eg: Just this one: `1.2.3`, or this one and any future patches: `^1.2.3`, or any on or after this one: `>=1.2.3`, or any before this one `<1.2.3` , or any between inclusive: `1.0.0 - 1.2.3`).  NOTE: Remember to wrap your range spec in quotes if it has any spaces in it.' }),
    // TODO: Move these into the base class
    silent: core_1.Flags.boolean({ exclusive: ["echo", "to-script"], dependsOn: ["asset"], 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": 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." })
};
AssetEntrypointSet.examples = [
    { description: "Interactive", command: '<%= config.bin %> <%= command.id %>' },
    {
        description: "Interactive, preset for the a specific asset.",
        command: '<%= config.bin %> <%= command.id %> -a my-api'
    },
    {
        description: "Interactive as above, but when you have more than a single enterprise, and one of them is named `ml`.",
        command: '<%= config.bin %> <%= command.id %> -e ml -a my-worker'
    },
    {
        description: "Non-interactive, removal by specifying the version range with an explicit match",
        command: "<%= config.bin %> <%= command.id %> -e ml -a my-worker -v '>=1.2.3' --silent"
    }
];
//# sourceMappingURL=delete.js.map