"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const tslib_1 = require("tslib");
const core_1 = require("@oclif/core");
const command_base_enterprise_1 = require("../../../command-base-enterprise");
const prettify_1 = require("../../../prettify");
const _ = require("lodash");
const uilogic_1 = require("../../../uilogic");
const cloud_provider_contextualizer_1 = require("../../../uilogic/cloud-provider-contextualizer");
const contracts_1 = require("@codiac.io/codiac-common/contracts");
class NocClusterRecreate extends command_base_enterprise_1.CommandBaseEnterprise {
    get uiCloudPvd() {
        if (this._uiCloudPvd_backer === undefined) {
            this._uiCloudPvd_backer = this.ioc.get(cloud_provider_contextualizer_1.CloudProviderContextualizer);
        }
        return this._uiCloudPvd_backer;
    }
    bootstrap() {
        super.bootstrap();
        uilogic_1.ClusterConfiguratorFactory.bootstrap(this.ioc, this);
    }
    run() {
        var _a;
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            try {
                this.bootstrap();
                const { args, flags } = yield this.parse(NocClusterRecreate);
                (_a = this._relayContainer) === null || _a === void 0 ? void 0 : _a.ready$.subscribe(() => tslib_1.__awaiter(this, void 0, void 0, function* () {
                    var _b, _c;
                    try {
                        this._userCntx = yield this.establishTenantContext(this._userCntx);
                        // ACT I:  Select an existing cluster
                        let existing;
                        if (args.name) {
                            let nameFilter = new contracts_1.StringFilter();
                            let readOneResult = yield this._codiacApi.ClusterDef.tryReadMany({ name: { $eq: args.name }, privateOwner: this._userCntx.currentTenantCode }); //, { projection: { pathsIncluded: [], pathsExcluded: ["identityInstance"] } });
                            if (readOneResult.error)
                                throw readOneResult.error;
                            else if (!readOneResult.output || readOneResult.output.length == 0)
                                this._ui.userMessage(`No cluster exists named [${args.name}]`);
                            else
                                existing = readOneResult.output[0];
                        }
                        if (!flags.silent && !existing) {
                            let readResult = yield this._codiacApi.ClusterDef.tryReadMany({ privateOwner: this._userCntx.currentTenantCode }); //, { projection: { pathsIncluded: [], pathsExcluded: ["identityInstance"] } });
                            if (readResult.error) {
                                this._ui.userMessage(`Error retrieving cluster list: ${(_b = readResult.error.name) !== null && _b !== void 0 ? _b : ""} ${readResult.error.message || readResult.error.stack}`);
                            }
                            else if (readResult.success && readResult.output && readResult.output.length > 0) {
                                let instances = readResult.output //.filter(c => c.instance != undefined)
                                    .map(c => { return { name: `${c.name} [${c.locationName}]${c.privateOwner ? this._styler.dim(" Owner: [" + c.privateOwner + "]") : ""}]`, value: c.name }; });
                                let clusterName = (yield this._ui.promptSelect("clusterName", "Select the cluster to re-create:", instances));
                                existing = readResult.output.find(c => c.name == clusterName);
                            }
                            else {
                                this._ui.userMessage("No clusters exist to re-create");
                            }
                        }
                        if (existing) {
                            // ACT II:  Overwrite any existing specs
                            let newClusterDef = _.cloneDeep(existing);
                            delete newClusterDef.instance;
                            // Overwrite existing ClusterDef with the given command arguments...
                            if (args.name != undefined)
                                newClusterDef.name = args.name;
                            if (flags.location != undefined)
                                newClusterDef.locationName = flags.location;
                            if (flags.owner != undefined)
                                newClusterDef.privateOwner = flags.owner;
                            if (flags.providerSubscription != undefined)
                                newClusterDef.providerSubscription = flags.providerSubscription;
                            if (flags.resourceGroup != undefined)
                                newClusterDef.providerGroup = flags.resourceGroup;
                            if (flags.k8sVersion != undefined)
                                newClusterDef.k8sVersion = flags.k8sVersion;
                            if (flags.subnet != undefined)
                                newClusterDef.subnet = flags.subnet;
                            if (flags.nodeSpec != undefined)
                                newClusterDef.nodeSpec = flags.nodeSpec;
                            if (flags.nodeQty != undefined)
                                newClusterDef.startingNodeCount = flags.nodeQty;
                            if (flags.windowsSupport) {
                                newClusterDef.nodePools = [
                                    Object.assign({ clusterName: newClusterDef.name, name: "windows", nodeSpec: flags.nodeSpec, startingNodeCount: flags.nodeQty, isWindows: true }, this.createAuditable())
                                ];
                                newClusterDef.windowsAdmin = {
                                    userName: "",
                                    password: ""
                                }; // if this prop is nullable, why set it up with empty creds?
                            }
                            // Lets just leave this alone.  It gets messy.
                            // if (flags.identityName) newClusterDef.identitySpec.name = flags.identityName;
                            // if (flags.identityType) newClusterDef.identitySpec.type = <SecurityAccountTypeEnum>flags.identityType;
                            // 2. User input loop
                            let defaults;
                            let confirmed = false;
                            while (!confirmed) {
                                console.clear();
                                this._ui.userMessage(this._styler.bold("New Cluster: (creates after you confirm the overall input)"));
                                // TODO: ask to change before prompting the list
                                let allPvds = this.cspUiFactory.getAllClusterProviders(this.ioc);
                                newClusterDef.provider = yield this.uiCloudPvd.promptForCloudProvider(allPvds, newClusterDef.provider);
                                yield this.uiCloudPvd.promptForCloudProviderCreds(this.ioc, newClusterDef.provider, undefined, undefined, undefined, newClusterDef.providerSubscription);
                                // load up the cluster configurator for the given clouduprovider
                                let regCfg = this.cspUiFactory.create(newClusterDef.provider, this.ioc);
                                let def = yield regCfg.defineCluster(newClusterDef, defaults); // note: "defaults" gets mutated here
                                defaults = def.defaults;
                                newClusterDef = def.spec;
                                // HACK: oclif or someone, doesnt seem to be honoring number type.  i cant find the culprit so i switch it back, last second
                                if (typeof newClusterDef.startingNodeCount == "string")
                                    newClusterDef.startingNodeCount = Number.parseInt(newClusterDef.startingNodeCount);
                                console.clear();
                                let msg = (0, prettify_1.prettify)(newClusterDef, [(_c = newClusterDef.identityInstance) === null || _c === void 0 ? void 0 : _c.password]);
                                this._ui.userMessage(msg);
                                this._ui.userMessage(`You are about to create this cluster. Continue?`);
                                confirmed = yield this._ui.promptConfirm("confirmCreate", `(hit 'N' to edit your input)`, false);
                            }
                            // ACT III:  Execute
                            if (flags.toScript) {
                                this._ui.userMessage(`codiac noc:cluster:recreate ${newClusterDef.name} -p ${newClusterDef.privateOwner} -s ${newClusterDef.providerSubscription} -g '${newClusterDef.providerGroup}' -l ${newClusterDef.locationName} -n ${newClusterDef.nodeSpec} -q ${newClusterDef.startingNodeCount} -k ${newClusterDef.subnet} ${(flags.windowsSupport) ? '-w' : ''} ${(flags.silent) ? '-y ' + flags.silent : ''}`);
                            }
                            else {
                                yield this.cspContextualizer.promptForCloudProviderCreds(this.ioc, existing.provider, undefined, undefined, undefined, existing.providerSubscription);
                                // tear down existing cluster
                                this._ui.startSpinner(`Tearing down existing cluster instance [${existing.name}]...`);
                                let teardownResult = yield this.genRelayClient.Call.postExecuteDeleteClusterInstance({ name: existing.name, deleteIdentity: false });
                                if (teardownResult.error) {
                                    this._ui.stopSpinner(`Error: ${teardownResult.error.name} ${teardownResult.error.message || ""}`);
                                    throw teardownResult.error;
                                }
                                else if (!teardownResult.success)
                                    this._ui.stopSpinner(`Failed without error attempt to delete cluster instance [${existing.name}]. This is likely because it does not exist.  Recreate shall resume.`);
                                else
                                    this._ui.stopSpinner(`Successfully deleted existing cluster instance [${existing.name}].`);
                                // Create new cluster instance
                                let result = yield this.watcher.triggerAndWatch((socketId) => this.genRelayClient.Call.clusterCreateAsync({ spec: newClusterDef, socketId: socketId }));
                                if (result.error) {
                                    let msg = `Error re-creating cluster [${newClusterDef.name}]: ${result.message} ${result.error.message || ""}`;
                                    this.logger.error(msg, result.error);
                                    this._ui.userMessage(`${this._styler.colorCritical("ERROR!")} ${msg}`);
                                }
                                else if (!result.success) {
                                    let msg = `Failed without error re-creating cluster [${newClusterDef.name}]: ${result.message}`;
                                    this.logger.notice(msg, result);
                                    this._ui.userMessage(`${this._styler.colorCritical("FAILED!")} ${msg}`);
                                }
                                else {
                                    let msg = `Re-created cluster [${this._styler.hilite(newClusterDef.name)}] \nin location [${newClusterDef.locationName}], resource group: [${newClusterDef.providerGroup}], subscription [${newClusterDef.providerSubscription}].`;
                                    this.logger.notice(msg, result);
                                    this._ui.userMessage(`${this._styler.colorGood("SUCCESS!")} ${msg}`);
                                }
                            }
                        }
                    }
                    catch (err) {
                        this.handleError(err);
                    }
                    finally {
                        yield this.closeConnections();
                        return;
                    }
                }));
            }
            catch (err) {
                this.handleError(err);
            }
            finally {
                yield this.closeConnections();
                return;
            }
        });
    }
}
exports.default = NocClusterRecreate;
NocClusterRecreate.description = 'Provisions the physical cluster using the existing ClusterDef record (with any changes provided by the caller).';
NocClusterRecreate.aliases = ["cluster:recreate"];
NocClusterRecreate.dependsOn = [];
NocClusterRecreate.flags = {
    help: core_1.Flags.help({ char: 'h' }),
    owner: core_1.Flags.string({ char: 'p', required: false, description: 'Tenant code of the private owner of the cluster' }),
    providerSubscription: core_1.Flags.string({ char: 's', required: false, description: 'The Azure subscription Id under which to create the cluster.' }),
    resourceGroup: core_1.Flags.string({ char: 'g', required: false, description: 'The Azure resource group to create for the cluster.' }),
    location: core_1.Flags.string({ char: 'l', required: false, description: 'The short name of the azure data center in which the cliuster is to reside.' }),
    subnet: core_1.Flags.string({ char: 'k', required: false, description: 'The resource id of the subnet in which the cluster is to be created.' }),
    k8sVersion: core_1.Flags.string({ char: 'v', required: false, description: 'The Kubernetes version for the new cluster.  This must be a supported version from the cloud provider.' }),
    nodeSpec: core_1.Flags.string({ char: 'n', required: false, description: 'The identifier for the type of VM to use for nodes in the cluster.' }),
    windowsSupport: core_1.Flags.boolean({ char: 'w', required: false, dependsOn: ['nodeSpec', 'nodeQty'], description: `Add a windows nodepool.` }),
    nodeQty: core_1.Flags.integer({ char: 'q', required: false, description: 'The starting node count with which to create the cluster.' }),
    identityName: core_1.Flags.string({ required: false, description: 'name of an existing service principal to use for the cluster identity' }),
    // identityType: Flags.string({ options: [SecurityAccountTypeEnum.servicePrincipal, SecurityAccountTypeEnum.managedIdentity], required: false, description: 'The type of security account to use for the cluster identity.' }),
    silent: core_1.Flags.boolean({ char: 'y', dependsOn: NocClusterRecreate.dependsOn, required: false, description: `(Optional: defaults to false) Prevents confirmations of user-values. Required: ${NocClusterRecreate.dependsOn}` }),
    toScript: core_1.Flags.boolean({ char: 't', required: false, description: `Outputs an assembled command string, NO execution` })
};
NocClusterRecreate.args = {
    name: core_1.Args.string({ description: 'Name of an existing cluster to re-create' })
};
//# sourceMappingURL=recreate.js.map