import { useMemo } from '@wordpress/element';
import { useSelect } from '@wordpress/data';
import type { WPCompleter } from '@wordpress/components';
import type { GFMergeTag } from '../stores/gpeb';
import { FormMergeTags } from '../stores/gpeb';
import { useFormId } from '../hooks';
import Fuse from 'fuse.js';

/**
 * A user mentions completer.
 *
 * @type {WPCompleter}
 */
export const GFMergeTagCompleter = {
	name: 'gf-merge-tag',
	className: 'editor-autocompleters__gf-merge-tag',
	triggerPrefix: '{{',

	useItems( filterValue ) {
		const formId = useFormId();

		const mergeTags: GFMergeTag[] | undefined = useSelect(
			( select ) => {
				if ( ! formId ) {
					return [];
				}

				const { getFormMergeTags } = select( 'gp-entry-blocks' );
				const formMergeTags: FormMergeTags = getFormMergeTags( formId );

				if ( ! formMergeTags ) {
					return undefined;
				}

				const set: Set< string > = new Set();

				for ( const mergeTagGroup of Object.values( formMergeTags ) ) {
					for ( const groupTag of mergeTagGroup.tags ) {
						/* Cheap way of unique objects. */
						set.add( JSON.stringify( groupTag ) );
					}
				}

				return Array.from( set ).map( ( jsonMergeTag ) => {
					return JSON.parse( jsonMergeTag );
				} );
			},
			[ filterValue ]
		);

		const options = useMemo( () => {
			if ( ! mergeTags ) {
				return [];
			}

			// Stop trying to autocomplete once the merge tag is closed
			if ( filterValue.indexOf( '}' ) !== -1 ) {
				return [];
			}

			const fuseList = mergeTags.map( ( mergeTag ) => ( {
				key: `gf-merge-tag-${ mergeTag.tag.replace( /(^{|}$)/g, '' ) }`,
				value: mergeTag.tag,
				label: mergeTag.label,
			} ) );

			if ( ! filterValue ) {
				return fuseList;
			}

			const fuse = new Fuse( fuseList, {
				minMatchCharLength: 1,
				keys: [ 'value', 'label' ],
			} );

			return fuse.search( filterValue ).map( ( result ) => {
				return result.item;
			} );
		}, [ mergeTags, filterValue ] );

		return [ options ];
	},

	getOptionCompletion( mergeTag: GFMergeTag[ 'tag' ] ) {
		return `${ mergeTag }`;
	},
};

export function appendMergeTagCompleter( completers, blockName ) {
	return [ ...completers, { ...GFMergeTagCompleter } ];
}
