// ============================================================================
// Safe Wrappers for Security-Sensitive Globals
// ============================================================================

/**
 * SafeObject - Blocks dangerous Object methods that could lead to RCE
 *
 * Blocked methods:
 * - defineProperty, setPrototypeOf: Prevent prototype pollution
 * - getOwnPropertyDescriptor: Prevent property descriptor manipulation
 * - __defineGetter__, __defineSetter__: Legacy descriptor manipulation
 */
export const SafeObject = new Proxy(Object, {
	get(target, prop) {
		// Block dangerous methods (return undefined)
		const blockedMethods = [
			'defineProperty',
			'defineProperties',
			'setPrototypeOf',
			'getOwnPropertyDescriptor',
			'getOwnPropertyDescriptors',
			'__defineGetter__',
			'__defineSetter__',
			'__lookupGetter__',
			'__lookupSetter__',
		];

		if (blockedMethods.includes(prop as string)) {
			return undefined;
		}

		// Block getPrototypeOf by throwing (more secure than returning undefined)
		if (prop === 'getPrototypeOf') {
			throw new Error('Object.getPrototypeOf is not allowed');
		}

		// Wrap Object.create to accept only one argument (blocks property descriptor injection)
		if (prop === 'create') {
			return (proto: object | null) => Object.create(proto);
		}

		// Allow other Object methods
		const value = (target as any)[prop];
		if (typeof value === 'function') {
			// Use arrow function wrapper to preserve 'this' binding
			return (...args: any[]) => value.apply(target, args);
		}
		return value;
	},
});

/**
 * Properties blocked on Error and all Error subclasses.
 * These can be exploited for sandbox escape via V8's stack trace API.
 */
const blockedErrorProperties = new Set([
	'captureStackTrace',
	'prepareStackTrace',
	'stackTraceLimit',
	'__defineGetter__',
	'__defineSetter__',
	'__lookupGetter__',
	'__lookupSetter__',
]);

/**
 * SafeError - Blocks stack manipulation methods on Error constructor.
 */
export const SafeError = new Proxy(Error, {
	get(target, prop) {
		if (blockedErrorProperties.has(prop as string)) {
			return undefined;
		}
		const value = (target as any)[prop];
		if (typeof value === 'function') {
			return (...args: any[]) => value.apply(target, args);
		}
		return value;
	},
	set(target, prop, value) {
		// Block setting prepareStackTrace
		if (prop === 'prepareStackTrace') {
			return false;
		}
		(target as any)[prop] = value;
		return true;
	},
	defineProperty() {
		return false;
	},
});

/**
 * Creates a safe wrapper for Error subclasses (TypeError, SyntaxError, etc.)
 * Blocks the same dangerous properties as SafeError for defense in depth.
 */
export function createSafeErrorSubclass<T extends ErrorConstructor>(ErrorClass: T): T {
	return new Proxy(ErrorClass, {
		get(target, prop) {
			if (blockedErrorProperties.has(prop as string)) {
				return undefined;
			}
			const value = (target as any)[prop];
			if (typeof value === 'function') {
				return (...args: any[]) => value.apply(target, args);
			}
			return value;
		},
		set() {
			return false;
		},
		defineProperty() {
			return false;
		},
	}) as T;
}

// ============================================================================
// ExpressionError - used by tournament-generated error handlers
// ============================================================================

export class ExpressionError extends Error {
	constructor(message: string) {
		super(message);
		this.name = 'ExpressionError';
	}
}

// ============================================================================
// Runtime sanitizer for dynamic property access
// Generated by PrototypeSanitizer: obj[expr] → obj[this.__sanitize(expr)]
// Must match the blocklist in packages/workflow/src/expression-sandboxing.ts
// ============================================================================

const unsafeObjectProperties = new Set([
	'__proto__',
	'prototype',
	'constructor',
	'__defineGetter__',
	'__defineSetter__',
	'__lookupGetter__',
	'__lookupSetter__',
	'toString',
	'valueOf',
	'toLocaleString',
	'hasOwnProperty',
	'isPrototypeOf',
	'propertyIsEnumerable',
]);

export function __sanitize(value: unknown): unknown {
	// Symbols are not string-coercible and fall outside the denylist; pass through.
	if (typeof value === 'symbol') return value;
	// Coerce once and return the string so the caller uses the already-checked key.
	const key = String(value);
	if (unsafeObjectProperties.has(key)) {
		throw new ExpressionError(`Cannot access "${key}" due to security concerns`);
	}
	return key;
}
