/**
 * @license Copyright (c) 2017-2025, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md.
 */

/**
 * Creates a watcher that detects when all elements lose focus via keyboard navigation.
 * It differentiates between blur caused by mouse clicks and keyboard navigation.
 * Additionally monitors keyboard events to detect navigation away from elements.
 * Only triggers when focus moves outside ALL of the monitored elements.
 *
 * @param config - Configuration object.
 * @param config.elements - Element or array of elements to monitor for keyboard-based blur.
 * @param config.callback - Function to call when keyboard blur occurs outside of all elements.
 * @param [config.checkDelay=50] - Delay in ms to check focus after keyboard event.
 * @returns Cleanup function to remove the listeners.
 */
export function createKeyboardBlurWatcher( { elements, callback, checkDelay = 50 } ) {
	let isMouseDown = false;

	// Convert single element to array for consistent handling
	if ( !Array.isArray( elements ) ) {
		elements = [ elements ];
	}

	// Track if mouse is being used
	const handleMouseDown = () => {
		isMouseDown = true;
	};

	const handleMouseUp = () => {
		isMouseDown = false;
	};

	// Check if current focus is inside any of our elements
	const isFocusWithinElements = () => {
		return elements.some( element =>
			element === document.activeElement || element.contains( document.activeElement )
		);
	};

	// Function to check focus state and call callback if needed
	const checkFocusState = event => {
		if ( !isFocusWithinElements() ) {
			callback( event );
		}
	};

	// When any element loses focus
	const handleBlur = event => {
		if ( isMouseDown ) {
			return; // Ignore blur from mouse interactions
		}

		// Use setTimeout to allow the next element to receive focus first
		setTimeout( () => checkFocusState( event ), 0 );
	};

	// Handle keyboard navigation that might move focus away
	const handleKeyDown = event => {
		// Primary focus navigation keys
		const navigationKeys = [ 'Tab', 'ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight', 'Escape' ];

		if ( navigationKeys.includes( event.key ) ) {
			// Wait a short time after key press to check where focus landed
			setTimeout( () => checkFocusState( event ), checkDelay );
		}
	};

	// Add event listeners
	document.addEventListener( 'mousedown', handleMouseDown );
	document.addEventListener( 'mouseup', handleMouseUp );

	// Attach blur and keydown listeners to each element
	elements.forEach( element => {
		element.addEventListener( 'blur', handleBlur );
		element.addEventListener( 'keydown', handleKeyDown );
	} );

	// Return cleanup function
	return () => {
		document.removeEventListener( 'mousedown', handleMouseDown );
		document.removeEventListener( 'mouseup', handleMouseUp );

		elements.forEach( element => {
			element.removeEventListener( 'blur', handleBlur );
			element.removeEventListener( 'keydown', handleKeyDown );
		} );
	};
}
