import {
	BlockControls,
	useBlockProps,
	useInnerBlocksProps,
	store as blockEditorStore,
} from '@wordpress/block-editor';
import { ToolbarButton } from '@wordpress/components';
import { useDispatch, useSelect, useRegistry } from '@wordpress/data';
import { isRTL, __ } from '@wordpress/i18n';
import {
	formatListBullets,
	formatListBulletsRTL,
	formatListNumbered,
	formatListNumberedRTL,
	formatOutdent,
	formatOutdentRTL,
} from '@wordpress/icons';
import { createBlock } from '@wordpress/blocks';
import { useCallback, useEffect } from '@wordpress/element';
import deprecated from '@wordpress/deprecated';
import OrderedListSettings from './ordered-list-settings';
import { migrateToListV2 } from './utils';
import TagName from './tag-name';

const DEFAULT_BLOCK = {
	name: 'core/list-item',
};

/**
 * At the moment, deprecations don't handle create blocks from attributes
 * (like when using CPT templates). For this reason, this hook is necessary
 * to avoid breaking templates using the old list block format.
 *
 * @param {Object} attributes Block attributes.
 * @param {string} clientId   Block client ID.
 */
function useMigrateOnLoad( attributes, clientId ) {
	const registry = useRegistry();
	const { updateBlockAttributes, replaceInnerBlocks, selectBlock } =
		useDispatch( blockEditorStore );

	useEffect( () => {
		// As soon as the block is loaded, migrate it to the new version.

		if ( ! attributes.values ) {
			return;
		}

		const [ newAttributes, newInnerBlocks ] = migrateToListV2( attributes );

		deprecated( 'Value attribute on the list block', {
			since: '6.0',
			version: '6.5',
			alternative: 'inner blocks',
		} );

		// The selection can be inside an inner block created from the block
		// type template at insertion, which the migration replaces; restore
		// the selection to the migrated block in that case.
		const shouldReselectBlock = registry
			.select( blockEditorStore )
			.hasSelectedInnerBlock( clientId, true );

		registry.batch( () => {
			updateBlockAttributes( clientId, newAttributes );
			replaceInnerBlocks( clientId, newInnerBlocks );
			if ( shouldReselectBlock ) {
				selectBlock( clientId );
			}
		} );
	}, [ attributes.values ] );
}

function useOutdentList( clientId ) {
	const { replaceBlocks, selectionChange } = useDispatch( blockEditorStore );
	const { getBlockRootClientId, getBlockAttributes, getBlock } =
		useSelect( blockEditorStore );

	return useCallback( () => {
		const parentBlockId = getBlockRootClientId( clientId );
		const parentBlockAttributes = getBlockAttributes( parentBlockId );
		// Create a new parent block without the inner blocks.
		const newParentBlock = createBlock(
			'core/list-item',
			parentBlockAttributes
		);
		const { innerBlocks } = getBlock( clientId );
		// Replace the parent block with a new parent block without inner blocks,
		// and make the inner blocks siblings of the parent.
		replaceBlocks( [ parentBlockId ], [ newParentBlock, ...innerBlocks ] );
		// Select the last child of the list being outdent.
		selectionChange( innerBlocks[ innerBlocks.length - 1 ].clientId );
	}, [ clientId ] );
}

function IndentUI( { clientId } ) {
	const outdentList = useOutdentList( clientId );
	const canOutdent = useSelect(
		( select ) => {
			const { getBlockRootClientId, getBlockName } =
				select( blockEditorStore );
			return (
				getBlockName( getBlockRootClientId( clientId ) ) ===
				'core/list-item'
			);
		},
		[ clientId ]
	);
	return (
		<>
			<ToolbarButton
				icon={ isRTL() ? formatOutdentRTL : formatOutdent }
				title={ __( 'Outdent' ) }
				description={ __( 'Outdent list item' ) }
				disabled={ ! canOutdent }
				onClick={ outdentList }
			/>
		</>
	);
}

export default function Edit( { attributes, setAttributes, clientId } ) {
	const { ordered, type, reversed, start } = attributes;
	const blockProps = useBlockProps( {
		style: {
			listStyleType: ordered && type !== 'decimal' ? type : undefined,
		},
	} );

	const innerBlocksProps = useInnerBlocksProps( blockProps, {
		defaultBlock: DEFAULT_BLOCK,
		directInsert: true,
		templateLock: false,
		__experimentalCaptureToolbars: true,
	} );
	useMigrateOnLoad( attributes, clientId );

	const controls = (
		<BlockControls group="block">
			<ToolbarButton
				icon={ isRTL() ? formatListBulletsRTL : formatListBullets }
				title={ __( 'Unordered' ) }
				description={ __( 'Convert to unordered list' ) }
				isActive={ ordered === false }
				onClick={ () => {
					setAttributes( { ordered: false } );
				} }
			/>
			<ToolbarButton
				icon={ isRTL() ? formatListNumberedRTL : formatListNumbered }
				title={ __( 'Ordered' ) }
				description={ __( 'Convert to ordered list' ) }
				isActive={ ordered === true }
				onClick={ () => {
					setAttributes( { ordered: true } );
				} }
			/>
			<IndentUI clientId={ clientId } />
		</BlockControls>
	);

	return (
		<>
			<TagName
				ordered={ ordered }
				reversed={ reversed }
				start={ start }
				{ ...innerBlocksProps }
			/>
			{ controls }
			{ ordered && (
				<OrderedListSettings
					{ ...{
						setAttributes,
						reversed,
						start,
						type,
					} }
				/>
			) }
		</>
	);
}
