// deno-lint-ignore no-explicit-any
const builtins: any = {};

// deno-lint-ignore no-explicit-any
const G: any = {};

// deno-lint-ignore no-explicit-any
export const addToContext = (name: string, value: any) => {
	builtins[name] = value;
}

export const getBuiltins = () => {
	return builtins;
}

addToContext("eval", eval);

const context = new Proxy(G, {
	get: (target, prop) => {
		if (prop in builtins) {
			return builtins[prop as keyof typeof builtins];
		}
		return target[prop];
	},
	set: (target, prop, value) => {
		target[prop] = value;
		return true;
	},
	has: () => {
		return true;
	},
});

export const runCode = (code: string) => {
	try {
		new Function(code);
	} catch (err) {
		throw err;
	}
	const fn = new Function("context", `
		with (context) {
			${code}
		}
	`);
	return fn(context);
}

export const evalCode = (code: string) => {
	try {
		return runCode(`return eval("(${code.replaceAll('"', '\\"')})");`);
	} catch (err) {
		if (err.name === "SyntaxError") {
			return runCode(`return eval("${code.replaceAll('"', '\\"')}");`);
		} else {
			throw err;
		}
	}
}