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

import { prefersReducedMotion } from './prefers-reduced-motion';
import { timeout } from './timeout';

/**
 * Animates an element by adding and removing animation state classes.
 * Uses CSS transitions with timeouts instead of animation events.
 * Handles changing the `display` property for elements initially hidden with `display: none`.
 *
 * @param element - The DOM element to animate
 * @param isVisible - Whether the element should be visible (true = enter, false = exit)
 * @param options - Animation options
 * @param [options.onEntering] - Callback when element starts entering
 * @param [options.onEntered] - Callback when element completes entering
 * @param [options.onExiting] - Callback when element starts exiting
 * @param [options.onExited] - Callback when element completes exiting
 * @param [options.duration] - Duration in milliseconds (defaults to 300)
 * @param [options.displayValue='flex'] - The display value to set when showing the element
 * @returns Promise that resolves when animation completes
 */
export async function animateElement( element, isVisible, options = {} ) {
	// Remove all animation state classes first
	element.classList.remove(
		'is-anim-entering',
		'is-anim-entered',
		'is-anim-exiting',
		'is-anim-exited'
	);

	// Default transition duration (in ms)
	let duration = options.duration || 300;
	const displayValue = options.displayValue || 'flex'; // Default display value

	// Check for reduced motion preference and shorten duration if needed
	if ( prefersReducedMotion() ) {
		duration = Math.min( duration, 10 ); // Significantly reduce animation time to 50ms max
	}

	if ( isVisible ) {
		element.style.display = displayValue;

		// Force browser reflow/repaint to apply the display change
		// before adding the animation class. Accessing offsetHeight is a common trick.
		// eslint-disable-next-line no-void
		void element.offsetHeight;

		// Start entering animation
		element.classList.add( 'is-anim-entering' );
		options.onEntering?.( element );

		// Wait for transition to complete
		await timeout( duration );

		// Transition completed
		element.classList.remove( 'is-anim-entering' );
		element.classList.add( 'is-anim-entered' );
		options.onEntered?.( element );
	} else {
		// Start exiting animation
		element.classList.add( 'is-anim-exiting' );
		options.onExiting?.( element );

		// Wait for transition to complete
		await timeout( duration );

		// Transition completed
		element.classList.remove( 'is-anim-exiting' );
		element.classList.add( 'is-anim-exited' );

		// Hide the element after the animation completes
		element.style.display = 'none';
		options.onExited?.( element );
	}
}
