/**
 * WordPress dependencies
 */
import { Platform } from '@wordpress/element';

/**
 * Internal dependencies
 */
import { createBlock, findTransform } from '../factory';
import parse from '../parser';
import { getBlockAttributes } from '../parser/get-block-attributes';
import { getRawTransforms } from './get-raw-transforms';
import type { Block } from '../../types';

/**
 * Converts HTML directly to blocks. Looks for a matching transform for each
 * top-level tag. The HTML should be filtered to not have any text between
 * top-level tags and formatted in a way that blocks can handle the HTML.
 *
 * @param html    HTML to convert.
 * @param handler The handler calling htmlToBlocks: either rawHandler
 *                or pasteHandler.
 *
 * @return An array of blocks.
 */
export function htmlToBlocks(
	html: string,
	handler: ( options: { HTML: string } ) => Block[] | string
): Block[] {
	const doc = document.implementation.createHTMLDocument( '' );

	doc.body.innerHTML = html;

	return Array.from( doc.body.children ).flatMap( ( node ) => {
		const transforms = getRawTransforms();
		const rawTransform = findTransform(
			transforms as unknown as Parameters< typeof findTransform >[ 0 ],
			( ( t: unknown ) => {
				const transform = t as ( typeof transforms )[ number ];
				return transform.isMatch( node );
			} ) as Parameters< typeof findTransform >[ 1 ]
		) as unknown as ( typeof transforms )[ number ] | null;

		if ( ! rawTransform ) {
			// Until the HTML block is supported in the native version, we'll parse it
			// instead of creating the block to generate it as an unsupported block.
			if ( ( Platform as Record< string, unknown > ).isNative ) {
				return parse(
					`<!-- wp:html -->${ node.outerHTML }<!-- /wp:html -->`
				);
			}
			return createBlock(
				// Should not be hardcoded.
				'core/html',
				getBlockAttributes( 'core/html', node.outerHTML )
			);
		}

		const { transform, blockName } = rawTransform;

		if ( transform ) {
			const block = transform( node, handler ) as Block;
			if ( node.hasAttribute( 'class' ) ) {
				block.attributes.className = node.getAttribute( 'class' );
			}
			return block;
		}

		return createBlock(
			blockName!,
			getBlockAttributes( blockName!, node.outerHTML )
		);
	} );
}
