/**
 * WordPress dependencies
 */
import { __, sprintf } from '@wordpress/i18n';
import { useBlockProps, useInnerBlocksProps, store as blockEditorStore, BlockControls } from '@wordpress/block-editor';
import { Toolbar, ToolbarGroup, ToolbarButton } from '@wordpress/components';
import { useDispatch, useSelect } from '@wordpress/data';
import { useCallback, useEffect } from '@wordpress/element';
import { createBlock } from '@wordpress/blocks';

/**
 * The defined Inner Blocks Template for Variations.
 */
const TEMPLATE = [
	[
		'personalizewp-pro/variation',
		{
			"metadata": {
				"name" : sprintf(
					// Translators: %d: Variation number being added.
					__( 'Variation %d', 'personalizewp' ),
					1
				),
			},
		},
		[
			[ 'core/paragraph', { 'placeholder' : __( 'Add variation content here', 'personalizewp' ) } ]
		],
	],
	[
		'personalizewp-pro/variation-fallback',
		{
			"metadata": {
				"name" : __( 'Fallback', 'personalizewp' )
			},
			"supports": {
				"lock": false
			},
			"lock": {
				"move": true,
				"remove": true
			},
			"inserter": false,
			"multiple": true,
			"html": false,
			"renaming": false,
		},
		[
			[ 'core/paragraph', { 'placeholder' : __( 'Add fallback content here', 'personalizewp' ) } ]
		],
	],
];

/**
 * The edit function describes the structure of your block in the context of the
 * editor. This represents what the editor will render when the block is used.
 *
 * @see https://developer.wordpress.org/block-editor/reference-guides/block-api/block-edit-save/#edit
 *
 * @return {WPElement} Element to render.
 */
export default function edit ( props ) {

	const {
		attributes,
		setAttributes,
		clientId,
		isSelected,
	} = props;

	/**
	 * List attributes as constants
	 */
	const {
		variationsBlockId,
	} = attributes;

	/**
	 * Get all the blocks within the selected variation, and the selected variations' client ID.
	 */
	const { innerVariationBlocks, selectedVariationClientId } = useSelect(
		( select ) => {
			const {
				getBlocks,
				getSelectedBlockClientId,
				hasSelectedInnerBlock,
			} = select( blockEditorStore );
			const innerBlocks = getBlocks( clientId );
			const selectedBlockClientId = getSelectedBlockClientId();
			let selectedVariationId = null;

			// Find the first variation that is selected or has selected inner blocks so we can set it as active.
			for ( const block of innerBlocks ) {
				if (
					block.clientId === selectedBlockClientId ||
					hasSelectedInnerBlock( block.clientId, true )
				) {
					selectedVariationId = block.clientId;
					break;
				}
			}
			return {
				innerVariationBlocks: innerBlocks,
				selectedVariationClientId: selectedVariationId,
			};
		},
		[ clientId ]
	);

	const {
		__unstableMarkNextChangeAsNotPersistent,
		updateBlockAttributes,
		insertBlock,
		selectBlock
	} = useDispatch( blockEditorStore );

	/**
	 * Use the currently selected variation clientId to mark it as active.
	 */
	const setActiveVariation = useCallback(
		( activeVariationClientId ) => {
			// Set each inner variations `isActive` attribute.
			innerVariationBlocks.forEach( ( block ) => {
				__unstableMarkNextChangeAsNotPersistent();
				updateBlockAttributes( block.clientId, {
					isActive: block.clientId === activeVariationClientId,
				} );
			} );
		},
		[
			innerVariationBlocks,
			updateBlockAttributes,
			__unstableMarkNextChangeAsNotPersistent,
		]
	);

	/**
	 * Set initial block attributes.
	 */
	useEffect( () => {
		// Set blockID, if not already set. Based on the unique and changing clientId (which is as good as anything).
		if ( ! variationsBlockId || '' === variationsBlockId ) {
			setAttributes( { variationsBlockId: clientId } );
		}
		// Set the fallback variation as active when the editor is loaded.
		if ( innerVariationBlocks?.length ) {
			setActiveVariation( innerVariationBlocks[ innerVariationBlocks.length - 1 ].clientId );
		}
	}, [] ); // Empty array ensures that this runs when block is created or editor loaded.

	/**
	 * Update active variation when selection or inner blocks change.
	 */
	useEffect( () => {
		const hasActiveVariation =
			innerVariationBlocks &&
			innerVariationBlocks.some( ( block ) => block.attributes.isActive );

		if ( selectedVariationClientId ) {
			// If an inner variation block is selected, or its inner blocks are selected, it becomes the active variation.
			setActiveVariation( selectedVariationClientId );
		} else if ( ! hasActiveVariation && innerVariationBlocks?.length ) {
			// Otherwise, if there's no active variation, default to the last inner tab (fallback).
			setActiveVariation( innerVariationBlocks[ innerVariationBlocks.length - 1 ].clientId );
		}
	}, [
		innerVariationBlocks,
		selectedVariationClientId,
		setActiveVariation,
	] );

	/**
	 * Checks for the blockId and sets if required.
	 */
	useEffect( () => {
		if ( null === variationsBlockId || '' === variationsBlockId ) {
			setAttributes( { variationsBlockId: clientId } );
		}
	}, [
		clientId,
		isSelected,
	] );

	/**
	 * Insert new "pwp/content-variation" block as a child of this variations block.
	 */
	const insertNewVariation = () => {

		// Increment the variation number when adding a new variant.
		const newVariationNumber = innerVariationBlocks.length;

		// Insert the new variant, using the same template as above.
		const newVariationBlock = createBlock(
			'personalizewp-pro/variation',
			{
				'metadata': {
					'name' : sprintf(
						// Translators: %d: Variation number being added.
						__( 'Variation %d', 'personalizewp' ),
						newVariationNumber
					),
				},
			},
			[
				createBlock(
					'core/paragraph',
					{
						'placeholder' : __( 'Add variation content here', 'personalizewp' )
					}
				)
			]
		);

		// Insert the new variation ahead of the fallback.
		insertBlock( newVariationBlock, innerVariationBlocks.length - 1, clientId );
		// Select the new variation block for immediate content entry.
		selectBlock( newVariationBlock.clientId );
	};

	// Note: 'variationsBlockId' is automatically added to blockprops.
	const blockProps = useBlockProps();
	const innerBlocksProps = useInnerBlocksProps( blockProps, {
		template: TEMPLATE,
		renderAppender: false, // We're manually adding new variation containers.
	} );

	return (
		<>
			<BlockControls group="block">
				<Toolbar label={ __( 'Variations', 'personalizewp' ) } >
					<ToolbarGroup>
						{ innerVariationBlocks.map( ( { clientId, attributes } ) => {
							const { metadata, isActive } = attributes;

							return (
								<ToolbarButton
									key={ clientId }
									label={ metadata.name }
									isActive={ isActive }
									onClick={ () => setActiveVariation( clientId ) }
								>
									{ metadata.name }
								</ToolbarButton>
							);
						} ) }
						<ToolbarButton
							icon="plus"
							label={ __( 'New Variation', 'personalizewp' ) }
							onClick={ () => insertNewVariation() }
						/>
					</ToolbarGroup>
				</Toolbar>
			</BlockControls>
			<div { ...innerBlocksProps } />
		</>
	);
}
