import clsx from 'clsx';
import { isBlobURL } from '@wordpress/blob';
import {
	SelectControl,
	Spinner,
	ToggleControl,
	__experimentalToolsPanel as ToolsPanel,
	__experimentalToolsPanelItem as ToolsPanelItem,
} from '@wordpress/components';
import { Text } from '@wordpress/ui';
import {
	BlockControls,
	BlockIcon,
	InspectorControls,
	MediaPlaceholder,
	MediaReplaceFlow,
	useBlockProps,
	useBlockEditingMode,
} from '@wordpress/block-editor';
import { __, _x } from '@wordpress/i18n';
import { useDispatch } from '@wordpress/data';
import { audio as icon } from '@wordpress/icons';
import { store as noticesStore } from '@wordpress/notices';
import { useState } from '@wordpress/element';
import { createUpgradedEmbedBlock } from '../embed/util';
import {
	useUploadMediaFromBlobURL,
	useToolsPanelDropdownMenuProps,
} from '../utils/hooks';
import { Caption } from '../utils/caption';

const ALLOWED_MEDIA_TYPES = [ 'audio' ];

/**
 * Renders the `core/audio` block on the editor.
 *
 * @param {Object}   props                   React props.
 * @param {Object}   props.attributes        Block attributes: `blob`, `src`, `caption`, `id`, `autoplay`, `loop` and `preload`.
 * @param {string}   props.className         Class name generated for the block.
 * @param {Function} props.setAttributes     Callback for updating block attributes.
 * @param {Function} props.onReplace         Callback for replacing this block with other blocks.
 * @param {boolean}  props.isSelected        Whether the block is the only selected block.
 * @param {Function} props.insertBlocksAfter Callback for inserting blocks after this one.
 *
 * @return {React.JSX.Element} React element.
 */
function AudioEdit( {
	attributes,
	className,
	setAttributes,
	onReplace,
	isSelected: isSingleSelected,
	insertBlocksAfter,
} ) {
	const { id, autoplay, loop, preload, src } = attributes;
	const [ temporaryURL, setTemporaryURL ] = useState( attributes.blob );
	const blockEditingMode = useBlockEditingMode();
	const hasNonContentControls = blockEditingMode === 'default';

	useUploadMediaFromBlobURL( {
		url: temporaryURL,
		allowedTypes: ALLOWED_MEDIA_TYPES,
		onChange: onSelectAudio,
		onError: onUploadError,
	} );

	function toggleAttribute( attribute ) {
		return ( newValue ) => {
			setAttributes( { [ attribute ]: newValue } );
		};
	}

	function onSelectURL( newSrc ) {
		// Set the block's src from the edit component's state, and switch off
		// the editing UI.
		if ( newSrc !== src ) {
			// Check if there's an embed block that handles this URL.
			const embedBlock = createUpgradedEmbedBlock( {
				attributes: { url: newSrc },
			} );
			if ( undefined !== embedBlock && onReplace ) {
				onReplace( embedBlock );
				return;
			}
			setAttributes( { src: newSrc, id: undefined, blob: undefined } );
			setTemporaryURL();
		}
	}

	const { createErrorNotice } = useDispatch( noticesStore );
	function onUploadError( message ) {
		createErrorNotice( message, { type: 'snackbar' } );
	}

	function onSelectAudio( media ) {
		if ( ! media || ! media.url ) {
			// In this case there was an error and we should continue in the editing state
			// previous attributes should be removed because they may be temporary blob urls.
			setAttributes( {
				src: undefined,
				id: undefined,
				caption: undefined,
				blob: undefined,
			} );
			setTemporaryURL();
			return;
		}

		if ( isBlobURL( media.url ) ) {
			setTemporaryURL( media.url );
			return;
		}

		// Sets the block's attribute and updates the edit component from the
		// selected media, then switches off the editing UI.
		setAttributes( {
			blob: undefined,
			src: media.url,
			id: media.id,
			caption: media.caption,
		} );
		setTemporaryURL();
	}

	const classes = clsx( className, {
		'is-transient': !! temporaryURL,
	} );

	const blockProps = useBlockProps( {
		className: classes,
	} );
	const dropdownMenuProps = useToolsPanelDropdownMenuProps();

	if ( ! src && ! temporaryURL ) {
		return (
			<div { ...blockProps }>
				<MediaPlaceholder
					icon={ <BlockIcon icon={ icon } /> }
					onSelect={ onSelectAudio }
					onSelectURL={ onSelectURL }
					accept="audio/*"
					allowedTypes={ ALLOWED_MEDIA_TYPES }
					value={ attributes }
					onError={ onUploadError }
				/>
			</div>
		);
	}

	return (
		<>
			{ isSingleSelected && (
				<BlockControls group="other">
					<MediaReplaceFlow
						mediaId={ id }
						mediaURL={ src }
						allowedTypes={ ALLOWED_MEDIA_TYPES }
						accept="audio/*"
						onSelect={ onSelectAudio }
						onSelectURL={ onSelectURL }
						onError={ onUploadError }
						onReset={ () => onSelectAudio( undefined ) }
						variant="toolbar"
					/>
				</BlockControls>
			) }
			<InspectorControls>
				<ToolsPanel
					label={ __( 'Settings' ) }
					resetAll={ () => {
						setAttributes( {
							autoplay: false,
							loop: false,
							preload: undefined,
						} );
					} }
					dropdownMenuProps={ dropdownMenuProps }
				>
					<Text className="wp-block-audio__autoplay-help-text">
						{ __(
							'Most browsers block audio until a visitor interacts with the page, so autoplay often won’t work, especially on mobile. Unexpected sound can also be disruptive for screen reader users.'
						) }
					</Text>
					<ToolsPanelItem
						label={ __( 'Autoplay' ) }
						isShownByDefault
						hasValue={ () => !! autoplay }
						onDeselect={ () =>
							setAttributes( {
								autoplay: false,
							} )
						}
					>
						<ToggleControl
							label={ __( 'Autoplay' ) }
							onChange={ toggleAttribute( 'autoplay' ) }
							checked={ !! autoplay }
						/>
					</ToolsPanelItem>
					<ToolsPanelItem
						label={ __( 'Loop' ) }
						isShownByDefault
						hasValue={ () => !! loop }
						onDeselect={ () =>
							setAttributes( {
								loop: false,
							} )
						}
					>
						<ToggleControl
							label={ __( 'Loop' ) }
							onChange={ toggleAttribute( 'loop' ) }
							checked={ !! loop }
						/>
					</ToolsPanelItem>
					<ToolsPanelItem
						label={ __( 'Preload' ) }
						isShownByDefault
						hasValue={ () => !! preload }
						onDeselect={ () =>
							setAttributes( {
								preload: undefined,
							} )
						}
					>
						<SelectControl
							label={ _x(
								'Preload',
								'noun; Audio block parameter'
							) }
							value={ preload || '' }
							// `undefined` is required for the preload attribute to be unset.
							onChange={ ( value ) =>
								setAttributes( {
									preload: value || undefined,
								} )
							}
							options={ [
								{ value: '', label: __( 'Browser default' ) },
								{ value: 'auto', label: __( 'Auto' ) },
								{ value: 'metadata', label: __( 'Metadata' ) },
								{
									value: 'none',
									label: _x( 'None', 'Preload value' ),
								},
							] }
						/>
					</ToolsPanelItem>
				</ToolsPanel>
			</InspectorControls>
			<figure { ...blockProps }>
				<audio
					controls="controls"
					inert={ ! isSingleSelected ? 'true' : undefined }
					src={ src ?? temporaryURL }
				/>
				{ !! temporaryURL && <Spinner /> }
				<Caption
					attributes={ attributes }
					setAttributes={ setAttributes }
					isSelected={ isSingleSelected }
					insertBlocksAfter={ insertBlocksAfter }
					label={ __( 'Audio caption text' ) }
					showToolbarButton={
						isSingleSelected && hasNonContentControls
					}
				/>
			</figure>
		</>
	);
}

export default AudioEdit;
