import clsx from 'clsx';
import { useEntityProp, store as coreStore } from '@wordpress/core-data';
import {
	useCallback,
	useEffect,
	useLayoutEffect,
	useMemo,
	useRef,
	useState,
} from '@wordpress/element';
import { Placeholder, SandBox, Spinner } from '@wordpress/components';
import { compose, useResizeObserver } from '@wordpress/compose';
import {
	withColors,
	ColorPalette,
	useBlockProps,
	useInnerBlocksProps,
	__experimentalUseGradient,
	store as blockEditorStore,
	useBlockEditingMode,
	privateApis as blockEditorPrivateApis,
} from '@wordpress/block-editor';
import { __ } from '@wordpress/i18n';
import { useSelect, useDispatch, useRegistry } from '@wordpress/data';
import { createBlocksFromInnerBlocksTemplate } from '@wordpress/blocks';
import { isBlobURL } from '@wordpress/blob';
import { store as noticesStore } from '@wordpress/notices';
import {
	attributesFromMedia,
	IMAGE_BACKGROUND_TYPE,
	VIDEO_BACKGROUND_TYPE,
	EMBED_VIDEO_BACKGROUND_TYPE,
	dimRatioToClass,
	isContentPositionCenter,
	getPositionClassName,
	mediaPosition,
} from '../shared';
import CoverInspectorControls from './inspector-controls';
import CoverBlockControls from './block-controls';
import CoverPlaceholder from './cover-placeholder';
import ResizableCoverPopover from './resizable-cover-popover';
import {
	getMediaColor,
	compositeIsDark,
	DEFAULT_BACKGROUND_COLOR,
	DEFAULT_OVERLAY_COLOR,
} from './color-utils';
import { DEFAULT_MEDIA_SIZE_SLUG } from '../constants';
import { getBackgroundEmbedHtml } from '../embed-video-utils';
import { unlock } from '../../lock-unlock';

const { openMediaEditorModalKey } = unlock( blockEditorPrivateApis );

function getInnerBlocksTemplate( attributes ) {
	return [
		[
			'core/paragraph',
			{
				style: {
					typography: {
						textAlign: 'center',
					},
				},
				placeholder: __( 'Write title…' ),
				...attributes,
			},
		],
	];
}

/**
 * Is the URL a temporary blob URL? A blob URL is one that is used temporarily while
 * the media (image or video) is being uploaded and will not have an id allocated yet.
 *
 * @param {number} id  The id of the media.
 * @param {string} url The url of the media.
 *
 * @return {boolean} Is the URL a Blob URL.
 */
const isTemporaryMedia = ( id, url ) => ! id && isBlobURL( url );

function CoverEdit( {
	attributes,
	clientId,
	isSelected,
	overlayColor,
	setAttributes,
	setOverlayColor,
	toggleSelection,
	context: { postId, postType },
} ) {
	const {
		contentPosition,
		id,
		url: originalUrl,
		backgroundType: originalBackgroundType,
		useFeaturedImage,
		dimRatio,
		focalPoint,
		hasParallax,
		isDark,
		isRepeated,
		minHeight,
		minHeightUnit,
		alt,
		allowedBlocks,
		templateLock,
		tagName: TagName = 'div',
		isUserOverlayColor,
		sizeSlug,
		poster,
	} = attributes;

	const [ featuredImage ] = useEntityProp(
		'postType',
		postType,
		'featured_media',
		postId
	);
	const { getSettings } = useSelect( blockEditorStore );
	const openMediaEditorModal = useSelect(
		( select ) =>
			select( blockEditorStore ).getSettings()[ openMediaEditorModalKey ],
		[]
	);

	const { __unstableMarkNextChangeAsNotPersistent, replaceInnerBlocks } =
		useDispatch( blockEditorStore );
	const registry = useRegistry();

	// Ref to access latest values after async operations (e.g. getMediaColor),
	// avoiding stale values that could overwrite concurrent remote changes.
	const propsRef = useRef( { attributes, overlayColor } );
	useLayoutEffect( () => {
		propsRef.current = { attributes, overlayColor };
	} );

	const { media } = useSelect(
		( select ) => {
			return {
				media:
					featuredImage && useFeaturedImage
						? select( coreStore ).getEntityRecord(
								'postType',
								'attachment',
								featuredImage,
								{
									context: 'view',
								}
						  )
						: undefined,
			};
		},
		[ featuredImage, useFeaturedImage ]
	);
	const mediaUrl =
		media?.media_details?.sizes?.[ sizeSlug ]?.source_url ??
		media?.source_url;

	// User can change the featured image outside of the block, but we still
	// need to update the block when that happens. This effect should only
	// run when the featured image changes in that case. All other cases are
	// handled in their respective callbacks.
	useEffect( () => {
		( async () => {
			if ( ! useFeaturedImage ) {
				return;
			}

			const averageBackgroundColor = await getMediaColor( mediaUrl );

			// Read latest values after await to avoid stale closures.
			const { attributes: currentAttrs, overlayColor: currentOverlay } =
				propsRef.current;

			let newOverlayColor = currentOverlay.color;
			if ( ! currentAttrs.isUserOverlayColor ) {
				newOverlayColor = averageBackgroundColor;
				__unstableMarkNextChangeAsNotPersistent();
				setOverlayColor( newOverlayColor );
			}

			const newIsDark = compositeIsDark(
				currentAttrs.dimRatio,
				newOverlayColor,
				averageBackgroundColor
			);
			__unstableMarkNextChangeAsNotPersistent();
			setAttributes( {
				isDark: newIsDark,
				isUserOverlayColor: currentAttrs.isUserOverlayColor || false,
			} );
		} )();
		// Update the block only when the featured image changes.
		// The other dependencies are stable references (dispatch actions / setters).
	}, [
		mediaUrl,
		__unstableMarkNextChangeAsNotPersistent,
		setAttributes,
		setOverlayColor,
		useFeaturedImage,
	] );

	// instead of destructuring the attributes
	// we define the url and background type
	// depending on the value of the useFeaturedImage flag
	// to preview in edit the dynamic featured image
	const url = useFeaturedImage
		? mediaUrl
		: // Ensure the url is not malformed due to sanitization through `wp_kses`.
		  originalUrl?.replaceAll( '&amp;', '&' );
	const backgroundType = useFeaturedImage
		? IMAGE_BACKGROUND_TYPE
		: originalBackgroundType;

	const { createErrorNotice } = useDispatch( noticesStore );
	const { gradientClass, gradientValue } = __experimentalUseGradient();

	// Create the initial inner paragraph when the block gains a background
	// and has no content yet.
	const scaffoldInnerBlocks = () => {
		const {
			getBlocks,
			isBlockSelected,
			getSelectedBlocksInitialCaretPosition,
		} = registry.select( blockEditorStore );
		if ( getBlocks( clientId ).length > 0 ) {
			return;
		}
		// Check for fontSize support before we pass a fontSize attribute to
		// the innerBlocks.
		const [ fontSizes ] = unlock(
			registry.select( blockEditorStore )
		).getBlockSettings( clientId, 'typography.fontSizes' );
		const hasFontSizes = fontSizes?.length > 0;
		replaceInnerBlocks(
			clientId,
			createBlocksFromInnerBlocksTemplate(
				getInnerBlocksTemplate( {
					fontSize: hasFontSizes ? 'large' : undefined,
				} )
			),
			isBlockSelected( clientId ),
			getSelectedBlocksInitialCaretPosition()
		);
	};

	const onSelectMedia = async ( newMedia ) => {
		const mediaAttributes = attributesFromMedia( newMedia );
		const isImage = [ newMedia?.type, newMedia?.media_type ].includes(
			IMAGE_BACKGROUND_TYPE
		);

		const averageBackgroundColor = await getMediaColor(
			isImage ? newMedia?.url : undefined
		);

		// Read latest values to avoid stale closures.
		const { attributes: currentAttrs, overlayColor: currentOverlay } =
			propsRef.current;

		let newOverlayColor = currentOverlay.color;
		if ( ! currentAttrs.isUserOverlayColor ) {
			newOverlayColor = averageBackgroundColor;
			setOverlayColor( newOverlayColor );
			// Fold next attribute change into the same undo level as the setOverlayColor above.
			__unstableMarkNextChangeAsNotPersistent();
		}

		// Only set a new dimRatio if there was no previous media selected
		// to avoid resetting to 50 if it has been explicitly set to 100.
		// See issue #52835 for context.
		const newDimRatio =
			currentAttrs.url === undefined && currentAttrs.dimRatio === 100
				? 50
				: currentAttrs.dimRatio;

		const newIsDark = compositeIsDark(
			newDimRatio,
			newOverlayColor,
			averageBackgroundColor
		);

		if ( backgroundType === IMAGE_BACKGROUND_TYPE && mediaAttributes?.id ) {
			const { imageDefaultSize } = getSettings();

			// Try to use the previous selected image size if it's available
			// otherwise try the default image size or fallback to full size.
			if (
				sizeSlug &&
				( newMedia?.sizes?.[ sizeSlug ] ||
					newMedia?.media_details?.sizes?.[ sizeSlug ] )
			) {
				mediaAttributes.sizeSlug = sizeSlug;
				mediaAttributes.url =
					newMedia?.sizes?.[ sizeSlug ]?.url ||
					newMedia?.media_details?.sizes?.[ sizeSlug ]?.source_url;
			} else if (
				newMedia?.sizes?.[ imageDefaultSize ] ||
				newMedia?.media_details?.sizes?.[ imageDefaultSize ]
			) {
				mediaAttributes.sizeSlug = imageDefaultSize;
				mediaAttributes.url =
					newMedia?.sizes?.[ imageDefaultSize ]?.url ||
					newMedia?.media_details?.sizes?.[ imageDefaultSize ]
						?.source_url;
			} else {
				mediaAttributes.sizeSlug = DEFAULT_MEDIA_SIZE_SLUG;
			}
		}

		registry.batch( () => {
			setAttributes( {
				...mediaAttributes,
				focalPoint: undefined,
				useFeaturedImage: undefined,
				dimRatio: newDimRatio,
				isDark: newIsDark,
				isUserOverlayColor: currentAttrs.isUserOverlayColor || false,
			} );

			scaffoldInnerBlocks();
		} );
	};

	const onClearMedia = () => {
		let newOverlayColor = overlayColor.color;

		// Skip for embeds, which never auto-assign an overlay color; otherwise
		// the non-persistent flag lands on the media reset itself (unsaveable).
		if ( ! isUserOverlayColor && overlayColor.color ) {
			newOverlayColor = DEFAULT_OVERLAY_COLOR;
			setOverlayColor( undefined );
			// Fold next attribute change into the same undo level as the setOverlayColor above.
			__unstableMarkNextChangeAsNotPersistent();
		}

		const newIsDark = compositeIsDark(
			dimRatio,
			newOverlayColor,
			DEFAULT_BACKGROUND_COLOR
		);

		setAttributes( {
			url: undefined,
			id: undefined,
			backgroundType: undefined,
			focalPoint: undefined,
			hasParallax: undefined,
			isRepeated: undefined,
			useFeaturedImage: undefined,
			isDark: newIsDark,
		} );
	};

	const onSetOverlayColor = async ( newOverlayColor ) => {
		const averageBackgroundColor = await getMediaColor( url );

		// Read latest dimRatio after await to avoid stale closure.
		const { attributes: currentAttrs } = propsRef.current;

		const newIsDark = compositeIsDark(
			currentAttrs.dimRatio,
			newOverlayColor,
			averageBackgroundColor
		);

		setOverlayColor( newOverlayColor );
		// Fold next attribute change into the same undo level as the setOverlayColor above.
		__unstableMarkNextChangeAsNotPersistent();

		registry.batch( () => {
			setAttributes( {
				isUserOverlayColor: true,
				isDark: newIsDark,
			} );

			// Skip when the color is cleared: that returns the block to its
			// placeholder, which only renders while there is no content.
			if ( newOverlayColor ) {
				scaffoldInnerBlocks();
			}
		} );
	};

	const onUpdateDimRatio = async ( newDimRatio ) => {
		const averageBackgroundColor = await getMediaColor( url );

		// Read latest overlayColor after await to avoid stale closure.
		const { overlayColor: currentOverlay } = propsRef.current;

		const newIsDark = compositeIsDark(
			newDimRatio,
			currentOverlay.color,
			averageBackgroundColor
		);

		setAttributes( {
			dimRatio: newDimRatio,
			isDark: newIsDark,
		} );
	};

	const onUploadError = ( message ) => {
		createErrorNotice( message, { type: 'snackbar' } );
	};

	const onSelectEmbedUrl = ( embedUrl ) => {
		// Only set a new dimRatio if there was no previous media selected
		// to avoid resetting to 50 if it has been explicitly set to 100.
		const newDimRatio =
			originalUrl === undefined && dimRatio === 100 ? 50 : dimRatio;

		// Set initial attributes with URL
		setAttributes( {
			url: embedUrl,
			backgroundType: EMBED_VIDEO_BACKGROUND_TYPE,
			dimRatio: newDimRatio,
			id: undefined,
			focalPoint: undefined,
			hasParallax: undefined,
			isRepeated: undefined,
			useFeaturedImage: undefined,
		} );
	};

	// Fetch embed preview for embed videos
	const { embedPreview, isFetchingEmbed } = useSelect(
		( select ) => {
			if ( backgroundType !== EMBED_VIDEO_BACKGROUND_TYPE || ! url ) {
				return {
					embedPreview: undefined,
					isFetchingEmbed: false,
				};
			}

			const { getEmbedPreview, isRequestingEmbedPreview } =
				select( coreStore );

			return {
				embedPreview: getEmbedPreview( url ),
				isFetchingEmbed: isRequestingEmbedPreview( url ),
			};
		},
		[ url, backgroundType ]
	);

	// Compute embed HTML for editor display via SandBox
	const embedHtml = useMemo( () => {
		if (
			backgroundType !== EMBED_VIDEO_BACKGROUND_TYPE ||
			! embedPreview?.html
		) {
			return null;
		}
		return getBackgroundEmbedHtml( embedPreview.html );
	}, [ embedPreview, backgroundType ] );

	// Set while the media editor has pointed the cover at a freshly
	// generated file the browser hasn't finished loading; cleared by the
	// background <img> load/error handlers (or immediately after
	// setAttributes for CSS backgrounds, which never fire load events).
	const [ isSwappingMedia, setIsSwappingMedia ] = useState( false );

	const isUploadingMedia = isTemporaryMedia( id, url );

	const isImageBackground = IMAGE_BACKGROUND_TYPE === backgroundType;
	const isVideoBackground = VIDEO_BACKGROUND_TYPE === backgroundType;
	const isEmbedVideoBackground =
		EMBED_VIDEO_BACKGROUND_TYPE === backgroundType;

	const blockEditingMode = useBlockEditingMode();
	const hasNonContentControls = blockEditingMode === 'default';

	const [ resizeListener, { height, width } ] = useResizeObserver();
	const resizableBoxDimensions = useMemo( () => {
		return {
			height: minHeightUnit === 'px' && minHeight ? minHeight : 'auto',
			width: 'auto',
		};
	}, [ minHeight, minHeightUnit ] );

	const minHeightWithUnit =
		minHeight && minHeightUnit
			? `${ minHeight }${ minHeightUnit }`
			: minHeight;

	const isImgElement = ! ( hasParallax || isRepeated );

	const style = {
		minHeight: minHeightWithUnit || undefined,
	};

	const backgroundImage = url ? `url(${ url })` : undefined;

	const backgroundPosition = mediaPosition( focalPoint );

	const bgStyle = { backgroundColor: overlayColor.color };
	const mediaStyle = {
		objectPosition:
			focalPoint && isImgElement
				? mediaPosition( focalPoint )
				: undefined,
	};

	const hasBackground = !! ( url || overlayColor.color || gradientValue );

	const hasInnerBlocks = useSelect(
		( select ) =>
			select( blockEditorStore ).getBlock( clientId ).innerBlocks.length >
			0,
		[ clientId ]
	);

	const ref = useRef();
	const blockProps = useBlockProps( { ref } );

	const innerBlocksProps = useInnerBlocksProps(
		{
			className: 'wp-block-cover__inner-container',
		},
		{
			allowedBlocks,
			templateLock,
			dropZoneElement: ref.current,
		}
	);

	const mediaElement = useRef();
	const editMediaButtonRef = useRef();
	const currentSettings = {
		isVideoBackground,
		isImageBackground,
		mediaElement,
		hasInnerBlocks,
		url,
		isImgElement,
		overlayColor,
	};

	const openCoverMediaEditorModal = useCallback( () => {
		if ( ! id || ! openMediaEditorModal ) {
			return;
		}

		openMediaEditorModal( {
			id,
			onClose: () => {
				editMediaButtonRef.current?.focus();
			},
			onUpdate: async ( { id: newId, url: newUrl } ) => {
				if ( typeof newId !== 'number' ) {
					return;
				}

				if ( newId !== id && newUrl ) {
					setIsSwappingMedia( true );
				}

				const nextAttributes = {
					id: newId,
					backgroundType: IMAGE_BACKGROUND_TYPE,
					...( newUrl ? { url: newUrl } : {} ),
					...( newId !== id
						? { sizeSlug: DEFAULT_MEDIA_SIZE_SLUG }
						: {} ),
				};

				if ( newUrl ) {
					const averageBackgroundColor =
						await getMediaColor( newUrl );

					// Read latest values after await to avoid stale closures.
					const {
						attributes: currentAttrs,
						overlayColor: currentOverlay,
					} = propsRef.current;

					let newOverlayColor = currentOverlay.color;
					if ( ! currentAttrs.isUserOverlayColor ) {
						newOverlayColor = averageBackgroundColor;
						setOverlayColor( newOverlayColor );
						// Fold next attribute change into the same undo level as the setOverlayColor above.
						__unstableMarkNextChangeAsNotPersistent();
					}

					nextAttributes.isDark = compositeIsDark(
						currentAttrs.dimRatio,
						newOverlayColor,
						averageBackgroundColor
					);
					nextAttributes.isUserOverlayColor =
						currentAttrs.isUserOverlayColor || false;
				}

				setAttributes( nextAttributes );

				// A CSS background (parallax/repeated) renders as a div and
				// never fires a load event; getMediaColor already fetched
				// the file, so the swap is done once attributes are set.
				const {
					hasParallax: currentHasParallax,
					isRepeated: currentIsRepeated,
				} = propsRef.current.attributes;
				if ( currentHasParallax || currentIsRepeated ) {
					setIsSwappingMedia( false );
				}
			},
		} );
	}, [
		id,
		openMediaEditorModal,
		setAttributes,
		setOverlayColor,
		__unstableMarkNextChangeAsNotPersistent,
	] );

	const showEditMediaButton =
		hasNonContentControls &&
		! useFeaturedImage &&
		isImageBackground &&
		!! id &&
		!! url &&
		! isUploadingMedia &&
		!! openMediaEditorModal;

	const toggleUseFeaturedImage = async () => {
		const newUseFeaturedImage = ! useFeaturedImage;

		const averageBackgroundColor = newUseFeaturedImage
			? await getMediaColor( mediaUrl )
			: DEFAULT_BACKGROUND_COLOR;

		// Read latest values after await to avoid stale closures.
		const { attributes: currentAttrs, overlayColor: currentOverlay } =
			propsRef.current;

		const newOverlayColor = ! currentAttrs.isUserOverlayColor
			? averageBackgroundColor
			: currentOverlay.color;

		if ( ! currentAttrs.isUserOverlayColor ) {
			if ( newUseFeaturedImage ) {
				setOverlayColor( newOverlayColor );
			} else {
				setOverlayColor( undefined );
			}
			// Fold next attribute change into the same undo level as the setOverlayColor above.
			__unstableMarkNextChangeAsNotPersistent();
		}

		const newDimRatio =
			currentAttrs.dimRatio === 100 ? 50 : currentAttrs.dimRatio;
		const newIsDark = compositeIsDark(
			newDimRatio,
			newOverlayColor,
			averageBackgroundColor
		);

		registry.batch( () => {
			setAttributes( {
				id: undefined,
				url: undefined,
				useFeaturedImage: newUseFeaturedImage,
				dimRatio: newDimRatio,
				backgroundType: useFeaturedImage
					? IMAGE_BACKGROUND_TYPE
					: undefined,
				isDark: newIsDark,
			} );

			// Skip when the featured image is disabled: that can return the
			// block to its placeholder, which only renders while there is no
			// content.
			if ( newUseFeaturedImage ) {
				scaffoldInnerBlocks();
			}
		} );
	};

	const blockControls = (
		<CoverBlockControls
			attributes={ attributes }
			setAttributes={ setAttributes }
			onSelectMedia={ onSelectMedia }
			onSelectEmbedUrl={ onSelectEmbedUrl }
			currentSettings={ currentSettings }
			toggleUseFeaturedImage={ toggleUseFeaturedImage }
			onClearMedia={ onClearMedia }
			blockEditingMode={ blockEditingMode }
			onEditMedia={ openCoverMediaEditorModal }
			editMediaButtonRef={ editMediaButtonRef }
			showEditMediaButton={ showEditMediaButton }
			isEditMediaDisabled={ isSwappingMedia }
		/>
	);

	const inspectorControls = (
		<CoverInspectorControls
			attributes={ attributes }
			setAttributes={ setAttributes }
			clientId={ clientId }
			setOverlayColor={ onSetOverlayColor }
			coverRef={ ref }
			currentSettings={ currentSettings }
			toggleUseFeaturedImage={ toggleUseFeaturedImage }
			updateDimRatio={ onUpdateDimRatio }
			onClearMedia={ onClearMedia }
			featuredImage={ media }
		/>
	);

	const resizableCoverProps = {
		className: 'block-library-cover__resize-container',
		clientId,
		height,
		minHeight: minHeightWithUnit,
		onResizeStart: () => {
			setAttributes( { minHeightUnit: 'px' } );
			toggleSelection( false );
		},
		onResize: ( value ) => {
			setAttributes( { minHeight: value } );
		},
		onResizeStop: ( newMinHeight ) => {
			toggleSelection( true );
			setAttributes( { minHeight: newMinHeight } );
		},
		// Hide the resize handle if an aspect ratio is set, as the aspect ratio takes precedence.
		showHandle: ! attributes.style?.dimensions?.aspectRatio,
		size: resizableBoxDimensions,
		width,
	};

	if ( ! useFeaturedImage && ! hasInnerBlocks && ! hasBackground ) {
		return (
			<>
				{ blockControls }
				{ inspectorControls }
				{ hasNonContentControls && isSelected && (
					<ResizableCoverPopover { ...resizableCoverProps } />
				) }
				<TagName
					{ ...blockProps }
					className={ clsx( 'is-placeholder', blockProps.className ) }
					style={ {
						...blockProps.style,
						minHeight: minHeightWithUnit || undefined,
					} }
				>
					{ resizeListener }
					<CoverPlaceholder
						onSelectMedia={ onSelectMedia }
						onError={ onUploadError }
						toggleUseFeaturedImage={ toggleUseFeaturedImage }
					>
						<div className="wp-block-cover__placeholder-background-options">
							<ColorPalette
								disableCustomColors
								value={ overlayColor.color }
								onChange={ onSetOverlayColor }
								clearable={ false }
								presentation="toggle-buttons"
								aria-label={ __( 'Overlay color' ) }
							/>
						</div>
					</CoverPlaceholder>
				</TagName>
			</>
		);
	}

	const classes = clsx(
		{
			'is-dark-theme': isDark,
			'is-light': ! isDark,
			'is-transient': isUploadingMedia || isSwappingMedia,
			'has-parallax': hasParallax,
			'is-repeated': isRepeated,
			'has-custom-content-position':
				! isContentPositionCenter( contentPosition ),
		},
		getPositionClassName( contentPosition )
	);

	const showOverlay =
		url || ! useFeaturedImage || ( useFeaturedImage && ! url );

	return (
		<>
			{ blockControls }
			{ inspectorControls }
			<TagName
				{ ...blockProps }
				className={ clsx( classes, blockProps.className ) }
				style={ { ...style, ...blockProps.style } }
				data-url={ url }
			>
				{ resizeListener }

				{ ! url && useFeaturedImage && (
					<Placeholder
						className="wp-block-cover__image--placeholder-image"
						withIllustration
					/>
				) }

				{ url &&
					isImageBackground &&
					( isImgElement ? (
						<img
							ref={ mediaElement }
							className="wp-block-cover__image-background"
							alt={ alt }
							src={ url }
							style={ mediaStyle }
							onLoad={ () => setIsSwappingMedia( false ) }
							onError={ () => setIsSwappingMedia( false ) }
						/>
					) : (
						<div
							ref={ mediaElement }
							role={ alt ? 'img' : undefined }
							aria-label={ alt ? alt : undefined }
							className={ clsx(
								classes,
								'wp-block-cover__image-background'
							) }
							style={ { backgroundImage, backgroundPosition } }
						/>
					) ) }
				{ url && isVideoBackground && (
					<video
						ref={ mediaElement }
						className="wp-block-cover__video-background"
						autoPlay
						muted
						loop
						src={ url }
						poster={ poster }
						style={ mediaStyle }
					/>
				) }
				{ isEmbedVideoBackground && embedHtml && (
					<div
						ref={ mediaElement }
						className="wp-block-cover__video-background wp-block-cover__embed-background"
						style={ mediaStyle }
					>
						<SandBox
							allowSameOrigin
							html={ embedHtml }
							title="Background video"
							styles={ [
								'iframe{position:fixed;top:0;left:0;width:100%;height:100%;}',
							] }
						/>
					</div>
				) }
				{ isEmbedVideoBackground && ! embedHtml && isFetchingEmbed && (
					<Spinner />
				) }

				{ showOverlay && (
					<span
						aria-hidden="true"
						className={ clsx(
							'wp-block-cover__background',
							dimRatioToClass( dimRatio ),
							{
								[ overlayColor.class ]: overlayColor.class,
								'has-background-dim': dimRatio !== undefined,
								// For backwards compatibility. Former versions of the Cover Block applied
								// `.wp-block-cover__gradient-background` in the presence of
								// media, a gradient and a dim.
								'wp-block-cover__gradient-background':
									url && gradientValue && dimRatio !== 0,
								'has-background-gradient': gradientValue,
								[ gradientClass ]: gradientClass,
							}
						) }
						style={ { backgroundImage: gradientValue, ...bgStyle } }
					/>
				) }

				{ ( isUploadingMedia || isSwappingMedia ) && <Spinner /> }

				<CoverPlaceholder
					disableMediaButtons
					onSelectMedia={ onSelectMedia }
					onError={ onUploadError }
					toggleUseFeaturedImage={ toggleUseFeaturedImage }
				/>
				<div { ...innerBlocksProps } />
			</TagName>
			{ hasNonContentControls && isSelected && (
				<ResizableCoverPopover { ...resizableCoverProps } />
			) }
		</>
	);
}

export default compose( [
	withColors( { overlayColor: 'background-color' } ),
] )( CoverEdit );
