// https://stackabuse.com/executing-shell-commands-with-node-js/

const { execSync, exec, spawn } = require('child_process');

class ExecWrapper {
    /** Executes shell command.
     * @param {String|String[]} command 
     * @param {Function} parserFunc function to parse response, if null original response will be send.
     * @returns shell command result, if parserFunc passed - it will be specific error type.
     */
    static execSync(command, parserFunc) {
        const result = { isSuccess: true, data: null };

        const cmd = ExecWrapper.tryCreateCommand(command);
        console.log(cmd)

        try {
            result.data = execSync(cmd).toString('utf8');
        } catch (error) {
            result.error = error;
            result.isSuccess = false;
        }

        return parserFunc ? parserFunc(result) : result;
    }

    static tryCreateCommand(command) {
        if (typeof command === "string") {
            return command;
        }

        return command.filter((option) => Boolean(option)).join(' ');
    }

    static async exec(command, parserFunc) {
        let result = { isSuccess: true, data: null };
        const cmd = ExecWrapper.tryCreateCommand(command);

        return new Promise((resolve) => {
            exec(cmd, (error, stdout, stderr) => {
                if (error || stderr) {
                    result.isSuccess = false;
                    result.error = {
                        stdout: error
                    };
                    result.status = error.code || error.status;
                } else {
                    result.data = stdout.toString();
                }

                result = parserFunc ? parserFunc(result) : result;

                resolve(result)
            });
        })
    }

    /**
     * Wrapper for child_process.spawn.
     * @param {String|String[]} command Index 0 - application to run, Other elements - arguments. 
     * @param {Function?} parserFunc function to parse response. 
     * @returns {Object} result
     */
    static async spawn(command, parserFunc) {
        const result = { isSuccess: true, data: null };
        const cmd = command[0];
        const options = ExecWrapper.tryCreateCommand(command.slice(1));

        const cmdResponse = spawn(cmd, options);

        return new Promise((resolve) => {
            let response = null;
            cmdResponse.stdout.on('data', (data) => {
                response = data.toString();
            });

            cmdResponse.stderr.on('data', (data) => {
                result.isSuccess = false;
                response = data;
            });

            cmdResponse.on('close', (code) => {
                result.status = code;
                if (code !== 0) {
                    result.isSuccess = false;
                    result.error = response;
                } else {
                    result.data = response;
                }
                resolve(parserFunc ? parserFunc(result) : result)
            });
        });
    }
}

module.exports = ExecWrapper;
