/**
 * External dependencies.
 */
import {
	__experimentalToolsPanel as ToolsPanel,
	__experimentalToolsPanelItem as ToolsPanelItem,
	__experimentalVStack as VStack,
	ToggleControl,
} from '@wordpress/components';
import { useDispatch } from '@wordpress/data';
import { useEffect } from '@wordpress/element';
import { __ } from '@wordpress/i18n';

/**
 * Internal dependencies.
 */
import DynamicImage from '@spectra-pro-extensions/dynamic-content/components/DynamicImage';
import { DynamicContentProvider } from '@spectra-pro-extensions/dynamic-content/context/DynamicContentContext';
import { getDynamicContent } from '@spectra-pro-extensions/dynamic-content/utils/helpers';

/**
 * Extends the WordPress Media Picker with dynamic image controls.
 *
 * Adds a tools panel to toggle dynamic image support and configure dynamic content
 * settings for supported blocks. Manages attribute updates and preview visibility
 * based on the dynamic image state.
 *
 * @since x.x.x
 *
 * @param {Object}   defaultValue         - The default value returned by the Media Picker.
 * @param {Object}   props                - The component props.
 * @param {string}   props.type           - The media type (e.g., 'image').
 * @param {string}   props.attributeLabel - The attribute key for the media content.
 * @param {Object}   props.attributeValue - The current value of the media attribute.
 * @param {Object}   props.attributes     - The block's attributes.
 * @param {Function} props.setAttributes  - Function to update block attributes.
 * @param {Function} props.setShowPreview - Function to toggle media preview visibility.
 * @return {Element|null} The tools panel for dynamic image controls or the default value.
 */
const MediaPickerWithDynamicImage = ( defaultValue, props ) => {
	// Destructure only what we need from props
	const { type, attributeLabel, attributeValue, attributes, setAttributes, setShowPreview, clientId, context = {} } = props;

	const { spectraProEnableDynamicImage, spectraProDynamicImage } = attributes;

	const { __unstableMarkNextChangeAsNotPersistent, updateBlockAttributes } = useDispatch( 'core/block-editor' );

	// Effect to handle preview visibility based on dynamic image state.
	useEffect( () => {
		// Only hide preview for image backgrounds when dynamic image is enabled.
		// Keep preview visible for video backgrounds to maintain proper UI display.
		if ( type === 'image' ) {
			setShowPreview( ! spectraProEnableDynamicImage );
		} else {
			setShowPreview( true ); // Always show preview for non-image types (like video).
		}
	}, [ spectraProEnableDynamicImage, setShowPreview, type ] );

	// Only override for images when dynamic image is enabled.
	if ( type !== 'image' ) {
		return defaultValue;
	}

	/**
	 * Handles toggling the dynamic image feature.
	 *
	 * Updates block attributes to enable or disable dynamic images, fetching dynamic
	 * content when enabling or restoring static image when disabling.
	 *
	 * @param {boolean} newValue - The new toggle state.
	 */
	const handleToggle = async ( newValue ) => {
		setAttributes( {
			spectraProEnableDynamicImage: newValue,
		} );

		if ( newValue ) {
			if ( spectraProDynamicImage ) {
				// Re-enable dynamic image and fetch content if settings exist.
				const dynamicImage = await getDynamicContent( spectraProDynamicImage, 'image' );

				if ( dynamicImage?.url ) {
					/**
					 * Use requestAnimationFrame to defer the attribute update until the next browser paint cycle.
					 * This ensures the UI is updated smoothly and prevents potential rendering conflicts.
					 *
					 * @see https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame
					 */
					requestAnimationFrame( () => {
						/**
						 * Mark the next change as not persistent to prevent it from being saved to the block's undo history.
						 * This is important for dynamic content as these changes are visual updates in the editor
						 * rather than actual content edits that should be tracked in the undo stack.
						 */
						__unstableMarkNextChangeAsNotPersistent();
						updateBlockAttributes( clientId, {
							[ attributeLabel ]: {
								...attributeValue,
								media: dynamicImage,
							},
						} );
					} );
				}
			}
		}
	};

	/**
	 * Resets all dynamic image settings.
	 *
	 * Disables dynamic image support and clears related attributes.
	 */
	const resetAll = () => {
		setAttributes( {
			spectraProEnableDynamicImage: false,
		} );
	};

	return (
		/* ToolsPanel for dynamic image settings.
			- Provides controls to toggle dynamic image support and configure dynamic content.
			- Default values:
			- Dynamic Image spectraProEnableDynamicImage: false
			- Includes a "Reset All" option to restore default values.
		*/
		<ToolsPanel label={ __( 'Dynamic Image', 'spectra-pro' ) } resetAll={ resetAll }>
			{ /* This ToolsPanelItem will reset when the following condition is met:
				- The "spectraProEnableDynamicImage" attribute is set. Default: false.
			*/ }
			<ToolsPanelItem
				label={ __( 'Dynamic Image', 'spectra-pro' ) }
				isShownByDefault
				hasValue={ () => !! spectraProEnableDynamicImage }
				onDeselect={ () => setAttributes( { spectraProEnableDynamicImage: false } ) }
			>
				<VStack spacing={ 4 }>
					<ToggleControl
						label={ __( 'Dynamic Image', 'spectra-pro' ) }
						checked={ spectraProEnableDynamicImage }
						onChange={ handleToggle }
					/>

					{ spectraProEnableDynamicImage && (
						<DynamicContentProvider>
							<DynamicImage
								attributes={ attributes }
								setAttributes={ setAttributes }
								attributeLabel={ attributeLabel }
								attributeValue={ attributeValue }
								context={ context }
							/>
						</DynamicContentProvider>
					) }
				</VStack>
			</ToolsPanelItem>
		</ToolsPanel>
	);
};

export default MediaPickerWithDynamicImage;
