/**
 * WordPress provides React during the page load. This is for TypeScript type checking only.
 *
 * Importing React from @wordpress/element causes "Cannot read property 'createElement' of undefined"
 */
import React, { useEffect } from 'react';

import { Placeholder } from '@wordpress/components';
import {
	useBlockProps,
	InnerBlocks,
	// eslint-disable-next-line @wordpress/no-unsafe-wp-apis
	__experimentalBlockVariationPicker,
	store as blockEditorStore,
} from '@wordpress/block-editor';
import FormSelectPlaceholder from '../../components/FormSelectPlaceholder';
import GPEBEntriesInspectorControls from './GPEBEntriesInspectorControls';
import { nanoid } from 'nanoid';
import {
	useCurrentView,
	useEntriesBlocks,
	useMultiEntryBlockCount,
} from '../../hooks';
import { Filters } from '../../components/controls-inspector/sidebar/FiltersControl/FiltersControl';
import { variations } from './variations';
import { useDispatch, useSelect } from '@wordpress/data';
import { __ } from '@wordpress/i18n';

import {
	createBlocksFromInnerBlocksTemplate,
	store as blocksStore,
} from '@wordpress/blocks';

export interface GPEntriesBlockAttributes {
	formId: number;
	filters: Filters;
	order: 'desc' | 'asc';
	orderBy: string;
	limit: number;
	uuid: string;
	mode: 'multi-entry' | 'view-single' | 'edit-single';
}

const Block = ( args: BlockProps< GPEntriesBlockAttributes > ) => {
	const { title, clientId, setAttributes, name } = args;

	const hasInnerBlocks = useSelect(
		( select ) =>
			select( blockEditorStore ).getBlocks( clientId ).length > 0,
		[ clientId ]
	);

	const { replaceInnerBlocks } = useDispatch( blockEditorStore );

	// eslint-disable-next-line no-shadow,@typescript-eslint/no-shadow
	const { defaultVariation, variations } = useSelect(
		( select ) => {
			const {
				getBlockVariations,
				getBlockType,
				getDefaultBlockVariation,
			} = select( blocksStore );

			return {
				blockType: getBlockType( name ),
				defaultVariation: getDefaultBlockVariation( name, 'block' ),
				variations: getBlockVariations( name, 'block' ),
			};
		},
		[ name ]
	);

	if ( ! window.GPEB.canEditEntriesBlocks ) {
		return (
			<Placeholder
				label={ __( 'Entries Block', 'gp-entry-blocks' ) }
				instructions={ __(
					'Sorry, you do not have permission to edit Gravity Forms Entry Blocks.',
					'gp-entry-blocks'
				) }
			/>
		);
	}

	if ( ! args.attributes.formId ) {
		return (
			<Placeholder
				label={ __( 'Entries Block', 'gp-entry-blocks' ) }
				instructions={ __( 'Select a form below to start displaying entries.', 'gp-entry-blocks' ) }
			>
				<div>
					<FormSelectPlaceholder { ...args } />
				</div>
			</Placeholder>
		);
	}

	// make allowed blocks filterable
	return hasInnerBlocks ? (
		<InnerBlocks
			allowedBlocks={ [
				'gp-entry-blocks/entries-table',
				'gp-entry-blocks/entries-loop',
				'gp-entry-blocks/pagination',
				'gp-entry-blocks/view-entry',
				'gp-entry-blocks/edit-entry',
			] }
		/>
	) : (
		<__experimentalBlockVariationPicker
			icon={ undefined }
			label={ title }
			variations={ variations }
			onSelect={ ( nextVariation = defaultVariation ) => {
				if ( nextVariation.attributes ) {
					setAttributes( nextVariation.attributes );
				}
				if ( nextVariation.innerBlocks ) {
					replaceInnerBlocks(
						clientId,
						createBlocksFromInnerBlocksTemplate(
							nextVariation.innerBlocks
						),
						true
					);
				}
			} }
			allowSkip
		/>
	);
};

/**
 * Entries blocks have a UUID attribute that is passed down to all of its inner Entry Blocks blocks using context. This is so we can associate the GF_Queryer instance of the main Entries block.
 *
 * @param  props
 */
function useVerifyUUID( props: BlockProps< GPEntriesBlockAttributes > ): void {
	const entriesBlocks = useEntriesBlocks();

	const generateUUID = () => props.setAttributes( {
		uuid: nanoid(),
	} );

	return useEffect( () => {
		for ( const block of entriesBlocks ) {
			if ( ! block.attributes?.uuid || block.clientId === props.clientId ) {
				continue;
			}

			if ( block.attributes.uuid === props.attributes.uuid ) {
				// eslint-disable-next-line no-console
				console.info( 'Duplicate Entry Blocks UUID detected. Changing UUID to be unique.' );
				generateUUID();
			}
		}

		// Generate UUID that can be used to reference PHP class instances belonging to this block.
		if ( ! props.attributes.uuid ) {
			generateUUID();
		}
	}, [ props.clientId, props.attributes.uuid ] );
}

const Edit = ( args: BlockProps< GPEntriesBlockAttributes > ) => {
	const blockProps = useBlockProps();
	const currentView = useCurrentView();
	const multiEntryBlockCount = useMultiEntryBlockCount( args );

	useVerifyUUID( args );

	/**
	 * Add class to hide blocks outside of the Single and Edit blocks if viewing Single/Edit blocks or any of their
	 * inner blocks.
	 */
	if (
		multiEntryBlockCount &&
		( currentView === 'view' || currentView === 'edit' )
	) {
		blockProps.className += ' gpeb-is-view-or-edit';
	}

	blockProps.className += ' gpeb-mode-' + args.attributes.mode;

	return (
		<div { ...blockProps }>
			{ window.GPEB.canEditEntriesBlocks && (
				<GPEBEntriesInspectorControls { ...args } />
			) }

			<Block { ...args } />
		</div>
	);
};

const Save = () => {
	const blockProps = useBlockProps.save();

	return (
		<div { ...blockProps }>
			<InnerBlocks.Content />
		</div>
	);
};

export const settings = {
	variations,
	edit: Edit,
	save: Save,
};
