/**
 * Drag-reorder list for content elements (badge, header, buttons, …).
 * Supports legacy string[] and responsive { desktop, tablet?, mobile? } maps.
 */
import { useState } from '@wordpress/element';
import { Button } from '@wordpress/components';
import { __ } from '@wordpress/i18n';
import Notice from '../../admin/components/ui/Notice';
import {
	effectiveElementsOrder,
	serializeElementsOrder,
	setElementsOrderAtBreakpoint,
} from '../../shared/elementsOrder';
import { DropSlot, moveToSlot, slotFromItemPointer } from '../../shared/dragReorder';
/**
 * @param {object}   props
 * @param {string}   props.label
 * @param {unknown}  props.value
 * @param {Function} props.onChange
 * @param {object}   [props.field]
 * @param {boolean}  [props.appliesGlobally] When true, show notice that order applies to all block usages.
 * @param {string}   [props.breakpoint]
 */
export default function ElementsOrderField( {
	label,
	value,
	onChange,
	field = {},
	appliesGlobally = true,
	breakpoint = 'desktop',
} ) {
	const defaults = Array.isArray( field.default )
		? field.default
		: [ 'badge', 'header', 'subheader', 'buttons', 'logos' ];
	const labels = field.labels || {};
	const order = effectiveElementsOrder( value, breakpoint, defaults );
	const [ dragIndex, setDragIndex ] = useState( null );
	const [ dropSlot, setDropSlot ] = useState( null );
	const dragging = dragIndex != null;

	const commit = ( nextList ) => {
		onChange(
			serializeElementsOrder(
				setElementsOrderAtBreakpoint(
					value,
					breakpoint,
					nextList,
					defaults
				)
			)
		);
	};

	const move = ( index, dir ) => {
		const next = [ ...order ];
		const target = index + dir;
		if ( target < 0 || target >= next.length ) {
			return;
		}
		const tmp = next[ index ];
		next[ index ] = next[ target ];
		next[ target ] = tmp;
		commit( next );
	};

	const applySlot = ( from, slot ) => {
		const next = moveToSlot( order, from, slot );
		if ( next ) {
			commit( next );
		}
	};

	const readFrom = ( event ) => {
		const raw = event.dataTransfer.getData( 'text/plain' );
		return raw !== '' ? parseInt( raw, 10 ) : dragIndex;
	};

	const rows = [];
	order.forEach( ( key, index ) => {
		const isDragging = dragIndex === index;
		rows.push(
			<DropSlot
				key={ `slot-${ index }` }
				tagName="li"
				slot={ index }
				active={ dragging && dropSlot === index }
				visible={ dragging }
				onHover={ setDropSlot }
				onDrop={ ( event, slot ) => {
					applySlot( readFrom( event ), slot );
					setDragIndex( null );
					setDropSlot( null );
				} }
			/>
		);
		rows.push(
			<li
				key={ key }
				className={ [
					'bm-order-field__item',
					isDragging ? 'is-dragging' : '',
				]
					.filter( Boolean )
					.join( ' ' ) }
				draggable
				onDragStart={ ( e ) => {
					setDragIndex( index );
					setDropSlot( index );
					e.dataTransfer.effectAllowed = 'move';
					e.dataTransfer.setData( 'text/plain', String( index ) );
					if ( e.currentTarget instanceof HTMLElement ) {
						e.dataTransfer.setDragImage( e.currentTarget, 16, 16 );
					}
				} }
				onDragEnd={ () => {
					setDragIndex( null );
					setDropSlot( null );
				} }
				onDragOver={ ( e ) => {
					e.preventDefault();
					e.dataTransfer.dropEffect = 'move';
					const slot = slotFromItemPointer( e, index );
					if ( dropSlot !== slot ) {
						setDropSlot( slot );
					}
				} }
				onDrop={ ( e ) => {
					e.preventDefault();
					const slot = slotFromItemPointer( e, index );
					applySlot( readFrom( e ), slot );
					setDragIndex( null );
					setDropSlot( null );
				} }
			>
				<span className="bm-order-field__index" aria-hidden="true">
					{ index + 1 }
				</span>
				<span
					className="bm-order-field__handle"
					aria-hidden="true"
					title={ __( 'Drag to reorder', 'joc-blockora-morph' ) }
				>
					<span className="dashicons dashicons-menu" />
				</span>
				<span className="bm-order-field__name">
					{ labels[ key ] || key }
				</span>
				<div className="bm-order-field__actions">
					<Button
						size="small"
						variant="tertiary"
						className="bm-order-field__icon-btn"
						disabled={ index === 0 }
						onClick={ () => move( index, -1 ) }
						aria-label={ __( 'Move up', 'joc-blockora-morph' ) }
						icon="arrow-up-alt2"
					/>
					<Button
						size="small"
						variant="tertiary"
						className="bm-order-field__icon-btn"
						disabled={ index === order.length - 1 }
						onClick={ () => move( index, 1 ) }
						aria-label={ __( 'Move down', 'joc-blockora-morph' ) }
						icon="arrow-down-alt2"
					/>
				</div>
			</li>
		);
	} );
	rows.push(
		<DropSlot
			key="slot-end"
			tagName="li"
			slot={ order.length }
			active={ dragging && dropSlot === order.length }
			visible={ dragging }
			onHover={ setDropSlot }
			onDrop={ ( event, slot ) => {
				applySlot( readFrom( event ), slot );
				setDragIndex( null );
				setDropSlot( null );
			} }
		/>
	);

	return (
		<div className="bm-field bm-order-field">
			{ label ? <span className="bm-field-label">{ label }</span> : null }
			<p className="bm-order-field__hint">
				{ __(
					'Drag items onto the drop lines between rows (or use the arrows).',
					'joc-blockora-morph'
				) }
			</p>
			{ appliesGlobally ? (
				<Notice variant="info" className="bm-order-field__notice">
					{ __(
						'This order is shared everywhere this block is used. Want a different arrangement? Clone this block, or create another hero and set its own order and layout.',
						'joc-blockora-morph'
					) }
				</Notice>
			) : null }
			<ul className="bm-order-field__list">{ rows }</ul>
		</div>
	);
}
