/**
 * External dependencies.
 */
import { useSelect } from '@wordpress/data';
import { useEffect, useMemo, useRef, useState } from '@wordpress/element';

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

/**
 * Higher-order component that enhances blocks with dynamic image content capability.
 *
 * This component wraps the original BlockEdit component to add support for dynamic images
 * in supported blocks. It handles fetching dynamic content, managing loading states,
 * and properly updating block attributes with the retrieved dynamic image data.
 *
 * @since x.x.x
 *
 * @param {Object}   params             - Component parameters.
 * @param {Object}   params.props       - Original props passed to the BlockEdit component.
 * @param {string}   params.blockName   - Name of the block being edited (e.g., 'core/image').
 * @param {Object}   params.attributes  - Current attributes of the block.
 * @param {string}   params.contentAttr - Attribute name where dynamic content should be stored.
 * @param {Function} params.BlockEdit   - Original BlockEdit component to be enhanced.
 * @return {Function} Enhanced BlockEdit component with dynamic image support.
 */
const DynamicImageEnhancer = ( { props, blockName, attributes, contentAttr, BlockEdit } ) => {
	// State to store the fetched dynamic image content.
	const [ processedContent, setProcessedContent ] = useState( null );

	// State to track whether the block editor has loaded.
	const [ isEditorLoaded, setIsEditorLoaded ] = useState( false );

	const { spectraProDynamicImage } = attributes;

	const { context } = props;

	// Ref to track the previous dynamic image settings for change detection.
	const previousDynamicImage = useRef( spectraProDynamicImage );

	// Monitor the number of blocks in the editor to detect when it has loaded.
	const blockCount = useSelect( ( select ) => select( 'core/block-editor' ).getBlockCount(), [] );

	// Get current device type from editor using the same method as responsive controls.
	const currentDevice = useSelect( ( select ) => {
		const deviceType = select( 'core/editor' )?.getDeviceType?.() || 'Desktop';
		// Map device types to breakpoints same as responsive controls.
		const BREAKPOINT_TYPE_MAP = {
			Mobile: 'sm',
			Tablet: 'md',
			Desktop: 'lg',
		};
		return BREAKPOINT_TYPE_MAP[ deviceType ] || 'lg';
	}, [] );

	// Update editor loaded state when blocks are detected.
	useEffect( () => {
		if ( blockCount > 0 && ! isEditorLoaded ) {
			setIsEditorLoaded( true );
		}
	}, [ blockCount ] );

	// Fetch dynamic image content when the editor loads or dynamic image settings change.
	useEffect( () => {
		if ( ! isEditorLoaded ) return;

		// Determine if content needs to be processed (initial load or settings change).
		const shouldProcess =
			// First load
			processedContent === null ||
			// Dynamic image source changed
			spectraProDynamicImage !== previousDynamicImage.current;

		if ( ! shouldProcess ) return;

		// Update the ref with the current dynamic image settings.
		previousDynamicImage.current = spectraProDynamicImage;

		// Asynchronously fetch and set the dynamic image content.
		const parseContent = async () => {
			// If context has postId and postType, update the dynamic image source.
			const updatedDynamicImage =
				'current_post' === spectraProDynamicImage?.source?.type && context?.postType && context?.postId
					? {
							...spectraProDynamicImage,
							source: {
								...spectraProDynamicImage.source,
								postId: context.postId,
								postType: context.postType,
							},
					  }
					: spectraProDynamicImage;

			const newContent = await getDynamicContent( updatedDynamicImage, 'image' );

			// Skip updating if no valid URL is returned.
			if ( ! newContent?.url ) {
				setProcessedContent( undefined );
			}

			setProcessedContent( newContent );
		};

		parseContent();
	}, [ isEditorLoaded, spectraProDynamicImage ] );

	// Memoize enhanced props to optimize rendering and handle block-specific attribute updates.
	const enhancedProps = useMemo( () => {
		// Return original props if no processed content or editor is not loaded.
		if ( ! processedContent || ! isEditorLoaded ) {
			return props;
		}

		// Special handling for core/image block attributes.
		if ( blockName === 'core/image' ) {
			return {
				...props,
				attributes: {
					...attributes,
					url: processedContent.url || attributes.url,
					id: processedContent.id || attributes.id,
				},
			};
		}

		// Default handling for other blocks (e.g., spectra/slider, spectra/container).
		// Handle responsive controls compatibility to ensure proper device-specific background rendering.
		if ( contentAttr === 'background' && attributes.responsiveControls && currentDevice ) {
			const deviceBg = attributes.responsiveControls[ currentDevice ]?.background;

			// Check for inheritance if current device has no background set.
			// Fallback order: lg -> md -> sm (desktop -> tablet -> mobile).
			let inheritedBg = deviceBg;
			if ( ! deviceBg?.type ) {
				let fallbackOrder = [];
				if ( currentDevice === 'sm' ) {
					fallbackOrder = [ 'md', 'lg' ];
				} else if ( currentDevice === 'md' ) {
					fallbackOrder = [ 'lg' ];
				}

				for ( const fallbackDevice of fallbackOrder ) {
					const fallbackBg = attributes.responsiveControls[ fallbackDevice ]?.background;
					if ( fallbackBg?.type ) {
						inheritedBg = fallbackBg;
						break;
					}
				}
			}

			// If current or inherited background is video, apply it without dynamic image interference.
			if ( inheritedBg?.type === 'video' ) {
				// Ensure the background structure is complete for the background component.
				const completeVideoBg = {
					...( attributes[ contentAttr ] || {} ), // Keep existing root background structure.
					...inheritedBg, // Override with device-specific or inherited video background.
					type: 'video', // Ensure type is explicitly set.
				};

				return {
					...props,
					attributes: {
						...attributes,
						[ contentAttr ]: completeVideoBg,
						// Keep responsiveControls intact so settings show correct selection.
					},
				};
			}

			// If current device has image background or no background type, apply dynamic image.
			if ( deviceBg?.type === 'image' || ! deviceBg?.type ) {
				const updatedResponsiveControls = { ...attributes.responsiveControls };

				// Update current device background with dynamic image.
				if ( ! updatedResponsiveControls[ currentDevice ] ) {
					updatedResponsiveControls[ currentDevice ] = {};
				}

				const updatedDeviceBg = {
					...deviceBg,
					type: 'image',
					media: processedContent,
				};

				updatedResponsiveControls[ currentDevice ] = {
					...updatedResponsiveControls[ currentDevice ],
					background: updatedDeviceBg,
				};

				return {
					...props,
					attributes: {
						...attributes,
						[ contentAttr ]: updatedDeviceBg, // Apply for rendering.
						responsiveControls: updatedResponsiveControls, // Keep settings in sync.
					},
				};
			}
		}

		// Fallback for blocks without responsive controls.
		return {
			...props,
			attributes: {
				...attributes,
				[ contentAttr ]: {
					...attributes[ contentAttr ],
					media: processedContent,
				},
			},
		};
	}, [ props, processedContent, blockName, attributes, contentAttr, isEditorLoaded, currentDevice ] );

	return <BlockEdit { ...enhancedProps } />;
};

export default DynamicImageEnhancer;
