// This works by proxying every function call and wrapping a try-catch block to filter out redundant and confusing
// `RuntimeError: unreachable` exceptions that would normally be printed in the browser's JS console upon a panic.
export function panicProxy<T extends object>(module: T): T {
	const proxyHandler = {
		get(target: T, propKey: string | symbol, receiver: unknown): unknown {
			const targetValue = Reflect.get(target, propKey, receiver);

			// Keep the original value being accessed if it isn't a function
			const isFunction = typeof targetValue === "function";
			if (!isFunction) return targetValue;

			// Special handling to wrap the return of a constructor in the proxy
			const isClass = isFunction && /^\s*class\s+/.test(String(targetValue));
			if (isClass) {
				return function (...args: unknown[]): unknown {
					// All three of these comment lines are necessary to suppress errors at both compile time and while editing this file (@ts-expect-error doesn't work here while editing the file)
					// eslint-disable-next-line @typescript-eslint/ban-ts-comment
					// @ts-ignore
					const result = new targetValue(...args);

					return panicProxy(result);
				};
			}

			// Replace the original function with a wrapper function that runs the original in a try-catch block
			return function (...args: unknown[]): unknown {
				let result;
				try {
					// @ts-expect-error TypeScript does not know what `this` is, since it should be able to be anything
					result = targetValue.apply(this, args);
				} catch (err) {
					// Suppress `unreachable` WebAssembly.RuntimeError exceptions
					if (!String(err).startsWith("RuntimeError: unreachable")) throw err;
				}
				return result;
			};
		},
	};

	return new Proxy<T>(module, proxyHandler);
}
