import { React, PropTypes, classnames } from '@gravityforms/libraries';
import { useIdContext, useStoreContext } from '@gravityforms/react-utils';
import { getId } from './utils';
import Pill from '../../elements/Pill';
import { BACKSPACE, DELETE } from '../../utils/keymap';

const { forwardRef, useRef } = React;

const COUNT_PLACEHOLDER = '{{count}}';

/**
 * @module CondensedDropdownPill
 * @description The condensed pill component for the dropdown.
 *
 * @since 5.6.3
 *
 * @param {object}   props          The component props.
 * @param {Function} props.onChange Callback for when the dropdown changes.
 *
 * @return {JSX.Element} The condensed pill component.
 */
const CondensedDropdownPill = ( {
	onChange = () => {},
} ) => {
	const triggerRef = useStoreContext( ( state ) => state.triggerRef );
	const selectedItem = useStoreContext( ( state ) => state.selectedItem );
	const setSelectedItem = useStoreContext( ( state ) => state.setSelectedItem );
	const i18n = useStoreContext( ( state ) => state.i18n );

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

	/**
	 * @function getCondensedPillLabel
	 * @description Get the label for the condensed pill.
	 *
	 * @since 5.6.3
	 *
	 * @return {string|JSX.Element} The label for the condensed pill.
	 */
	const getCondensedPillLabel = () => {
		const label = i18n?.condensedPillLabel;

		if ( typeof label === 'string' ) {
			if ( label.includes( COUNT_PLACEHOLDER ) ) {
				return label.replaceAll( COUNT_PLACEHOLDER, `${ selectedItem.length }` );
			}
			return label;
		}

		return `${ selectedItem.length }`;
	};

	return <DropdownPill
		content={ getCondensedPillLabel() }
		removeItem={ ( event ) => {
			onChange( event, [] );
			setSelectedItem( [] );
			triggerRef.current.focus();
		} }
	/>;
};

CondensedDropdownPill.propTypes = {
	onChange: PropTypes.func,
};

/**
 * @module DefaultDropdownPill
 * @description The default pill component for the dropdown.
 *
 * @since 4.6.3
 *
 * @param {object}   props            The component props.
 * @param {object}   props.item       The item object.
 * @param {Function} props.selectItem Callback to select an item.
 *
 * @return {JSX.Element} The default pill component.
 */
const DefaultDropdownPill = ( {
	item = {},
	selectItem = () => {},
} ) => {
	const selectedItem = useStoreContext( ( state ) => state.selectedItem );
	const triggerRef = useStoreContext( ( state ) => state.triggerRef );
	const pillRef = useRef( null );

	/**
	 * @function removeItem
	 * @description Removes the item from the dropdown.
	 *
	 * @since 4.6.3
	 *
	 * @param {Event} event The event object.
	 */
	const removeItem = ( event ) => {
		// Find index of removed item.
		const index = selectedItem.findIndex( ( selItem ) => selItem.value === item.value );
		const length = selectedItem.length;

		// Remove item from selected items.
		selectItem( event, item );

		// If current item is last one and there are more than 1 item, set focus on previous one.
		if ( pillRef.current && length > 1 && index === length - 1 ) {
			pillRef.current.previousSibling.focus();
		}

		// If current item is last one and there is only 1 item, set focus on trigger.
		if ( length === 1 ) {
			triggerRef.current.focus();
		}
	};

	return <DropdownPill
		content={ item.label }
		removeItem={ removeItem }
		ref={ pillRef }
	/>;
};

DefaultDropdownPill.propTypes = {
	item: PropTypes.object,
	selectItem: PropTypes.func,
};

/**
 * @module DropdownPill
 * @description The pill component for the dropdown.
 *
 * @param {object}   props            The component props.
 * @param {string}   props.content    The content of the pill.
 * @param {Function} props.removeItem Callback to remove the item.
 * @param {object}   ref              The ref object.
 *
 * @return {JSX.Element} The pill component.
 */
const DropdownPill = forwardRef( ( {
	content = '',
	removeItem = () => {},
}, ref ) => {
	const pillProps = {
		content,
		customClasses: [ 'gform-dropdown__pill' ],
		customAttributes: {
			role: 'option',
			tabIndex: '0',
			'aria-keyshortcuts': 'Backspace Delete',
			onKeyDown: ( event ) => {
				// If not backspace or delete, return early.
				if ( ! [ BACKSPACE, DELETE ].includes( event.key ) ) {
					return;
				}

				removeItem( event );
			},
		},
		tagName: 'div',
		onClick: removeItem,
	};

	return <Pill { ...pillProps } ref={ ref } />;
} );

DropdownPill.propTypes = {
	content: PropTypes.string,
	removeItem: PropTypes.func,
};

/**
 * @module DropdownPills
 * @description The pills component for the dropdown.
 *
 * @since 4.5.0
 *
 * @param {object}   props               The component props.
 * @param {boolean}  props.condensePills Whether to condense pills.
 * @param {boolean}  props.multi         Whether the dropdown is multi-select.
 * @param {Function} props.onChange      Callback for when the dropdown changes.
 * @param {Function} props.selectItem    Callback for when an item is selected.
 * @param {object}   ref                 The ref object.
 *
 * @return {JSX.Element} The pills component.
 */
const DropdownPills = forwardRef( ( {
	condensePills = false,
	multi = false,
	onChange = () => {},
	selectItem = () => {},
}, ref ) => {
	const id = useIdContext();
	const selectedItem = useStoreContext( ( state ) => state.selectedItem );

	// If not multi, or selected item is not array, return early.
	if ( ! multi || ! Array.isArray( selectedItem ) ) {
		return null;
	}

	const pillsId = getId( id, 'pills' );
	const pillsProps = {
		className: classnames( {
			'gform-dropdown__pills': true,
			'gform-dropdown__pills--condensed': condensePills,
		} ),
		id: pillsId,
		role: 'listbox',
	};

	return (
		<div { ...pillsProps } ref={ ref }>
			{
				condensePills
					? <CondensedDropdownPill onChange={ onChange } />
					: selectedItem.map( ( item, index ) => (
						<DefaultDropdownPill key={ index } item={ item } selectItem={ selectItem } />
					) )
			}
		</div>
	);
} );

DropdownPills.propTypes = {
	condensePills: PropTypes.bool,
	multi: PropTypes.bool,
	onChange: PropTypes.func,
	selectItem: PropTypes.func,
};

export default DropdownPills;
