"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_exec_1 = require("../../../command-base-exec");
const prettify_1 = require("../../../prettify");
const type_safety_utils_1 = require("../../../uilogic/type-safety-utils");
class AssetProbeCreate extends command_base_exec_1.CommandBaseExec {
    captureArgs() {
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            const { flags } = yield this.parse(AssetProbeCreate);
            let partial = {};
            if (flags.enterprise)
                partial.enterprise = flags.enterprise;
            return [partial, false];
        });
    }
    buildCandidate_silently(partial, rootMap) {
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            const { args, flags } = yield this.parse(AssetProbeCreate);
            const enterprise = partial.enterprise;
            const ent = rootMap.enterprises.find(e => e.name === enterprise);
            if (!ent)
                throw new Error(`Enterprise "${enterprise}" not found.`);
            // Select asset
            let [assetName] = yield this.promptForAsset(ent.assets, flags.asset);
            const asset = ent.assets.find(a => a.name === assetName);
            // Assemble probe
            const { probe, probeAction } = this.assembleProbe(enterprise, assetName, asset, args, flags);
            return { enterprise, assetName, probe, probeAction };
        });
    }
    buildCandidate_interactively(partial, rootMap, takeDefaults) {
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            const { args, flags } = yield this.parse(AssetProbeCreate);
            const enterprise = partial.enterprise;
            const ent = rootMap.enterprises.find(e => e.name === enterprise);
            if (!ent)
                throw new Error(`Enterprise "${enterprise}" not found.`);
            // Select asset
            let [assetName] = yield this.promptForAsset(ent.assets, flags.asset);
            const asset = ent.assets.find(a => a.name === assetName);
            // Assemble probe
            const { probe, probeAction } = this.assembleProbe(enterprise, assetName, asset, args, flags);
            // Confirm
            this._ui.userMessage("You are about to set this ".concat(probe.type.toString(), " probe on ", this._styler.hilite(assetName), ":\n"));
            const display = _.omit(probe, "modified", "modifiedBy", "created", "createdBy", "id", "enterprise", "asset");
            this._ui.userMessage((0, prettify_1.prettify)(display));
            const confirm = yield this._ui.promptConfirm("confirm", "Continue:", false);
            if (!confirm)
                return undefined;
            return { enterprise, assetName, probe, probeAction };
        });
    }
    buildCommandString(candidate, cmd) {
        cmd.push("-n", candidate.enterprise);
        cmd.push("-a", candidate.assetName);
        cmd.push("-u", candidate.probe.type);
        if (typeof candidate.probeAction === 'string') {
            cmd.push("--predefined", candidate.probeAction);
        }
        cmd.push("--silent");
        return cmd;
    }
    executeTask(candidate) {
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            this._ui.startSpinner(`Writing new Probe`);
            yield this._codiacApi.Probe.upsert(candidate.probe);
            this._ui.stopSpinner("Done");
            const actionDesc = (typeof candidate.probeAction === 'string')
                ? `using predefined action [${candidate.probeAction}]`
                : `${candidate.probeAction.actionType} action`;
            this._ui.userMessage(`${this._styler.colorGood("SUCCESSFUL!")} Created ${actionDesc} as ${candidate.probe.type} probe for asset ${candidate.assetName} in ${candidate.enterprise}.`);
        });
    }
    resolvePort(port) {
        if (typeof port === "number")
            return port;
        if (Array.isArray(port))
            return port[0];
        if (port && typeof port === "object" && "external" in port)
            return port.external;
        return undefined;
    }
    assembleProbe(enterprise, assetName, asset, args, flags) {
        var _a, _b, _c, _d, _e, _f, _g;
        // Determine action type
        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) {
            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;
            if (actionType == undefined)
                actionType = codiac_domain_1.ProbeActionTypesEnum.exec;
        }
        let probeAction;
        if (flags.predefined) {
            probeAction = flags.predefined;
        }
        else {
            switch (actionType) {
                case codiac_domain_1.ProbeActionTypesEnum.exec:
                    let cmd;
                    if (args.cmd) {
                        cmd = args.cmd;
                        let isep = this.argv.indexOf("--");
                        if (isep >= 0) {
                            cmd = (isep + 1 < this.argv.length) ? this.argv.slice(isep + 1) : 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: (_b = (_a = flags.httpScheme) === null || _a === void 0 ? void 0 : _a.toUpperCase()) !== null && _b !== void 0 ? _b : "HTTP",
                        host: flags.httpHost,
                        port: (_d = (_c = flags.httpPort) !== null && _c !== void 0 ? _c : this.resolvePort(asset.port)) !== null && _d !== void 0 ? _d : 80,
                        path: (_e = flags.httpPath) !== null && _e !== void 0 ? _e : "",
                        httpHeaders: this.toDictionary((_f = flags.httpHeader) !== null && _f !== void 0 ? _f : [])
                    };
                    break;
                case codiac_domain_1.ProbeActionTypesEnum.socket:
                    probeAction = {
                        actionType: codiac_domain_1.ProbeActionTypesEnum.socket,
                        host: flags.socketHost,
                        port: flags.socketPort
                    };
                    break;
                default:
                    probeAction = { actionType: codiac_domain_1.ProbeActionTypesEnum.exec };
            }
        }
        const usage = type_safety_utils_1.TypeSafetyUtils.toEnum(codiac_domain_1.ProbeUsageTypesEnum, flags.usage);
        if (usage == undefined)
            throw new Error("Unrecognized usage type.");
        const probe = new codiac_domain_1.Probe(enterprise, 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 = (_g = flags.versions) !== null && _g !== void 0 ? _g : ">=0.0.0";
        return { probe, probeAction };
    }
    /** @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 ultimate asset name and a flag indicating 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) {
                const found = assets.find(x => x.name === selected);
                if (found == undefined)
                    selected = undefined;
            }
            if (selected == undefined) {
                if (assets.length === 1) {
                    selected = assets[0].name;
                }
                else {
                    const choices = assets.map(a => ({ name: a.name, value: a.name }));
                    selected = (yield this._ui.promptSelectFuzzy("asset", "Asset:", () => tslib_1.__awaiter(this, void 0, void 0, function* () { return choices; })));
                }
                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.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.' }),
    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: '(For use only with http) The host name or IP to probe.', dependsOn: ['http'],
        exclusive: ['execCommand', 'grpcHost', 'grpcPort', 'socketHost', 'socketPort']
    }),
    httpPort: core_1.Flags.integer({
        description: '(For use only with http) The port to probe.', dependsOn: ['http'],
        exclusive: ['execCommand', 'grpcHost', 'grpcPort', 'socketHost', 'socketPort']
    }),
    httpPath: core_1.Flags.string({
        description: '(For use only with http) The URL path to probe.', 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) The host to connect to.', dependsOn: ['socket'],
        exclusive: ['execCommand', 'grpcHost', 'grpcPort', 'httpScheme', 'httpHost', 'httpPort', 'httpPath', 'httpHeader']
    }),
    socketPort: core_1.Flags.integer({
        description: '(For use only with socket) The port to connect to.', dependsOn: ['socket'],
        exclusive: ['execCommand', 'grpcHost', 'grpcPort', 'httpScheme', 'httpHost', 'httpPort', 'httpPath', 'httpHeader']
    }),
    silent: core_1.Flags.boolean({ exclusive: ["echo", "to-script"], description: 'Executes without any user interaction; fails on missing or invalid arguments.' }),
    echo: core_1.Flags.boolean({ exclusive: ["to-script", "silent"], description: 'Renders the equivalent non-interactive command for future use before executing.' }),
    "to-script": core_1.Flags.boolean({ exclusive: ["echo", "silent"], description: 'Renders the equivalent non-interactive command without executing it.' }),
};
AssetProbeCreate.examples = [
    { description: "Create a probe interactively.", command: "<%= config.bin %> <%= command.id %>" },
    { description: "Exec probe using a predefined action.", command: "<%= config.bin %> <%= command.id %> --predefined builtin-heartbeat-file-exists" },
    { description: "Exec probe with inline command.", command: "<%= config.bin %> <%= command.id %> -p 15 --exec -- \"cat /app/data/last-heartbeat.json\"" },
    { description: "Http probe with headers.", command: "<%= config.bin %> <%= command.id %> -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" },
    { description: "Non-interactive invocation.", command: "<%= config.bin %> <%= command.id %> -n acme -a myapi --predefined builtin-heartbeat-file-exists --silent" },
    { description: "Renders the equivalent silent command without executing.", command: "<%= config.bin %> <%= command.id %> -n acme -a myapi --predefined builtin-heartbeat-file-exists --to-script" },
];
//# sourceMappingURL=create.js.map