"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const tslib_1 = require("tslib");
const codiac_domain_1 = require("@codiac.io/codiac-domain");
const core_1 = require("@oclif/core");
const _ = require("lodash");
const command_base_enterprise_1 = require("../../../command-base-enterprise");
const prettify_1 = require("../../../prettify");
const type_safety_utils_1 = require("../../../uilogic/type-safety-utils");
class AssetProbeCreate extends command_base_enterprise_1.CommandBaseEnterprise {
    run() {
        var _a;
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            this.bootstrap();
            const { args, flags } = yield this.parse(AssetProbeCreate);
            (_a = this._relayContainer) === null || _a === void 0 ? void 0 : _a.ready$.subscribe((ready) => tslib_1.__awaiter(this, void 0, void 0, function* () {
                var _b, _c, _d, _e, _f, _g, _h, _j, _k;
                try {
                    this._userCntx = yield this.establishTenantContext(this._userCntx);
                    let rootMap = yield this.getRootMap();
                    const interactionMode = this.getInteractionMode(flags.silent, flags.echo, flags['to-script']);
                    let enterprise = (yield this.establishEnterpriseContext(rootMap, flags.enterprise, interactionMode)).enterprise;
                    _.remove(rootMap.enterprises, e => e.name != enterprise.name); // trim down to match legacy conditions
                    let probe;
                    // STEP 1: Select the target asset
                    // let aCntx = await this.establishAssetContext(enterprise, true);
                    // let assetName = aCntx[0];
                    // let asset = enterprise.assets.find(a => a.name === assetName);  // we're only looking for one.
                    // if (asset === undefined) {
                    //   throw new Error(`Could not find asset ${aCntx} in enterprise ${enterprise.code}.`);
                    // }
                    let assetName = flags.asset;
                    let assetChanged;
                    [assetName, assetChanged] = yield this.promptForAsset(enterprise.assets, assetName);
                    const asset = enterprise.assets.find(a => a.name == assetName); // assertion operator ok bc we just selected from that list
                    // STEP 2) assemble the probe
                    let actionType;
                    if (flags.exec)
                        actionType = codiac_domain_1.ProbeActionTypesEnum.exec;
                    if (flags.grpc)
                        actionType = codiac_domain_1.ProbeActionTypesEnum.grpc;
                    if (flags.http)
                        actionType = codiac_domain_1.ProbeActionTypesEnum.http;
                    if (flags.socket)
                        actionType = codiac_domain_1.ProbeActionTypesEnum.socket;
                    if (actionType == undefined) {
                        // Derive the actionType if possible
                        if (flags.grpcHost || flags.grpcPort)
                            actionType = codiac_domain_1.ProbeActionTypesEnum.grpc;
                        if (flags.execCommand)
                            actionType = codiac_domain_1.ProbeActionTypesEnum.exec;
                        if (flags.httpHeader || flags.httpHost || flags.httpPath || flags.httpPort || flags.httpScheme)
                            actionType = codiac_domain_1.ProbeActionTypesEnum.http;
                        if (flags.socketHost || flags.socketPort)
                            actionType = codiac_domain_1.ProbeActionTypesEnum.socket;
                        // Fall back to default
                        if (actionType == undefined)
                            actionType = codiac_domain_1.ProbeActionTypesEnum.exec;
                    }
                    let probeAction;
                    if (flags.predefined) {
                        // Calling out for a predefined Probe action 
                        probeAction = flags.predefined;
                    }
                    else {
                        // Probe action defined by the command args            
                        switch (actionType) {
                            case codiac_domain_1.ProbeActionTypesEnum.exec:
                                let cmd;
                                if (args.cmd) {
                                    cmd = args.cmd;
                                    let isep = this.argv.indexOf("--");
                                    if (isep >= 0) {
                                        if (isep + 1 < this.argv.length) {
                                            cmd = this.argv.slice(isep + 1);
                                        }
                                        else {
                                            cmd = undefined;
                                        }
                                    }
                                }
                                probeAction = { actionType: codiac_domain_1.ProbeActionTypesEnum.exec, command: cmd };
                                break;
                            case codiac_domain_1.ProbeActionTypesEnum.grpc:
                                probeAction = { actionType: codiac_domain_1.ProbeActionTypesEnum.grpc, port: flags.grpcPort, service: flags.grpcHost };
                                break;
                            case codiac_domain_1.ProbeActionTypesEnum.http:
                                probeAction = {
                                    actionType: codiac_domain_1.ProbeActionTypesEnum.http,
                                    scheme: (_c = (_b = flags.httpScheme) === null || _b === void 0 ? void 0 : _b.toUpperCase()) !== null && _c !== void 0 ? _c : "HTTP",
                                    host: flags.httpHost,
                                    port: (_e = (_d = flags.httpPort) !== null && _d !== void 0 ? _d : asset.port) !== null && _e !== void 0 ? _e : 80,
                                    path: (_f = flags.httpPath) !== null && _f !== void 0 ? _f : "",
                                    httpHeaders: this.toDictionary((_g = flags.httpHeader) !== null && _g !== void 0 ? _g : [])
                                };
                                break;
                            case codiac_domain_1.ProbeActionTypesEnum.socket:
                                probeAction = {
                                    actionType: codiac_domain_1.ProbeActionTypesEnum.socket,
                                    host: flags.socketHost,
                                    port: flags.socketPort
                                };
                                break;
                        }
                    }
                    let usage = type_safety_utils_1.TypeSafetyUtils.toEnum(codiac_domain_1.ProbeUsageTypesEnum, flags.usage);
                    if (usage == undefined)
                        throw new Error("Unrecognized usage type.");
                    probe = new codiac_domain_1.Probe(enterprise.name, assetName, usage, probeAction);
                    if (flags.delay)
                        probe.initialDelaySeconds = flags.delay;
                    if (flags.period)
                        probe.periodSeconds = flags.period;
                    if (flags.timeout)
                        probe.timeoutSeconds = flags.timeout;
                    if (flags['success-threshold'])
                        probe.successThreshold = flags['success-threshold'];
                    if (flags['failure-threshold'])
                        probe.failureThreshold = flags['failure-threshold'];
                    if (flags.versions)
                        probe.versionRange = (_h = flags.versions) !== null && _h !== void 0 ? _h : ">=0.0.0"; // They need the "All" filter to fully hydrate the index so that it gets selected as the identifier
                    // STEP 3) Confirm it
                    this._ui.userMessage("You are about to set this ".concat(probe.type.toString(), " probe on ", this._styler.hilite(assetName), ":\n")); // <-- adding a space in there
                    let display = _.omit(probe, "modified", "modifiedBy", "created", "createdBy", "id", "enterprise", "asset");
                    this._ui.userMessage((0, prettify_1.prettify)(display));
                    let confirm = yield this._ui.promptConfirm("confirm", "Continue:", false);
                    if (confirm) {
                        // STEP 4) write it          
                        this._ui.startSpinner(`Writing new Probe`);
                        let saved = yield this._codiacApi.Probe.upsert(probe); // relies on the db index for uniqueness
                        this._ui.stopSpinner("Done");
                        this._ui.userMessage(`${this._styler.colorGood("SUCCESSFUL!")} Created ${(typeof probeAction == 'string') ? `using predefined action [${probeAction}]` : `${probeAction.actionType} action`} as ${probe.type} probe for asset ${asset.name} in ${enterprise.name}.`);
                    }
                    else {
                        this._ui.userMessage("Cancelled.  No work was done.");
                    }
                }
                catch (err) {
                    this.logger.error(err);
                    this._ui.userMessage(`${this._styler.colorCritical("ERROR!")} ${(_j = err.name) !== null && _j !== void 0 ? _j : ""} ${(_k = err.message) !== null && _k !== void 0 ? _k : ""}.`);
                }
                finally {
                    yield this.closeConnections();
                    return;
                }
            }));
        });
    }
    /** @param kvps (in format key=value) */
    toDictionary(kvps) {
        let output = [];
        for (let expr of kvps) {
            let parts = expr.split("=");
            if (parts.length === 2) {
                output.push({ name: parts[0], value: parts[1] });
            }
            else if (parts.length === 1) {
                output.push({ name: parts[0], value: "" });
            }
        }
        return output;
    }
    /** Returns the ultiamate asset name and the flag telling whether the original user input was changed. */
    promptForAsset(assets, current) {
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            let selected = current;
            let changed = false;
            if (assets.length === 0) {
                throw new Error("This enterprise has no assets. You must create an asset to run this command.");
            }
            if (selected != undefined) {
                // Drop the current value if its invalid
                let found = assets.find(x => x.name === selected);
                if (found == undefined) {
                    selected = undefined;
                }
            }
            if (selected == undefined) {
                if (assets.length === 1) {
                    selected = assets[0].name; // Auto-select the one-and-only
                }
                else {
                    // PROMPT FOR IT
                    let choices = assets.map(a => { return { name: a.name, value: a.name }; });
                    selected = (yield this._ui.promptSelect("asset", "Asset: ", choices)); // TODO: This needs to be a fuzzy-select
                }
                changed = Boolean(current != undefined);
            }
            return [selected, changed];
        });
    }
}
exports.default = AssetProbeCreate;
AssetProbeCreate.description = 'Declares the implementation of a health or readiness probe for an asset.';
AssetProbeCreate.aliases = ["probe:create"];
AssetProbeCreate.examples = [
    `cod asset probe create --predefined builtin-heartbeat-file-exists`,
    `cod asset probe create -p 15 --exec --execCommand "cat /app/data/last-heartbeat.json"`,
    `cod asset probe create -n acme -a myapi -u readiness -p 15 --exec --execCommand "cat /app/data/last-heartbeat.json"`,
    `cod asset probe create -n acme -a myapi -u readiness -p 15 --exec --execCommand cat --execCommand /app/data/last-heartbeat.json`,
    `cod asset probe create -n acme -a myapi -u readiness -p 15 --http --httpScheme https --httpHost myapi.acme.com --httpPort 443 --httpPath /diag/heartbeat -r Accept=application/json -r user-agent=MyUserAgent`
];
AssetProbeCreate.strict = false; // disables argument validation so we can support variable-length arguments.
AssetProbeCreate.args = {
    cmd: core_1.Args.string({ description: '(requires --exec) command and its space-separated arguments to run in the target replica.' })
};
AssetProbeCreate.flags = {
    help: core_1.Flags.help({ char: 'h' }),
    enterprise: core_1.Flags.string({ char: 'n', required: false, description: 'The name of the enterprise to which this probe is to apply.' }),
    asset: core_1.Flags.string({ char: 'a', required: false, description: 'The name of the asset to which this probe is to apply.' }),
    versions: core_1.Flags.string({ char: 'v', description: 'Asset version range for which this probe is to be used. (optional: defaults to >=latest)' }),
    usage: core_1.Flags.string({ char: 'u', default: codiac_domain_1.ProbeUsageTypesEnum.readiness, required: true, description: 'The diagnostic purpose the probe will serve.', options: [codiac_domain_1.ProbeUsageTypesEnum.readiness, codiac_domain_1.ProbeUsageTypesEnum.liveness, codiac_domain_1.ProbeUsageTypesEnum.startup] }),
    delay: core_1.Flags.integer({ char: 'd', default: 0, description: 'The time (in seconds) to wait before the first execution of the probe.' }),
    period: core_1.Flags.integer({ char: 'p', default: 10, description: 'How often (in seconds) to fire the probe.' }),
    timeout: core_1.Flags.integer({ char: 't', required: false, description: 'How long to wait (in seconds) for a probe to respond before declaring it a failed execution (default and min 1 second).' }),
    'success-threshold': core_1.Flags.integer({ required: false, description: 'Minimum consecutive successes for the probe to be considered successful after having failed. Defaults to 1. Must be 1 for liveness and startup Probes. Minimum value is 1.' }),
    'failure-threshold': core_1.Flags.integer({ required: false, description: 'The number of unsuccessful probe executions to allow before determining the pod unhealthy.' }),
    predefined: core_1.Flags.string({ description: 'Calls out by name for an existing standard or shared probe action.  Use this argument instead of defining the probe action with the exec, grpc, http or socket arguments.', exclusive: ['exec', 'grpc', 'http', 'socket'] }),
    exec: core_1.Flags.boolean({ description: 'Declares the probe shall be carried out as an executable CLI command. A return code of zero indicates success.  Any nonzero result indicates failure.', exclusive: ['predefined', 'grpc', 'http', 'socket'] }),
    grpc: core_1.Flags.boolean({ description: 'Declares the probe shall be carried out as a grpc api request.', exclusive: ['predefined', 'exec', 'http', 'socket'] }),
    http: core_1.Flags.boolean({ description: 'Declares the probe shall be carried out as an http request. Any code greater than or equal to 200 and less than 400 indicates success. Any other code indicates failure.', exclusive: ['predefined', 'exec', 'grpc', 'socket'] }),
    socket: core_1.Flags.boolean({ description: 'Declares the probe shall be carried out as a TCP Socket request.', exclusive: ['predefined', 'exec', 'grpc', 'http'] }),
    action: core_1.Flags.string({ char: 'x', description: 'The JSON specification for the probe action itself.  The structure follows the actionType.' }),
    execCommand: core_1.Flags.string({
        multiple: true, dependsOn: ['exec'], description: '(For use only with exec) The executable command to run as the probe, as a single string, or as an array of commands and arguments.  If it returns any nonzero result, the probe gets interpreted as a negative/failure.',
        exclusive: ['grpcHost', 'grpcPort', 'httpScheme', 'httpHost', 'httpPort', 'httpPath', 'httpHeader', 'socketHost', 'socketPort']
    }),
    grpcHost: core_1.Flags.string({
        description: '(For use only with grpc).', dependsOn: ['grpc'],
        exclusive: ['execCommand', 'httpScheme', 'httpHost', 'httpPort', 'httpPath', 'httpHeader', 'socketHost', 'socketPort']
    }),
    grpcPort: core_1.Flags.integer({
        description: '(For use only with grpc).', dependsOn: ['grpc'],
        exclusive: ['execCommand', 'httpScheme', 'httpHost', 'httpPort', 'httpPath', 'httpHeader', 'socketHost', 'socketPort']
    }),
    httpScheme: core_1.Flags.string({
        options: ["http", "https"], description: '(For use only with http) The http protocol to use.', dependsOn: ['http'],
        exclusive: ['execCommand', 'grpcHost', 'grpcPort', 'socketHost', 'socketPort']
    }),
    httpHost: core_1.Flags.string({
        description: '.', dependsOn: ['http'],
        exclusive: ['execCommand', 'grpcHost', 'grpcPort', 'socketHost', 'socketPort']
    }),
    httpPort: core_1.Flags.integer({
        description: '(For use only with http).', dependsOn: ['http'],
        exclusive: ['execCommand', 'grpcHost', 'grpcPort', 'socketHost', 'socketPort']
    }),
    httpPath: core_1.Flags.string({
        description: '(For use only with http).', dependsOn: ['http'],
        exclusive: ['execCommand', 'grpcHost', 'grpcPort', 'socketHost', 'socketPort']
    }),
    httpHeader: core_1.Flags.string({
        char: 'r', multiple: true, dependsOn: ['http'], description: '(For use only with http) Key value pair (in format x=y) to be used as an http header in an http probe action.',
        exclusive: ['execCommand', 'grpcHost', 'grpcPort', 'socketHost', 'socketPort']
    }),
    socketHost: core_1.Flags.string({
        description: '(For use only with socket).', dependsOn: ['socket'],
        exclusive: ['execCommand', 'grpcHost', 'grpcPort', 'httpScheme', 'httpHost', 'httpPort', 'httpPath', 'httpHeader']
    }),
    socketPort: core_1.Flags.integer({
        description: '(For use only with socket).', dependsOn: ['socket'],
        exclusive: ['execCommand', 'grpcHost', 'grpcPort', 'httpScheme', 'httpHost', 'httpPort', 'httpPath', 'httpHeader']
    })
};
//# sourceMappingURL=create.js.map