/**
 * External dependencies
 */
import { Component } from '@wordpress/element';
import { createHigherOrderComponent } from '@wordpress/compose';
import { compareWithWpVersion } from '@woocommerce/settings';

const { getBlockType } = wp.blocks;
const { addFilter } = wp.hooks;

/**
 * withDefaultAttributes HOC for editor.BlockListBlock.
 *
 * @param	object BlockListBlock The BlockListBlock element.
 */
const withDefaultAttributes = createHigherOrderComponent(
	( BlockListBlock ) => {
		class WrappedComponent extends Component {
			mounted = false;

			componentDidMount() {
				const { block, setAttributes } = this.props;

				if ( block.name.startsWith( 'woocommerce/' ) ) {
					setAttributes( this.getAttributesWithDefaults() );
				}
			}

			componentDidUpdate() {
				if (
					this.props.block.name.startsWith( 'woocommerce/' ) &&
					! this.mounted
				) {
					this.mounted = true;
				}
			}

			getAttributesWithDefaults() {
				const blockType = getBlockType( this.props.block.name );
				let attributes = this.props.attributes;

				if (
					! this.mounted &&
					this.props.block.name.startsWith( 'woocommerce/' ) &&
					typeof blockType.attributes !== 'undefined' &&
					typeof blockType.defaults !== 'undefined'
				) {
					attributes = Object.assign(
						{},
						this.props.attributes || {}
					);
					Object.keys( blockType.attributes ).map( ( key ) => {
						if (
							typeof attributes[ key ] === 'undefined' &&
							typeof blockType.defaults[ key ] !== 'undefined'
						) {
							attributes[ key ] = blockType.defaults[ key ];
						}
						return key;
					} );
				}
				return attributes;
			}

			render() {
				return (
					<BlockListBlock
						{ ...this.props }
						attributes={ this.getAttributesWithDefaults() }
					/>
				);
			}
		}
		return WrappedComponent;
	},
	'withDefaultAttributes'
);

/**
 * Hook into `editor.BlockListBlock` to set default attributes (if blocks
 * define them separately) when a block is inserted.
 *
 * This is a workaround for Gutenberg which does not save "default" attributes
 * to the post, which means if defaults change, all existing blocks change too.
 *
 * See https://github.com/WordPress/gutenberg/issues/7342
 *
 * To use this, the block name needs a `woocommerce/` prefix, and as well
 * as defining `attributes` during block registration, you must also declare an
 * array called `defaults`. Defaults should be omitted from `attributes`.
 */
if ( compareWithWpVersion( '5.3', '<=' ) ) {
	addFilter(
		'editor.BlockListBlock',
		'woocommerce-blocks/block-list-block',
		withDefaultAttributes
	);
}
