const { promisify } = require('util');
const { exec } = require('child_process');
const zws = String.fromCharCode(8203);
let sensitivePattern;

/**
 * Contains static methods to be used throughout klasa
 */
class Util {

	/**
	 * This class may not be initiated with new
	 */
	constructor() {
		throw new Error('This class may not be initiated with new');
	}

	/**
	 * Makes a codeblock markup string
	 * @param {string} lang The codeblock language
	 * @param {string} expression The expression to be wrapped in the codeblock
	 * @returns {string}
	 */
	static codeBlock(lang, expression) {
		return `\`\`\`${lang}\n${expression || '\u200b'}\`\`\``;
	}

	/**
	 * Cleans sensitive info from strings
	 * @param {string} text The text to clean
	 * @returns {string}
	 */
	static clean(text) {
		if (typeof text === 'string') return text.replace(sensitivePattern, '「ｒｅｄａｃｔｅｄ」').replace(/`/g, `\`${zws}`).replace(/@/g, `@${zws}`);
		return text;
	}

	/**
	 * Initializes the sensitive patterns for clean()
	 * @private
	 * @param {KlasaClient} client The Klasa client
	 */
	static initClean(client) {
		const patterns = [];
		if (client.token) patterns.push(client.token);
		if (client.user.email) patterns.push(client.user.email);
		if (client.password) patterns.push(client.password);
		sensitivePattern = new RegExp(patterns.join('|'), 'gi');
	}

	/**
	 * Converts a string to Title Case
	 * @param {string} str The string to titlecaseify
	 * @returns {string}
	 */
	static toTitleCase(str) {
		return str.replace(/\w\S*/g, (txt) => txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase());
	}

	/**
	 * Generates an error object used for argument reprompting
	 * @param {Error} error An error object
	 * @param {number} code The status code to assign to the error
	 * @returns {Error}
	 */
	static newError(error, code) {
		if (error.status) {
			this.statusCode = error.response.res.statusCode;
			this.statusMessage = error.response.res.statusMessage;
			this.code = error.response.body.code;
			this.message = error.response.body.message;
			return this;
		}
		this.code = code || null;
		this.message = error;
		this.stack = error.stack || null;
		return this;
	}

	/**
	 * Cleans a string from regex injection
	 * @param {string} str The string to clean
	 * @returns {string}
	 */
	static regExpEsc(str) {
		return str.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&');
	}

	/**
	 * Applies an interface to a class
	 * @param {Object} base The interface to apply to a structure
	 * @param {Object} structure The structure to apply the interface to
	 * @param {string[]} [skips=[]] The methods to skip when applying this interface
	 */
	static applyToClass(base, structure, skips = []) {
		for (const method of Object.getOwnPropertyNames(base.prototype)) {
			if (!skips.includes(method)) Object.defineProperty(structure.prototype, method, Object.getOwnPropertyDescriptor(base.prototype, method));
		}
	}

}

Util.exec = promisify(exec);

Util.wait = promisify(setTimeout);

module.exports = Util;

@@ -2902,47 +2902,1295 @@
 %0A%7D%0A%0A
-Util.exec = promisify(exec);%0A%0AUtil.wait
+/**%0A * @typedef %7BObject%7D execOptions%0A * @memberof %7BUtil%7D%0A * @property %7Bstring%7D %5Bcwd=process.cwd()%5D Current working directory of the child process%0A * @property %7BObject%7D %5Benv=%7B%7D%5D Environment key-value pairs%0A * @property %7Bstring%7D %5Bencoding='utf8'%5D encoding to use%0A * @property %7Bstring%7D %5Bshell=os === unix ? '/bin/sh' : process.env.ComSpec%5D Shell to execute the command with%0A * @property %7Bnumber%7D %5Btimeout=0%5D %0A * @property %7Bnumber%7D %5BmaxBuffer=200*1024%5D Largest amount of data in bytes allowed on stdout or stderr. If exceeded, the child process is terminated.%0A * @property %7Bstring%7Cnumber%7D %5BkillSignal='SIGTERM'%5D %3Cstring%3E %7C %3Cinteger%3E (Default: 'SIGTERM')%0A * @property %7Bnumber%7D %5Buid%5D Sets the user identity of the process.%0A * @property %7Bnumber%7D %5Bgid%5D Sets the group identity of the process.%0A */%0A%0A/**%0A * Promisified version of child_process.exec for use with await%0A * @param %7Bstring%7D command The command to run%0A * @param %7BexecOptions%7D options The options to pass to exec%0A * @returns %7Bstring%7D%0A */%0AUtil.exec = promisify(exec);%0A%0A/**%0A * Promisified version of setTimeout for use with await%0A * @param %7Bnumber%7D delay The amount of time in ms to delay%0A * @param %7Bany%7D args Any args to pass to the .then (mostly pointless in this form)%0A * @returns %7BPromise%3Cany%3E%7D The args value passed in%0A */%0AUtil.sleep
  = p

document util.exec and util.sleep