/**
 * Gutenberg / My Blocks identity icons (inserter + naming).
 * Storage: dashicon slug | `lucide:{id}` | `emoji:{char}`
 */
import { createElement } from '@wordpress/element';
import { BLOCK_ICONS } from './dashicons';
import { LUCIDE_CATALOG } from './lucideCatalog';
import { BLOCK_IDENTITY_EMOJIS } from './blockIdentityEmojis';
import {
	fetchInlineIconSvg,
	getCachedInlineIconSvg,
	normalizeInlineSvg,
} from './iconRegistry';

/**
 * @typedef {'dashicons'|'lucide'|'emoji'} BlockIdentityKind
 * @typedef {{
 *   kind: BlockIdentityKind,
 *   value: string,
 *   ref: string,
 *   label: string,
 *   keywords: string[],
 * }} BlockIdentityItem
 */

/**
 * @param {string} icon
 * @return {{ kind: BlockIdentityKind, value: string }|null}
 */
export function parseBlockIdentityIcon( icon ) {
	const raw = String( icon || '' ).trim();
	if ( ! raw ) {
		return null;
	}
	if ( /^lucide:([a-z0-9_-]+)$/i.test( raw ) ) {
		return {
			kind: 'lucide',
			value: raw.slice( 7 ).toLowerCase(),
		};
	}
	if ( /^emoji:(.+)$/u.test( raw ) ) {
		return {
			kind: 'emoji',
			value: raw.replace( /^emoji:/u, '' ),
		};
	}
	const slug = raw.replace( /^dashicons-/i, '' ).toLowerCase();
	if ( /^[a-z0-9-]+$/.test( slug ) ) {
		return { kind: 'dashicons', value: slug };
	}
	return null;
}

/**
 * @param {string} icon
 * @return {string}
 */
export function normalizeBlockIdentityIcon( icon ) {
	const parsed = parseBlockIdentityIcon( icon );
	if ( ! parsed ) {
		return 'screenoptions';
	}
	if ( parsed.kind === 'lucide' ) {
		return `lucide:${ parsed.value }`;
	}
	if ( parsed.kind === 'emoji' ) {
		return `emoji:${ parsed.value }`;
	}
	return parsed.value || 'screenoptions';
}

/**
 * Flat catalog for the identity IconPicker.
 *
 * @return {BlockIdentityItem[]}
 */
export function blockIdentityCatalog() {
	const dash = BLOCK_ICONS.map( ( id ) => ( {
		kind: 'dashicons',
		value: id,
		ref: id,
		label: id,
		keywords: [ 'dashicons', id ],
	} ) );
	const lucide = LUCIDE_CATALOG.map( ( item ) => {
		const id = item.id;
		return {
			kind: 'lucide',
			value: id,
			ref: `lucide:${ id }`,
			label: item.label || id,
			keywords: [
				'lucide',
				id,
				...( Array.isArray( item.keywords ) ? item.keywords : [] ),
			],
		};
	} );
	const emoji = BLOCK_IDENTITY_EMOJIS.map( ( char ) => ( {
		kind: 'emoji',
		value: char,
		ref: `emoji:${ char }`,
		label: char,
		keywords: [ 'emoji', char ],
	} ) );
	return [ ...dash, ...lucide, ...emoji ];
}

/**
 * Convert attribute name to React prop (stroke-width → strokeWidth).
 *
 * @param {string} name
 * @return {string}
 */
function svgAttrToProp( name ) {
	const n = String( name || '' );
	if ( n === 'class' ) {
		return 'className';
	}
	if ( n.startsWith( 'data-' ) || n.startsWith( 'aria-' ) ) {
		return n;
	}
	return n.replace( /-([a-z])/g, ( _, c ) => c.toUpperCase() );
}

/**
 * Convert Lucide SVG markup into a React icon component Gutenberg Icon can render.
 * Returns a function component (not a raw <svg>) so WP Icon does not re-wrap via
 * its primitives SVG path (which was dropping / breaking stroke icons).
 *
 * @param {string} svgString
 * @return {Function|null}
 */
export function svgMarkupToReactIcon( svgString ) {
	const normalized = normalizeInlineSvg( svgString );
	if ( ! normalized || typeof DOMParser === 'undefined' ) {
		return null;
	}

	let doc;
	try {
		doc = new DOMParser().parseFromString( normalized, 'image/svg+xml' );
	} catch ( e ) {
		return null;
	}
	const root = doc?.documentElement;
	if (
		! root ||
		root.tagName.toLowerCase() !== 'svg' ||
		root.querySelector( 'parsererror' )
	) {
		return null;
	}

	const viewBox = root.getAttribute( 'viewBox' ) || '0 0 24 24';
	const childSpecs = Array.from( root.children ).map( ( el ) => {
		const tag = el.tagName.toLowerCase();
		const attrs = {};
		Array.from( el.attributes ).forEach( ( attr ) => {
			attrs[ svgAttrToProp( attr.name ) ] = attr.value;
		} );
		return { tag, attrs };
	} );

	if ( ! childSpecs.length ) {
		return null;
	}

	function LucideBlockIcon( { size = 24, className = '', ...rest } ) {
		const dim = Number( size ) || 24;
		return createElement(
			'svg',
			{
				xmlns: 'http://www.w3.org/2000/svg',
				viewBox,
				width: dim,
				height: dim,
				fill: 'none',
				stroke: 'currentColor',
				strokeWidth: 2,
				strokeLinecap: 'round',
				strokeLinejoin: 'round',
				className: [
					'bm-block-identity-icon',
					'bm-block-identity-icon--svg',
					className,
				]
					.filter( Boolean )
					.join( ' ' ),
				style: {
					fill: 'none',
					stroke: 'currentColor',
					color: 'currentColor',
					overflow: 'visible',
				},
				'aria-hidden': true,
				focusable: 'false',
				...rest,
			},
			childSpecs.map( ( spec, index ) =>
				createElement( spec.tag, {
					...spec.attrs,
					key: `${ spec.tag }-${ index }`,
					fill: 'none',
				} )
			)
		);
	}

	return LucideBlockIcon;
}

/**
 * Build a Gutenberg `registerBlockType` icon from localized meta.
 *
 * @param {{ icon?: string, iconSvg?: string, iconEmoji?: string }} meta
 * @return {string|object|Function}
 */
export function gutenbergBlockIdentityIcon( meta = {} ) {
	const emoji = String( meta.iconEmoji || '' ).trim();
	if ( emoji ) {
		return {
			src: createElement(
				'span',
				{
					className: 'bm-block-identity-icon bm-block-identity-icon--emoji',
					style: {
						fontSize: '20px',
						lineHeight: 1,
						display: 'inline-flex',
						alignItems: 'center',
						justifyContent: 'center',
					},
					'aria-hidden': true,
				},
				emoji
			),
		};
	}

	const IconComponent = svgMarkupToReactIcon(
		String( meta.iconSvg || '' ).trim()
	);
	if ( IconComponent ) {
		return { src: IconComponent };
	}

	const parsed = parseBlockIdentityIcon( meta.icon || 'screenoptions' );
	if ( ! parsed || parsed.kind !== 'dashicons' ) {
		return 'screenoptions';
	}
	return parsed.value;
}

/**
 * Prefetch Lucide SVG for identity picker cells.
 *
 * @param {string} iconId
 * @return {Promise<string>}
 */
export function fetchLucideIdentitySvg( iconId ) {
	return fetchInlineIconSvg( 'lucide', iconId );
}

/**
 * @param {string} iconId
 * @return {string}
 */
export function cachedLucideIdentitySvg( iconId ) {
	return getCachedInlineIconSvg( `lucide:${ iconId }` );
}
