/**
 * WordPress dependencies
 */
import { useMemo, useRef } from '@wordpress/element';
import { useIsomorphicLayoutEffect } from '@wordpress/compose';

/**
 * Internal dependencies
 */
import useRegistry from '../registry-provider/use-registry';
import type { DataRegistry } from '../../types';

type DispatchMap = (
	dispatch: DataRegistry[ 'dispatch' ],
	registry: DataRegistry
) => Record< string, ( ...args: unknown[] ) => unknown >;

/**
 * Custom react hook for returning aggregate dispatch actions using the provided
 * dispatchMap.
 *
 * Currently this is an internal api only and is implemented by `withDispatch`
 *
 * @param dispatchMap Receives the `registry.dispatch` function as
 *                    the first argument and the `registry` object
 *                    as the second argument.  Should return an
 *                    object mapping props to functions.
 * @param deps        An array of dependencies for the hook.
 * @return An object mapping props to functions created by the passed
 *         in dispatchMap.
 */
const useDispatchWithMap = (
	dispatchMap: DispatchMap,
	deps: unknown[]
): Record< string, ( ...args: unknown[] ) => unknown > => {
	const registry = useRegistry();
	const currentDispatchMapRef = useRef( dispatchMap );

	useIsomorphicLayoutEffect( () => {
		currentDispatchMapRef.current = dispatchMap;
	} );

	return useMemo( () => {
		const currentDispatchProps = currentDispatchMapRef.current(
			registry.dispatch,
			registry
		);
		return Object.fromEntries(
			Object.entries( currentDispatchProps ).map(
				( [ propName, dispatcher ] ) => {
					if ( typeof dispatcher !== 'function' ) {
						// eslint-disable-next-line no-console
						console.warn(
							`Property ${ propName } returned from dispatchMap in useDispatchWithMap must be a function.`
						);
					}
					return [
						propName,
						( ...args: unknown[] ) =>
							currentDispatchMapRef
								.current( registry.dispatch, registry )
								[ propName ]( ...args ),
					];
				}
			)
		);
	}, [ registry, ...deps ] );
};

export default useDispatchWithMap;
