/*
 * Copyright 2019-2020 IBM Corporation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
import { ExecType, i18n, isHeadless, inBrowser } from '@kui-shell/core';
import commandPrefix from '../command-prefix';
import { getContextForArgv, getFileForArgv, getNamespace } from './options';
import { stringToTable } from '../../lib/view/formatTable';
const strings = i18n('plugin-kubectl');
/** No-op argument preparation */
export const NoPrepare = (args) => args.command;
/** Standard status preparation */
function DefaultPrepareForStatus(cmd, args) {
    const rest = args.argvNoOptions.slice(args.argvNoOptions.indexOf(cmd) + 1).join(' ');
    return `${getFileForArgv(args, true)}${rest}`;
}
/**
 * Execute the given command in the browser; this dispatches to
 * _kubectl, which runs on the proxy (for electron and headless, these
 * are the same machine).
 *
 */
export function doExecWithoutPty(args, prepare = NoPrepare, exec = 'kubectl') {
    return __awaiter(this, void 0, void 0, function* () {
        const raw = `_${exec}$1`;
        const command = prepare(args)
            .replace(new RegExp(`^${exec}(\\s)?`), raw)
            .replace(/^k(\s)?/, raw);
        const dbl = new RegExp(`_${exec}(\\s)?`);
        const doubleCheck = dbl.test(command) ? command : `_${exec} ${command}`;
        return args.REPL.qexec(doubleCheck, undefined, undefined, args.execOptions).catch((err) => {
            if (err.code === 500 || err.statusCode === 500) {
                err.code = err.statusCode = 500;
            }
            throw err;
        });
    });
}
/**
 * Behaves as does `exec`, except that it projects out just the
 * `stdout` part -- thus ignoring the exit `code` and `stderr`.
 *
 */
export function doExecWithStdout(args, prepare = NoPrepare, exec) {
    return doExecWithoutPty(args, prepare, exec).then(_ => _.content.stdout);
}
/**
 * Execute the given command using a pty
 *
 */
export function doExecWithPty(args, prepare = NoPrepare) {
    return __awaiter(this, void 0, void 0, function* () {
        if (isHeadless() || (!inBrowser() && args.execOptions.raw)) {
            return doExecWithStdout(args, prepare);
        }
        else {
            //
            // For commands `kubectl (--help/-h)` and `k (--help/-h)`, render usage model;
            // Otherwise, execute the given command using a pty
            //
            const commandToPTY = args.command.replace(/^k(\s)/, 'kubectl$1');
            if (args.execOptions.onInit) {
                args.execOptions.quiet = true; // don't ever emit anything on your own
                args.execOptions.replSilence = true; // repl: same thing
                args.execOptions.echo = false; // do not even echo "ok"
            }
            return args.REPL.qexec(`sendtopty ${commandToPTY}`, args.block, undefined, args.execOptions.onInit
                ? args.execOptions
                : Object.assign({}, args.execOptions, {
                    rawResponse: true,
                    quiet: args.execOptions.quiet === undefined
                        ? args.execOptions.type === ExecType.TopLevel
                            ? false
                            : undefined
                        : args.execOptions.quiet
                })).catch((err) => {
                if (err.code === 500 || err.statusCode === 500) {
                    err.code = err.statusCode = 500;
                }
                throw err;
            });
        }
    });
}
/**
 * Execute the given command using a pty, but return a string
 *
 */
export function doExecWithStdoutViaPty(args, prepare = NoPrepare) {
    return __awaiter(this, void 0, void 0, function* () {
        // eslint-disable-next-line no-async-promise-executor
        return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () {
            let stdout = '';
            // a bit of plumbing: tell the PTY that we will be handling everything
            const myExecOptions = Object.assign({}, args.execOptions, {
                rethrowErrors: true,
                quiet: true,
                replSilence: true,
                echo: false,
                // the PTY will call this when the PTY process is ready; in
                // return, we send it back a consumer of streaming output
                onInit: () => {
                    return (chunk) => {
                        if (typeof chunk === 'string') {
                            stdout += chunk;
                        }
                    };
                }
            });
            const myArgs = Object.assign({}, args, { execOptions: myExecOptions });
            yield doExecWithPty(myArgs, prepare).catch(err => {
                console.error(err);
                reject(err);
            });
            resolve(stdout);
        }));
    });
}
/**
 * Decide whether to use a pty or a raw exec.
 *
 */
export function exec(args, prepare = NoPrepare, exec = 'kubectl') {
    return __awaiter(this, void 0, void 0, function* () {
        if (args.argvNoOptions.includes('|')) {
            return Promise.resolve({
                content: {
                    code: 0,
                    stdout: yield doExecWithPty(args, prepare),
                    stderr: '',
                    wasSentToPty: true
                }
            });
        }
        else {
            return doExecWithoutPty(args, prepare, exec);
        }
    });
}
/**
 * Behaves as does `exec`, except that it projects out just the
 * `stdout` part and parses it into a Table model.
 *
 */
export function doExecWithTable(args, prepare = NoPrepare, command = 'kubectl', { usePty = false, nameColumn, verb, entityType } = {}) {
    return __awaiter(this, void 0, void 0, function* () {
        const response = usePty
            ? { content: { stdout: yield doExecWithStdoutViaPty(args, prepare), stderr: undefined } }
            : yield doExecWithoutPty(args, prepare, command);
        const table = yield stringToTable(response.content.stdout, response.content.stderr, args, command, verb, entityType, nameColumn);
        if (typeof table === 'string') {
            throw new Error(strings('Unable to parse table'));
        }
        else {
            return table;
        }
    });
}
/**
 * Execute a command, and then execute the status command which will
 * poll until the given FinalState is reached.
 *
 */
export const doExecWithStatus = (cmd, finalState, command = 'kubectl', prepareForExec = NoPrepare, prepareForStatus = DefaultPrepareForStatus) => (args) => __awaiter(void 0, void 0, void 0, function* () {
    const response = yield exec(args, prepareForExec, command);
    if (response.content.code !== 0) {
        const err = new Error(response.content.stderr);
        err.code = response.content.code;
        throw err;
    }
    else if (isHeadless()) {
        return response.content.stdout;
    }
    else {
        const contextArgs = `-n ${yield getNamespace(args)} ${getContextForArgv(args)}`;
        const watchArgs = `--final-state ${finalState} --watch`;
        // this helps with error reporting: if something goes wrong with
        // displaying "status", we can always report the initial response
        // from the exec command
        const errorReportingArgs = `--response "${response.content.stdout}"`;
        const statusArgs = prepareForStatus(cmd, args);
        const commandArgs = `--command ${command}`;
        const statusCmd = `${commandPrefix} status ${statusArgs} ${watchArgs} ${contextArgs} ${errorReportingArgs} ${commandArgs}`;
        return args.REPL.qexec(statusCmd, args.block);
    }
});
export function doExecWithRadioTable(resources, defaultSelectedIdx, onSelect, { title = resources.length === 0 ? undefined : resources[0].kind, nameColumnTitle = 'NAME' } = {}) {
    return __awaiter(this, void 0, void 0, function* () {
        if (resources.length > 0) {
            return {
                apiVersion: 'kui-shell/v1',
                kind: 'RadioTable',
                title,
                defaultSelectedIdx,
                header: {
                    cells: [nameColumnTitle]
                },
                body: resources.map(resource => {
                    const name = resource.metadata.name;
                    return {
                        nameIdx: 0,
                        cells: [name],
                        onSelect: () => onSelect(name, resource)
                    };
                })
            };
        }
    });
}
export default exec;
//# sourceMappingURL=exec.js.map