import { VueConstructor } from 'vue';

/**
 * Allows extensions to modify the returned value from
 * the specified method by providing a callback.
 * 
 * The callback receives the return value of the underlying
 * method as its first parameter. Additional parameters
 * passed to the underlying method are also passed into
 * the callback.
 * 
 * @param component The component to extend
 * @param method The method to extend
 * @param callback Custom logic to patch onto the original method
 */
export function extend(component: VueConstructor, method: string, callback: (...args: any) => any) {
	const methods = (component as any).extendOptions.methods;
	const handler = methods[method];

	methods[method] = function(...args: any) {
		const value = handler ? handler.apply(this, args) : undefined;

		callback.apply(this, [value, ...args]);

		return value;
	};

	Object.assign(methods[method], handler);
}

/**
 * Allows extensions to modify the returned value from
 * the specified method by providing a callback.
 * 
 * The callback receives the return value of the underlying
 * method as its first parameter. Additional parameters
 * passed to the underlying method are also passed into
 * the callback.
 * 
 * @param component The component to extend
 * @param method The method to extend
 * @param callback Custom logic to patch onto the original method
 */
export function override(component: VueConstructor, method: string, callback: (...args: any) => any) {
	const methods = (component as any).extendOptions.methods;
	const handler = methods[method];

	methods[method] = function(...args: any) {
		const value = handler ? handler.apply(this, args) : undefined;

		return callback.apply(this, [value, ...args]);
	};

	Object.assign(methods[method], handler);
}