import { React, classnames, PropTypes } from '@gravityforms/libraries';
import { usePopup } from '@gravityforms/react-utils';
import { spacerClasses } from '@gravityforms/utils';
import DroplistItem from './DroplistItem';
import DroplistList from './DroplistList';
import Popover from '../../elements/Popover';

const { forwardRef, useCallback, useEffect, useRef } = React;

/**
 * @module DroplistGroupItem
 * @description The DroplistGroupItem component.
 *
 * @since 4.3.0
 *
 * @param {object}      props                Props for the DroplistGroupItem component.
 * @param {number}      props.depth          The depth of the item.
 * @param {number}      props.index          The index of the item.
 * @param {object}      props.item           The item object.
 * @param {object}      props.propsWithState The props and state object.
 * @param {object|null} ref                  Ref to the component.
 *
 * @return {JSX.Element|null} The DroplistGroupItem component.
 */
export const DroplistGroupItem = forwardRef( ( {
	depth = 0,
	index = 0,
	item = {},
	propsWithState = {},
}, ref ) => {
	const containerRef = useRef( null );

	const {
		align,
		itemKey,
		openOnHover,
		selectedState,
		setSelectedState,
		stackNestedGroups,
	} = propsWithState;
	const {
		onAfterClose = () => {},
		onAfterOpen = () => {},
		onClose = () => {},
		onOpen = () => {},
	} = item;
	const {
		customClasses: groupTriggerCustomClasses = [],
		id: triggerId = `${ itemKey }-trigger`,
		onClick = () => {},
		...restGroupTriggerAttributes
	} = item.triggerAttributes || {};
	const {
		customClasses: groupListContainerCustomClasses = [],
		width = 0,
		...restGroupListContainerAttributes
	} = item.listContainerAttributes || {};
	const {
		closePopup,
		openPopup,
		popupHide,
		popupOpen,
		popupReveal,
		popupRef,
		triggerRef,
	} = usePopup( {
		closeOnClickOutside: false,
		onAfterClose,
		onAfterOpen,
		onClose,
		onOpen,
	} );

	/**
	 * @function updateSelectedState
	 * @description Updates the selected state.
	 */
	const updateSelectedState = () => {
		if ( popupOpen ) {
			setSelectedStateClosed();
		} else {
			setSelectedStateOpen();
		}
	};

	/**
	 * @function setSelectedStateOpen
	 * @description Sets the selected state to open.
	 */
	const setSelectedStateOpen = () => {
		// If the group is already open, do nothing.
		if ( selectedState[ depth ] === triggerId ) {
			return;
		}
		const depthKeys = Object.keys( selectedState );
		const filteredState = depthKeys
			.filter( ( key ) => key < depth )
			.reduce( ( acc, key ) => {
				acc[ key ] = selectedState[ key ];
				return acc;
			}, {} );
		const newSelectedState = {
			...filteredState,
			[ depth ]: triggerId,
		};
		setSelectedState( newSelectedState );
	};

	/**
	 * @function setSelectedStateClosed
	 * @description Sets the selected state to closed.
	 */
	const setSelectedStateClosed = () => {
		const depthKeys = Object.keys( selectedState );
		const filteredState = depthKeys
			.filter( ( key ) => key < depth )
			.reduce( ( acc, key ) => {
				acc[ key ] = selectedState[ key ];
				return acc;
			}, {} );
		setSelectedState( filteredState );
	};

	useEffect( () => {
		if ( selectedState[ depth ] === triggerId && ! popupOpen ) {
			openPopup();
		} else if ( selectedState[ depth ] !== triggerId && popupOpen ) {
			closePopup();
		}
	}, [ selectedState, triggerId, popupOpen ] );

	const groupListItemProps = {
		className: classnames( {
			'gform-droplist__item': true,
			'gform-droplist__item--group': true,
			'gform-droplist__item--has-divider': item.hasDivider,
		}, item.customClasses || [] ),
	};
	const groupTriggerProps = {
		customAttributes: {
			'aria-expanded': popupOpen ? 'true' : 'false',
			'aria-haspopup': 'listbox',
			id: triggerId,
			onClick: ( event ) => {
				onClick( event );
				updateSelectedState();
			},
			onMouseEnter: openOnHover && ! stackNestedGroups ? () => {
				setSelectedStateOpen();
			} : undefined,
		},
		customClasses: classnames(
			'gform-droplist__item-trigger',
			groupTriggerCustomClasses,
		),
		depth,
		index,
		...restGroupTriggerAttributes,
	};
	const popoverProps = {
		align: 'top',
		autoPlacement: true,
		className: classnames(
			'gform-droplist__popover',
			groupListContainerCustomClasses,
		),
		containerRef,
		isHide: popupHide,
		isOpen: popupOpen,
		isReveal: popupReveal,
		placement: align === 'left' ? 'right' : 'left',
		popoverClasses: spacerClasses( align === 'left' ? [ 0, 0, 0, 2 ] : [ 0, 2, 0, 0 ] ),
		popoverRef: popupRef,
		triggerRef: containerRef, // Use container to position the popover rather than the trigger itself.
		width,
		...restGroupListContainerAttributes,
	};

	const setRefs = useCallback( ( node ) => {
		containerRef.current = node;
		if ( ref ) {
			ref.current = node;
		}
	}, [ ref ] );

	return (
		<li { ...groupListItemProps } ref={ setRefs }>
			<DroplistItem { ...groupTriggerProps } ref={ triggerRef } />
			{ stackNestedGroups ? null : (
				<Popover { ...popoverProps } ref={ popupRef }>
					<DroplistList { ...propsWithState } listItems={ item.listItems } depth={ depth + 1 } />
				</Popover>
			) }
		</li>
	);
} );

DroplistGroupItem.propTypes = {
	depth: PropTypes.number,
	item: PropTypes.object,
	propsWithState: PropTypes.object,
};

DroplistGroupItem.displayName = 'DroplistGroupItem';

export default DroplistGroupItem;
