/**
 * External dependencies.
 */
import { MediaUpload, MediaUploadCheck } from '@wordpress/block-editor';
import { Button, BaseControl, __experimentalHStack as HStack } from '@wordpress/components';
import { useEffect, useState, useMemo, useContext, createContext, useRef } from '@wordpress/element';
import { __ } from '@wordpress/i18n';

// Create a context to pass the popover close function down to child components
export const PopoverCloseContext = createContext( null );

/**
 * Renders a control for selecting or removing a fallback image.
 *
 * Displays a media upload interface to select a fallback image and a preview of the
 * selected image with options to replace or remove it. Updates the dynamic content
 * settings with the fallback image configuration.
 *
 * @since x.x.x
 *
 * @param {Object}   props                       - The component props.
 * @param {Object}   props.settings              - The dynamic content settings from the context.
 * @param {Function} props.updateAdvancedSetting - Function to update advanced settings in the context.
 * @param {Function} [props.directSave]          - Optional function to save directly to block attributes.
 * @return {Element|null} The fallback image control or null if no media is set initially.
 */
const FallbackImage = ( { settings, updateAdvancedSetting, directSave } ) => {
	const [ media, setMedia ] = useState( null );
	
	// Get the popover close function from context (if available)
	const closePopover = useContext( PopoverCloseContext );

	// Ref to store the reopen function when media upload starts
	const reopenPopoverRef = useRef( null );

	// Synchronize media state with settings when fallback image changes.
	useEffect( () => {
		if ( settings?.advanced?.fallback && ! media ) {
			setMedia( settings.advanced.fallback );
		}
	}, [ settings?.advanced?.fallback, media ] );

	/**
	 * Handles selection of a new fallback image.
	 *
	 * Updates the local media state and dynamic content settings with the new image.
	 *
	 * @param {Object} newMedia - The selected media object.
	 */
	const handleSelect = ( newMedia ) => {
		const updated = {
			id: newMedia.id,
			url: newMedia.url,
			type: newMedia.type,
		};
		
		// Update both local state and settings
		setMedia( updated );
		updateAdvancedSetting( 'fallback', updated );
		
		// If directSave function is provided, also save directly to block attributes
		// This handles the case when popover is closed during media selection
		if ( directSave ) {
			directSave( updated );
		}

		// Reopen popover after media upload if we have a reopen function
		if ( reopenPopoverRef.current ) {
			// Small delay to ensure media modal has fully closed
			setTimeout( () => {
				reopenPopoverRef.current();
				reopenPopoverRef.current = null; // Clear the reference
			}, 300 );
		}
	};

	/**
	 * Handles removal of the fallback image.
	 *
	 * Clears the local media state and removes the fallback setting.
	 */
	const handleRemove = () => {
		setMedia( null );
		updateAdvancedSetting( 'fallback', undefined );
	};

	/**
	 * Enhanced open function that closes popover before opening media modal
	 * This prevents conflicts between URLPopover and MediaUpload modal
	 *
	 * @param {Function} originalOpen - The original media modal open function.
	 */
	const handleOpenMedia = ( originalOpen ) => {
		// Store the reopen function if we have a way to reopen the popover
		if ( closePopover && closePopover.reopen ) {
			reopenPopoverRef.current = closePopover.reopen;
		}

		// Close the popover first to prevent conflicts
		if ( closePopover ) {
			closePopover();
		}
		
		// Small delay to ensure popover is closed before opening media modal
		setTimeout( () => {
			originalOpen();
		}, 100 );
	};

	// Memoize the media upload render function to prevent unnecessary re-renders.
	const renderMediaUpload = useMemo( () => {
		return ( { open } ) => (
			<>
				{ ! media || ! media.url ? (
					<BaseControl
						id="spectra-pro-dynamic-content-fallback-image"
						label={ __( 'Fallback Image', 'spectra-pro' ) }
					>
						<HStack justify="start">
							<Button
								__next40pxDefaultSize
								onClick={ () => handleOpenMedia( open ) }
								variant="secondary"
								aria-label={ __( 'Select fallback image', 'spectra-pro' ) }
							>
								{ __( 'Set Fallback Image', 'spectra-pro' ) }
							</Button>
						</HStack>
					</BaseControl>
				) : (
					<>
						<BaseControl
							id="spectra-pro-dynamic-content-fallback-image"
							label={ __( 'Fallback Image', 'spectra-pro' ) }
						>
							<img
								id="spectra-pro-dynamic-content-advanced-settings-fallback-image"
								src={ media.url }
								alt={ media.alt || __( 'Fallback image', 'spectra-pro' ) }
								style={ {
									maxWidth: '100%',
									height: 'auto',
									borderRadius: '4px',
									border: '1px solid #ddd'
								} }
							/>
						</BaseControl>
						<HStack spacing={ 4 }>
							<Button
								variant="secondary"
								onClick={ () => handleOpenMedia( open ) }
								aria-label={ __( 'Replace fallback image', 'spectra-pro' ) }
							>
								{ __( 'Replace', 'spectra-pro' ) }
							</Button>
							<Button
								variant="link"
								onClick={ handleRemove }
								isDestructive
								aria-label={ __( 'Remove fallback image', 'spectra-pro' ) }
							>
								{ __( 'Remove', 'spectra-pro' ) }
							</Button>
						</HStack>

						<span className="components-base-control__help">
							{ __(
								'This image will be used as a fallback if no dynamic image is found.',
								'spectra-pro'
							) }
						</span>
					</>
				) }
			</>
		);
	}, [ media, handleRemove, closePopover ] );

	// Render the fallback image control.
	return (
		<MediaUploadCheck>
			<MediaUpload
				onSelect={ handleSelect }
				allowedTypes={ [ 'image' ] }
				value={ media?.id }
				render={ renderMediaUpload }
			/>
		</MediaUploadCheck>
	);
};

export default FallbackImage;
