import { React, ReactDND, PropTypes, classnames } from '@gravityforms/libraries';
import { ConditionalWrapper } from '@gravityforms/react-utils';
import { spacerClasses, sprintf } from '@gravityforms/utils';
import { UP, DOWN, BLOCK, INLINE } from './constants';
import { ARROW_UP, ARROW_DOWN, ARROW_LEFT, ARROW_RIGHT, TAB } from '../../utils/keymap';
import itemTypes from './item-types';
import Button from '../../elements/Button';
import Text from '../../elements/Text';

const { useRef, useState } = React;
const { useDrag, useDrop } = ReactDND;

const RepeaterItem = ( {
	addItem = undefined,
	actionButtons = [],
	addButtonAttributes = {},
	addButtonClasses = [],
	blockContentTitle = '',
	blockHeaderAttributes = {},
	blockHeaderClasses = [],
	children = null,
	collapseItem = () => {},
	collapsible = false,
	collapsibleButtonAttributes = {},
	collapsibleButtonClasses = [],
	confirmDelete = false,
	deleteConfirmationComponent = null,
	deleteButtonAttributes = {},
	deleteButtonClasses = [],
	deleteItem = () => {},
	downButtonAttributes = {},
	downButtonClasses = [],
	dragHandleAttributes = {},
	dragHandleClasses = [],
	fillContent = false,
	i18n = {},
	id = '',
	index = 0,
	inlineAdd = false,
	isCollapsed = false,
	isDraggable = false,
	isSortable = false,
	itemAttributes = {},
	itemClasses = [],
	itemCount = 0,
	itemDraggable = false,
	itemSpacing = '',
	maxItems = 0,
	minItems = 0,
	moveItem = () => {},
	repeaterInstanceId = '',
	showActions = false,
	showActionsOnHover = false,
	showAdd = false,
	showArrows = false,
	showDelete = false,
	showDragHandle = false,
	speak = () => {},
	style = 'regular',
	type = INLINE,
	upButtonAttributes = {},
	upButtonClasses = [],
} ) => {
	const ref = useRef( null );
	const dragHandleRef = useRef( null );
	const startIndexRef = useRef( null );
	const [ keyboardDragActive, setKeyboardDragActive ] = useState( false );
	const inlineButtonSize = style === 'minimal' && type === INLINE ? 'size-height-s' : 'size-height-m';
	const [ isConfirmingDelete, setIsConfirmingDelete ] = useState( false );

	const dndItemType = `${ itemTypes.REPEATER_ITEM }_${ repeaterInstanceId }`;

	const handleKeyboardNav = ( event ) => {
		event.stopPropagation();

		if ( event.key === TAB && keyboardDragActive ) {
			event.preventDefault();
			return;
		}

		if ( ! keyboardDragActive ) {
			return;
		}

		if ( ( event.key === ARROW_UP || event.key === ARROW_LEFT ) ) {
			moveItem( index, index - 1, id );
			return;
		}

		if ( ( event.key === ARROW_DOWN || event.key === ARROW_RIGHT ) ) {
			moveItem( index, index + 1, id );
		}
	};

	const beginDragging = () => {
		speak( sprintf( i18n.beginDrag, id ) );
		setKeyboardDragActive( true );
	};

	const endDragging = () => {
		speak( sprintf( i18n.endDrag, id ) );
		setKeyboardDragActive( false );
	};

	const toggleDragging = () => {
		if ( keyboardDragActive ) {
			endDragging();
		} else {
			beginDragging();
		}
	};

	const handleCollapsibleClick = () => {
		collapseItem( index, id );
	};

	const handleArrowPress = ( event, direction ) => {
		const toIndex = direction === UP ? index - 1 : index + 1;
		moveItem( index, toIndex, id );
	};

	const handleDelete = () => {
		if ( confirmDelete && deleteConfirmationComponent ) {
			setIsConfirmingDelete( true ); // Trigger confirmation dialog
		} else {
			deleteItem( index, id ); // Proceed directly if no confirmation required
		}
	};

	const handleConfirmDelete = ( confirmed ) => {
		setIsConfirmingDelete( false ); // Reset confirmation state
		if ( confirmed ) {
			deleteItem( index, id ); // Proceed with deletion if confirmed
		}
	};

	const [ { isDragging }, drag, preview ] = useDrag( {
		type: dndItemType,
		item: { id, index, repeaterInstanceId },
		collect: ( monitor ) => ( {
			isDragging: monitor.isDragging(),
		} ),
	} );

	const [ { handlerId }, drop ] = useDrop( {
		accept: dndItemType,
		collect( monitor ) {
			return {
				handlerId: monitor.getHandlerId(),
				isOver: monitor.isOver(),
			};
		},
		hover( item, monitor ) {
			if ( item.repeaterInstanceId !== repeaterInstanceId ) {
				return;
			}

			if ( ! ref.current || item.index === index ) {
				return;
			}

			const dragIndex = item.index;
			const hoverIndex = index;

			if ( startIndexRef.current === null ) {
				startIndexRef.current = dragIndex;
			}

			if ( dragIndex === hoverIndex ) {
				return;
			}

			const hoverBoundingRect = ref.current?.getBoundingClientRect();
			const hoverMiddleY = ( hoverBoundingRect.bottom - hoverBoundingRect.top ) / 2;
			const clientOffset = monitor.getClientOffset();
			const hoverClientY = clientOffset.y - hoverBoundingRect.top;

			if ( dragIndex < hoverIndex && hoverClientY < hoverMiddleY ) {
				return;
			}

			if ( dragIndex > hoverIndex && hoverClientY > hoverMiddleY ) {
				return;
			}

			moveItem( dragIndex, hoverIndex, id );
			item.index = hoverIndex;
		},
		drop( item ) {
			speak( sprintf( i18n.endDrop, id, item.index ) );
		},
	} );

	if ( itemDraggable && isDraggable && isSortable ) {
		drag( drop( ref ) );
	} else if ( showDragHandle && isDraggable && isSortable ) {
		drag( dragHandleRef );
		preview( drop( ref ) );
	}

	const renderControls = () => {
		if ( ( ! showArrows && ( ! showDragHandle || ! isDraggable ) ) || ! isSortable ) {
			return null;
		}

		const dragHandleProps = {
			size: inlineButtonSize,
			type: style === 'minimal' ? 'icon-grey' : 'icon-white',
			icon: 'drag-indicator',
			iconPrefix: 'gravity-component-icon',
			label: i18n.dragLabel,
			customAttributes: {
				type: 'button',
				onKeyDown: handleKeyboardNav,
			},
			onClick: ( e ) => toggleDragging( e ),
			customClasses: classnames( [
				'gform-repeater-item__control',
				'gform-repeater-item__control--drag-toggle',
			], dragHandleClasses ),
			ref: showDragHandle && ! itemDraggable ? dragHandleRef : undefined,
			disabled: itemCount === 1,
			...dragHandleAttributes,
		};

		const upButtonProps = {
			size: inlineButtonSize,
			type: style === 'minimal' ? 'icon-grey' : 'icon-white',
			label: sprintf( i18n.upLabel, id ),
			iconPrefix: 'gravity-component-icon',
			icon: 'chevron-up',
			onClick: ( e ) => handleArrowPress( e, UP ),
			customAttributes: {
				type: 'button',
			},
			customClasses: classnames( [
				'gform-repeater-item__control',
				'gform-repeater-item__control--up',
			], upButtonClasses ),
			disabled: itemCount === 1 || ( itemCount !== 1 && index === 0 ),
			...upButtonAttributes,
		};

		const downButtonProps = {
			size: inlineButtonSize,
			type: style === 'minimal' ? 'icon-grey' : 'icon-white',
			label: sprintf( i18n.downLabel, id ),
			iconPrefix: 'gravity-component-icon',
			icon: 'chevron-down',
			onClick: ( e ) => handleArrowPress( e, DOWN ),
			customAttributes: {
				type: 'button',
			},
			customClasses: classnames( [
				'gform-repeater-item__control',
				'gform-repeater-item__control--down',
			], downButtonClasses ),
			disabled: itemCount === 1 || ( itemCount !== 1 && index === itemCount - 1 ),
			...downButtonAttributes,
		};

		const className = classnames( [
			'gform-repeater-item__controls',
		] );

		return (
			<div className={ className }>
				{ showDragHandle && isDraggable && isSortable && <Button { ...dragHandleProps } /> }
				{ showArrows && isSortable && <Button { ...upButtonProps } /> }
				{ showArrows && isSortable && <Button { ...downButtonProps } /> }
			</div>
		);
	};

	const itemWrapperProps = {
		className: 'gform-repeater-item__wrapper',
	};

	const blockHeaderProps = {
		className: classnames( [
			'gform-repeater-item__block-header',
		], blockHeaderClasses ),
		size: 'text-sm',
		weight: 'medium',
		...blockHeaderAttributes,
	};

	let buttonType = 'white';

	if ( type === INLINE && style === 'minimal' ) {
		buttonType = 'icon-grey';
	} else if ( type === INLINE ) {
		buttonType = 'icon-white';
	}

	const renderActionButtons = () => {
		return actionButtons.length ? actionButtons.map( ( button, idx ) => {
			const { icon, label, onClick, ...rest } = button;
			return (
				<Button
					key={ `action-button-${ idx }` }
					size={ inlineButtonSize }
					type={ buttonType }
					icon={ icon }
					iconPosition="leading"
					iconPrefix="gravity-component-icon"
					label={ label }
					onClick={ ( event ) => onClick( event, index ) }
					customAttributes={ {
						type: 'button',
					} }
					customClasses={ [
						'gform-repeater-item__action',
						'gform-repeater-item__action-control',
					] }
					{ ...rest }
				/>
			);
		} ) : null;
	};

	const deleteButtonProps = {
		size: inlineButtonSize,
		type: buttonType,
		icon: 'trash',
		iconPosition: 'leading',
		iconPrefix: 'gravity-component-icon',
		label: i18n.deleteLabel,
		onClick: handleDelete, // Use handleDelete to manage confirmation
		disabled: minItems > 0 && itemCount <= minItems,
		customAttributes: {
			type: 'button',
		},
		customClasses: classnames( [
			'gform-repeater-item__delete',
			'gform-repeater-item__action-control',
		], deleteButtonClasses ),
		...deleteButtonAttributes,
	};

	const addButtonProps = {
		size: inlineButtonSize,
		type: buttonType,
		icon: 'plus-regular',
		iconAttributes: addButtonAttributes?.iconAttributes || {},
		iconPosition: 'leading',
		iconPrefix: 'gravity-component-icon',
		label: i18n.addLabel,
		onClick: addItem,
		disabled: !! ( maxItems && itemCount >= maxItems ) || addButtonAttributes?.disabled,
		customAttributes: {
			type: 'button',
		},
		customClasses: classnames( [
			'gform-repeater-item__add',
			'gform-repeater-item__action-control',
		], addButtonClasses ),
	};

	const collapsibleButtonProps = {
		size: 'size-height-m',
		type: 'icon-grey',
		iconPrefix: 'gravity-component-icon',
		icon: 'chevron-down',
		label: i18n.collapsibleLabel,
		onClick: ( e ) => handleCollapsibleClick( e ),
		customAttributes: {
			type: 'button',
			'aria-expanded': ! isCollapsed,
			'aria-controls': `${ id }-block-content`,
			id: `${ id }-collapsible-button`,
		},
		customClasses: classnames( [
			'gform-repeater-item__collapsible',
		], collapsibleButtonClasses ),
		...collapsibleButtonAttributes,
	};

	const blockContentProps = {
		'aria-hidden': isCollapsed,
		'aria-labelledby': `${ id }-collapsible-button`,
		className: 'gform-repeater-item__block-content',
		id: `${ id }-block-content`,
		role: 'region',
	};

	const componentProps = {
		className: classnames( {
			'gform-repeater-item': true,
			[ `gform-repeater-item--style-${ style }` ]: true,
			[ `gform-repeater-item--type-${ type }` ]: type,
			'gform-repeater-item--show-actions-on-hover': showActionsOnHover,
			'gform-repeater-item--has-arrows': showArrows && isSortable,
			'gform-repeater-item--has-drag-handle': showDragHandle && isDraggable && isSortable,
			'gform-repeater-item--is-draggable': isDraggable && isSortable,
			'gform-repeater-item--is-sortable': isSortable,
			'gform-repeater-item--is-collapsed': isCollapsed,
			'gform-repeater-item--is-dragging': isDragging,
			'gform-repeater-item--fill-content': fillContent,
			'gform-repeater-item--disable-item-drag': ! itemDraggable,
			'gform-repeater-item--is-keyboard-nav': keyboardDragActive,
			...spacerClasses( itemSpacing ),
		}, itemClasses ),
		'data-index': index,
		'data-handler-id': handlerId,
		id,
		...itemAttributes,
	};

	return (
		<div { ...componentProps } ref={ ref }>
			<div { ...itemWrapperProps }>
				{ renderControls() }
				{ type === BLOCK && blockContentTitle && (
					<Text { ...blockHeaderProps }>{ blockContentTitle }</Text>
				) }
				{ type === INLINE && children }
				<ConditionalWrapper
					condition={ ( showAdd || showActions || showDelete ) && type === INLINE && style === 'minimal' }
					wrapper={ ( ch ) => <div className="gform-repeater-item__minimal-actions">{ ch }</div> }
				>
					{ showAdd && type === INLINE && <Button { ...addButtonProps } /> }
					{ showActions && type === INLINE && renderActionButtons() }
					{ showDelete && type === INLINE && <Button { ...deleteButtonProps } /> }
				</ConditionalWrapper>
				{ collapsible && type === BLOCK && <Button { ...collapsibleButtonProps } /> }
			</div>
			{ type === BLOCK && (
				<div { ...blockContentProps }>
					{ children }
					<div className="gform-repeater-item__block-content-footer">
						{ showActions && renderActionButtons() }
						{ showDelete && <Button { ...deleteButtonProps } /> }
					</div>
					{ showAdd && ! inlineAdd && <Button { ...addButtonProps } /> }
				</div>
			) }
			{ isConfirmingDelete && deleteConfirmationComponent && (
				deleteConfirmationComponent( { index, id, onConfirm: handleConfirmDelete } )
			) }
		</div>
	);
};

RepeaterItem.propTypes = {
	addItem: PropTypes.func,
	actionButtons: PropTypes.array,
	blockContentTitle: PropTypes.string,
	blockHeaderAttributes: PropTypes.object,
	blockHeaderClasses: PropTypes.oneOfType( [
		PropTypes.element,
		PropTypes.func,
		PropTypes.array,
		PropTypes.string,
	] ),
	children: PropTypes.node,
	collapseItem: PropTypes.func,
	collapsible: PropTypes.bool,
	collapsibleButtonAttributes: PropTypes.object,
	collapsibleButtonClasses: PropTypes.oneOfType( [
		PropTypes.element,
		PropTypes.func,
		PropTypes.array,
		PropTypes.string,
	] ),
	confirmDelete: PropTypes.bool,
	deleteConfirmationComponent: PropTypes.func,
	deleteButtonAttributes: PropTypes.object,
	deleteButtonClasses: PropTypes.oneOfType( [
		PropTypes.element,
		PropTypes.func,
		PropTypes.array,
		PropTypes.string,
	] ),
	deleteItem: PropTypes.func,
	downButtonAttributes: PropTypes.object,
	downButtonClasses: PropTypes.oneOfType( [
		PropTypes.element,
		PropTypes.func,
		PropTypes.array,
		PropTypes.string,
	] ),
	dragHandleAttributes: PropTypes.object,
	dragHandleClasses: PropTypes.oneOfType( [
		PropTypes.element,
		PropTypes.func,
		PropTypes.array,
		PropTypes.string,
	] ),
	fillContent: PropTypes.bool,
	i18n: PropTypes.object,
	id: PropTypes.string.isRequired,
	index: PropTypes.number.isRequired,
	inlineAdd: PropTypes.bool,
	isCollapsed: PropTypes.bool,
	isDraggable: PropTypes.bool,
	isSortable: PropTypes.bool,
	itemAttributes: PropTypes.object,
	itemClasses: PropTypes.oneOfType( [
		PropTypes.element,
		PropTypes.func,
		PropTypes.array,
		PropTypes.string,
	] ),
	itemCount: PropTypes.number,
	itemDraggable: PropTypes.bool,
	itemSpacing: PropTypes.oneOfType( [
		PropTypes.string,
		PropTypes.number,
		PropTypes.array,
		PropTypes.object,
	] ),
	maxItems: PropTypes.number,
	minItems: PropTypes.number,
	moveItem: PropTypes.func.isRequired,
	repeaterInstanceId: PropTypes.string,
	showAdd: PropTypes.bool,
	showActions: PropTypes.bool,
	showActionsOnHover: PropTypes.bool,
	showArrows: PropTypes.bool,
	showDelete: PropTypes.bool,
	showDragHandle: PropTypes.bool,
	speak: PropTypes.func,
	style: PropTypes.string,
	type: PropTypes.string,
	upButtonAttributes: PropTypes.object,
	upButtonClasses: PropTypes.oneOfType( [
		PropTypes.string,
		PropTypes.array,
		PropTypes.object,
	] ),
};

export default RepeaterItem;
