"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const tslib_1 = require("tslib");
const _ = require("lodash");
const core_1 = require("@oclif/core");
const codiac_domain_1 = require("@codiac.io/codiac-domain");
const type_safety_utils_1 = require("../../../uilogic/type-safety-utils");
const command_base_exec_tenant_1 = require("../../../command-base-exec-tenant");
const config_ui_utils_1 = require("../../../uilogic/config-ui-utils");
class NocClusterForget extends command_base_exec_tenant_1.CommandBaseExecTenant {
    captureArgs() {
        var _a, _b;
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            const { args, flags } = yield this.parse(NocClusterForget);
            let partial = {
                provider: type_safety_utils_1.TypeSafetyUtils.toEnum(codiac_domain_1.CloudProviderEnum, flags.provider),
                name: flags.name,
                running: type_safety_utils_1.TypeSafetyUtils.toBoolean(flags.running)
            };
            // ---------------------------------------------------------------
            // 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) {
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            if (partial.name == undefined)
                throw new Error("Cluster name was not provided.");
            if (partial.provider == undefined)
                throw new Error("Cloud service provider was not provided.");
            let cluster = yield this.promptForCluster(partial.name, partial.provider, partial.running, true);
            let candidate;
            if (cluster) {
                candidate = {
                    id: cluster.id,
                    name: cluster.name,
                    provider: cluster.provider
                };
                if (cluster.thisMachine != undefined)
                    candidate.thisMachine = cluster.thisMachine;
            }
            return candidate;
        });
    }
    buildCandidate_interactively(partial, takeDefaults, rootMap) {
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            let cluster = yield this.promptForCluster(partial.name, partial.provider, partial.running, false);
            let candidate;
            if (cluster) {
                candidate = {
                    id: cluster.id,
                    name: cluster.name,
                    provider: cluster.provider
                };
                if (cluster.thisMachine != undefined)
                    candidate.thisMachine = cluster.thisMachine;
            }
            return candidate;
        });
    }
    buildCommandString(candidate, cmd) {
        cmd.push("--provider", candidate.provider);
        cmd.push("--name", candidate.name);
        if (candidate.running == true)
            cmd.push("--running");
        cmd.push("--silent");
        return cmd;
    }
    executeTask(candidate) {
        var _a;
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            this._ui.startSpinner(`Forgetting ${candidate.provider} cluster: ${this._styler.hilite(candidate.name)}...`);
            let req = { id: candidate.id };
            if (candidate.thisMachine == true)
                req.thisMachine = true;
            let result = yield this._codiacApi.ClusterDef.tryDelete(req, {});
            this._ui.stopSpinner(result.success ? "SUCCESS" : "FAILED");
            let msg;
            if (result.success) {
                msg = this._styler.colorGood("SUCCESS").concat(": Codiac record of ", candidate.provider, " cluster [", this._styler.hilite(candidate.name), "] was successfully forgotten.");
                if (result.message)
                    msg = msg.concat(" ", result.message);
            }
            else {
                msg = this._styler.colorCritical("FAILED").concat(": Failed removing Codiac record of ", candidate.provider, " cluster [", this._styler.hilite(candidate.name), "]");
                msg = _.isEmpty(result.message) ? msg.concat(".") : msg.concat(": ", (_a = result.message) !== null && _a !== void 0 ? _a : "");
            }
            this.logger.debug(msg, result);
            this._ui.userMessage(msg);
        });
    }
    promptForCluster(name, provider, running, silent) {
        var _a, _b, _c;
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            try {
                // // Use the rootMap to determine if the selected cluster has any child envs or cabs
                let rootMapResult = yield this._codiacApi.Call.getRootMap({ all: true }, {});
                if (rootMapResult.error)
                    throw new Error(`Error loading entities: ${(_a = rootMapResult.message) !== null && _a !== void 0 ? _a : rootMapResult.error.message}`);
                if (rootMapResult.output == undefined)
                    throw new Error(`Error enterprise entities: empty root map.`);
                let rootMap = rootMapResult.output;
                // this.logger.trace("rootmap", rootMap);
                // Get the list of clusters
                let cluster;
                let readResult = yield this._codiacApi.ClusterDef.tryReadMany({ all: true }, { projection: { pathsIncluded: ["id", "name", "provider", "thisMachine", "locationName", "instance", "modified", "modifiedBy"], pathsExcluded: [] } });
                let clusterDefs = (_b = readResult.output) !== null && _b !== void 0 ? _b : [];
                clusterDefs = _.orderBy(clusterDefs, c => c.name);
                if (provider) {
                    clusterDefs = clusterDefs.filter(c => c.provider == provider);
                }
                if (readResult.error == undefined && (clusterDefs == undefined || clusterDefs.length === 0)) {
                    this._ui.userMessage("No clusters found");
                }
                else {
                    if (running !== true) {
                        clusterDefs = clusterDefs.filter(c => c.instance == undefined);
                    }
                    if (readResult.error == undefined && clusterDefs.length === 0) {
                        this._ui.userMessage("No clusters exist without instances");
                    }
                    else if (silent === true) {
                        cluster = clusterDefs.find(c => c.name === name && c.provider === provider);
                        if (cluster == undefined) {
                            // this._ui.userMessage("No clusters found with the given name and provider");  // no need to throw here
                            throw new Error("No clusters found with the given name and provider"); // fail loudly when silent?
                        }
                    }
                    else {
                        let candidate = {};
                        if (name)
                            candidate.name = name;
                        let confirm = false;
                        do {
                            if (readResult.error) {
                                // if there is a problem with the cluster record, it may prevent a good read, 
                                // so we're going to allow the user to invoke the operation nonetheless.
                                let allPvds = this.cspUiFactory.getAllClusterProviders(this.ioc);
                                const csp = yield this.cspContextualizer.promptForCloudProvider(allPvds, undefined);
                                const name = (yield this._ui.prompt("clusterName", "Enter name of the cluster to forget:", true));
                                cluster = { name: name, provider: csp, modified: new Date(Date.now()), modifiedBy: "unknown" };
                            }
                            else { // if (readResult.success && readResult.output && readResult.output.length > 0) {
                                let found = (candidate.name) ? clusterDefs.find(c => c.name === candidate.name) : undefined;
                                if (found) {
                                    cluster = found;
                                }
                                else {
                                    let choices = clusterDefs
                                        .map(c => { return { name: this.describe(c), value: c.name }; });
                                    const name = (yield this._ui.promptSelect("clusterName", "Select the cluster to forget:", choices));
                                    cluster = (clusterDefs.find(c => c.name === name));
                                }
                            }
                            // Warnings
                            if (cluster.instance) {
                                // They wd have to have set the --running flag to get this, 
                                // so this warning is redundant, but it's important enough to point out anyway. 
                                this._ui.userMessage(`${this._styler.colorWarning("Warning!")} The ${cluster.provider} cluster [${cluster.name}] appears to still have a running instance.`);
                            }
                            let children = this.findChildrenFor(cluster.name, rootMap);
                            if (children > 0) {
                                // TODO: Any associated cabinets should be forgotten also  (ie: Perform a cab obliterate without the infrx piece).
                                this._ui.userMessage(this._styler.colorWarning("Warning!").concat(" The ", cluster.provider, " cluster [", cluster.name, "] has ", (_c = this._styler) === null || _c === void 0 ? void 0 : _c.hilite("" + children), " existing cabinet(s)."));
                            }
                            confirm = yield this._ui.promptConfirm("confirmForget", `Forget the cluster: ${this._styler.hilite(this.describe(cluster))} ? `, false);
                            if (!confirm)
                                candidate = {}; // if cluster was set as a cmd flag, but they denied the confirm, this allows selecting a different one.
                        } while (confirm !== true);
                    }
                }
                return cluster;
            }
            catch (err) {
                throw err;
            }
        });
    }
    /** Seeks out all cabinet records that are situated in the given cluster. */
    findChildrenFor(clusterName, rootMap) {
        let envs = [];
        let cabs = [];
        for (let ent of rootMap.enterprises) {
            for (let env of ent.environments) {
                let theseCabs = env.cabinets.filter(c => c.cluster === clusterName);
                if (theseCabs.length > 0) {
                    envs.push(env);
                    cabs.push(...theseCabs);
                }
            }
        }
        return cabs.length;
    }
    /** Renders a user-friendly cluster description. */
    describe(cluster) {
        var _a;
        const UTF8_HEART = "\u2665";
        let existing = _.isEmpty(cluster.instance) ? "" : " " + this._styler.colorWarning(UTF8_HEART);
        let dim1 = ` (${cluster.provider}, ${cluster.locationName})`;
        let dim2 = ` (${new Date((_a = cluster.modified) !== null && _a !== void 0 ? _a : "").toLocaleDateString()}, by ${cluster.modifiedBy})`;
        return `${cluster.name}${existing} ${this._styler.dim(dim1)} ${this._styler.dim(dim2)}`;
    }
}
exports.default = NocClusterForget;
NocClusterForget.description = "Permanently removes the record of a cluster.  This command does NOT affect any infrastructure; it simply removes the cluster from the Codiac consciousness. You must remove all cabinets and environments in a cluster before forgetting it."; // TODO: Add this ---> Use `cod cluster capture` to re-acquire an existing but forgotten cluster.  
NocClusterForget.examples = [
    { description: "Forget a cluster using interactive prompts:", command: '<%= config.bin %> <%= command.id %>' },
    { description: "Forget an aws cluster:", command: "<%= config.bin %> <%= command.id %> --provider aws --name my-cluster" },
    { description: "Forget an azure cluster:", command: "<%= config.bin %> <%= command.id %> --provider azure --name my-other-cluster" },
    { description: "Forget an azure cluster in silent mode:", command: "<%= config.bin %> <%= command.id %> --provider azure --name my-other-cluster --silent" },
    { description: "Forget a cluster that is not destroyed yet:", command: "<%= config.bin %> <%= command.id %> --running" },
];
NocClusterForget.aliases = ["cluster:forget"];
NocClusterForget.flags = {
    name: core_1.Flags.string({ char: 'n', required: false, description: 'Name of the cluster.' }),
    provider: core_1.Flags.string({ char: 'p', options: Object.keys(codiac_domain_1.CloudProviderEnum), required: false, description: 'Code for the cloud service provider.' }),
    running: core_1.Flags.boolean({ char: 'r', required: false, default: false, description: 'Include clusters that still have a running instance.' }),
    silent: core_1.Flags.boolean({ char: 'y', dependsOn: ["name", "provider"], required: false, description: `(Optional: defaults to false) Prevents confirmations of user-values. Required: ["name", "provider"]` })
};
NocClusterForget.args = {};
//# sourceMappingURL=forget.js.map