import { useSelect } from '@wordpress/data';
import { store as blockEditorStore } from '@wordpress/block-editor';
import { useAsync } from 'react-async-hook';
import { useMemo, useRef, useEffect } from '@wordpress/element';
import { adminAjaxFetchMemoized } from './helpers';
import { GPEntriesBlockAttributes } from './blocks/entries';

export function useFormId(): number | undefined {
	return useSelect( ( select: any ) => {
		const {
			getSelectedBlock,
			getBlockParentsByBlockName,
			getBlocksByClientId,
		} = select( blockEditorStore );

		const clientId = getSelectedBlock()?.clientId;

		if ( ! clientId ) {
			return undefined;
		}

		const parentIds = getBlockParentsByBlockName( clientId, [
			'gp-entry-blocks/entries',
		] );
		const parents = getBlocksByClientId( parentIds );

		if ( ! parents.length ) {
			return undefined;
		}

		const parent = parents[ 0 ];

		return parent.attributes.formId;
	}, [] );
}

export function useSelectedBlock() {
	return useSelect( ( select ) => {
		return select( blockEditorStore ).getSelectedBlock();
	} );
}

export function useEntriesBlocks() : BlockProps<GPEntriesBlockAttributes>[] {
	return useSelect( ( select: any ) => {
		const {
			getBlocks,
		} = select( blockEditorStore );

		const entriesBlocks: BlockProps<GPEntriesBlockAttributes>[] = [];

		for ( const block of getBlocks() ) {
			if ( block.name !== 'gp-entry-blocks/entries' ) {
				continue;
			}

			entriesBlocks.push( block );
		}

		return entriesBlocks;
	}, [] );
}

export function useHasChildBlocks( props: BlockProps<any> ) {
	return useSelect(
		( select ) => {
			return (
				select( blockEditorStore ).getBlockCount( props.clientId ) > 0
			);
		},
		[ props.clientId ]
	);
}

export function useMultiEntryBlockCount( block? ): number {
	const entriesBlock = useEntriesBlock( block );

	return entriesBlock?.innerBlocks?.filter( ( innerBlock ) =>
		[
			'gp-entry-blocks/entries-table',
			'gp-entry-blocks/entries-loop',
			'gp-entry-blocks/pagination',
		].includes( innerBlock.name )
	).length;
}

export function useCurrentView(): 'entries' | 'view' | 'edit' | undefined {
	const selectedBlock = useSelectedBlock();
	const multiEntryBlockCount = useMultiEntryBlockCount();
	const viewEntryBlock = useViewEntryBlock();
	const editEntryBlock = useEditEntryBlock();

	return useSelect(
		( select ) => {
			if ( ! selectedBlock ) {
				return undefined;
			}

			const { getBlockParentsByBlockName } = select( blockEditorStore );

			const isEntriesDescendent =
				getBlockParentsByBlockName(
					selectedBlock?.clientId,
					'gp-entry-blocks/entries'
				)?.length > 0;
			const isViewDescendent =
				getBlockParentsByBlockName(
					selectedBlock?.clientId,
					'gp-entry-blocks/view-entry'
				)?.length > 0;
			const isEditDescendent =
				getBlockParentsByBlockName(
					selectedBlock?.clientId,
					'gp-entry-blocks/edit-entry'
				)?.length > 0;

			if (
				! isEntriesDescendent &&
				selectedBlock.name !== 'gp-entry-blocks/entries'
			) {
				return undefined;
			} else if (
				isViewDescendent ||
				selectedBlock.name === 'gp-entry-blocks/view-entry'
			) {
				return 'view';
			} else if (
				isEditDescendent ||
				selectedBlock.name === 'gp-entry-blocks/edit-entry'
			) {
				return 'edit';
			}

			/* If no multi entry block is present, then the current view can't be entries. */
			if ( multiEntryBlockCount === 0 ) {
				if ( viewEntryBlock ) {
					return 'view';
				}

				if ( editEntryBlock ) {
					return 'edit';
				}
			}

			return 'entries';
		},
		[ selectedBlock ]
	);
}

export function useEntriesBlock( block? ) {
	const selectedBlock = useSelectedBlock();

	if ( ! block ) {
		block = selectedBlock;
	}

	return useSelect(
		( select ) => {
			const { getBlockParentsByBlockName, getBlock } = select(
				blockEditorStore
			);

			if ( ! block ) {
				return undefined;
			}

			if ( block.name === 'gp-entry-blocks/entries' ) {
				return getBlock( block.clientId );
			}

			const entriesBlockClientId = getBlockParentsByBlockName(
				block?.clientId,
				'gp-entry-blocks/entries'
			)?.[ 0 ];

			if ( ! entriesBlockClientId ) {
				return undefined;
			}

			return getBlock( entriesBlockClientId );
		},
		[ block ]
	);
}

export function useViewEntryBlock( block? ) {
	const entriesBlock = useEntriesBlock( block );

	return entriesBlock?.innerBlocks?.find(
		( innerBlock ) => innerBlock.name === 'gp-entry-blocks/view-entry'
	);
}

export function useEditEntryBlock( block? ) {
	const entriesBlock = useEntriesBlock( block );

	return entriesBlock?.innerBlocks?.find(
		( innerBlock ) => innerBlock.name === 'gp-entry-blocks/edit-entry'
	);
}

export function useBlockAJAXPreview<B = any>(
	block: BlockProps<B>
) {
	/* https://github.com/slorber/react-async-hook/issues/31#issuecomment-662883815 */
	const params = useMemo( () => ( {
		action: 'gpeb_block_preview',
		block: JSON.stringify( block ),
	} ), [ JSON.stringify( block ) ] ); // JSON.stringify() here to avoid unnecessary re-renders

	const response = useAsync<{ html: string }>( adminAjaxFetchMemoized, [ params ], {
		setLoading: ( state ) => ( { ...state, loading: true } ),
	} );

	if ( ! response.result ) {
		return null;
	}

	return response.result.html;
}

export function usePreventInnerEventsRef( innerContents: React.ReactNode ) {
	const ref = useRef( null );

	/**
	 * Disable onClick of all elements inside the ref.
	 */
	useEffect( () => {
		if ( ! ref.current ) {
			return;
		}

		for ( const childEl of ref.current.querySelectorAll( '*' ) ) {
			childEl.onclick = ( event ) => event.preventDefault();
		}
	}, [ ref, innerContents ] );

	return ref;
}
