import { __ } from '@wordpress/i18n';
import { BaseControl, Button, Dropdown } from '@wordpress/components';
import { useEffect, useState } from '@wordpress/element';

const LENGTH_UNITS = [ 'px', 'em', 'rem', 'vw', 'vh', 'ch' ];
const LENGTH_PERCENT_UNITS = [ 'px', 'em', '%', 'rem', 'vw', 'vh', 'ch' ];

const PRESET_CONFIGS = {
	spacing: {
		units: LENGTH_PERCENT_UNITS,
		defaultUnit: 'px',
	},
	margin: {
		units: LENGTH_PERCENT_UNITS,
		defaultUnit: 'px',
	},
	fontSize: {
		units: LENGTH_PERCENT_UNITS,
		defaultUnit: 'px',
	},
	letterSpacing: {
		units: LENGTH_UNITS,
		defaultUnit: 'px',
	},
	size: {
		units: LENGTH_PERCENT_UNITS,
		defaultUnit: 'px',
	},
	minSize: {
		units: LENGTH_PERCENT_UNITS,
		defaultUnit: 'px',
	},
	maxSize: {
		units: LENGTH_PERCENT_UNITS,
		defaultUnit: 'px',
	},
	gap: {
		units: LENGTH_PERCENT_UNITS,
		defaultUnit: 'px',
	},
	borderWidth: {
		units: LENGTH_UNITS,
		defaultUnit: 'px',
	},
	borderRadius: {
		units: LENGTH_PERCENT_UNITS,
		defaultUnit: 'px',
	},
	inset: {
		units: LENGTH_PERCENT_UNITS,
		defaultUnit: 'px',
	},
	flexBasis: {
		units: LENGTH_PERCENT_UNITS,
		defaultUnit: 'px',
	},
	translate: {
		units: LENGTH_PERCENT_UNITS,
		defaultUnit: 'px',
	},
	translateZ: {
		units: LENGTH_UNITS,
		defaultUnit: 'px',
	},
	shadow: {
		units: LENGTH_UNITS,
		defaultUnit: 'px',
	},
	blur: {
		units: LENGTH_UNITS,
		defaultUnit: 'px',
	},
	filterPercent: {
		units: [ '%' ],
		defaultUnit: '%',
	},
	iconSize: {
		units: LENGTH_PERCENT_UNITS,
		defaultUnit: 'px',
	},
};

const isNumericValue = ( value ) =>
	/^[+-]?(?:\d+\.?\d*|\.\d+)$/.test( String( value ?? '' ).trim() );

const isEditableNumericValue = ( value ) =>
	/^[+-]?(?:\d+\.?\d*|\.\d*|)$/.test( String( value ?? '' ).trim() );

const extractExampleValue = ( placeholder = '' ) => {
	const trimmed = String( placeholder ?? '' ).trim();

	if ( ! trimmed ) {
		return '';
	}

	const withoutPrefix = trimmed.replace( /^e\.g\.,\s*/i, '' ).trim();
	return withoutPrefix.split( ',' )[ 0 ]?.trim() || '';
};

const getUnitMatch = ( value, units ) => {
	const sortedUnits = [ ...units ].sort( ( a, b ) => b.length - a.length );
	const lowerValue = value.toLowerCase();

	return (
		sortedUnits.find( ( unit ) => {
			if ( ! lowerValue.endsWith( unit.toLowerCase() ) ) {
				return false;
			}

			const numericPart = value.slice( 0, value.length - unit.length );
			return isEditableNumericValue( numericPart );
		} ) || ''
	);
};

const getDefaultUnit = ( config, placeholder ) => {
	const exampleValue = extractExampleValue( placeholder );
	const matchedUnit = getUnitMatch( exampleValue, config.units || [] );

	return matchedUnit || config.defaultUnit || config.units?.[ 0 ] || 'px';
};

const getControlState = ( rawValue, config, defaultUnit ) => {
	const value =
		typeof rawValue === 'string'
			? rawValue.trim()
			: rawValue === undefined || rawValue === null
				? ''
				: String( rawValue ).trim();

	if ( ! value ) {
		return {
			inputValue: '',
			unit: defaultUnit,
			mode: 'unit',
		};
	}

	const matchedUnit = getUnitMatch( value, config.units || [] );

	if ( matchedUnit ) {
		return {
			inputValue: value.slice( 0, value.length - matchedUnit.length ),
			unit: matchedUnit,
			mode: 'unit',
		};
	}

	if ( isEditableNumericValue( value ) ) {
		return {
			inputValue: value,
			unit: defaultUnit,
			mode: 'unit',
		};
	}

	return {
		inputValue: value,
		unit: defaultUnit,
		mode: 'raw',
	};
};

const serializeState = ( state ) => {
	const trimmedInput = String( state.inputValue ?? '' ).trim();

	if ( ! trimmedInput ) {
		return '';
	}

	if ( state.mode === 'raw' || ! isNumericValue( trimmedInput ) ) {
		return state.inputValue;
	}

	return `${ trimmedInput }${ state.unit }`;
};

export default function UnitValueControl( {
	label,
	value = '',
	onChange,
	placeholder = '',
	help = '',
	className = '',
	preset = 'spacing',
} ) {
	const config = PRESET_CONFIGS[ preset ] || PRESET_CONFIGS.spacing;
	const availableUnits = config.units || [];
	const hasSelectableUnits = availableUnits.length > 1;
	const defaultUnit = getDefaultUnit( config, placeholder );
	const [ controlState, setControlState ] = useState( () =>
		getControlState( value, config, defaultUnit )
	);

	useEffect( () => {
		setControlState( getControlState( value, config, defaultUnit ) );
	}, [ value, config, defaultUnit ] );

	const placeholderValue = extractExampleValue( placeholder );
	const matchedPlaceholderUnit = getUnitMatch( placeholderValue, config.units || [] );
	const inputPlaceholder = matchedPlaceholderUnit
		? placeholderValue.slice( 0, placeholderValue.length - matchedPlaceholderUnit.length )
		: placeholder;

	const handleInputChange = ( event ) => {
		const nextInputValue = event.target.value;
		const nextState = {
			inputValue: nextInputValue,
			unit: controlState.unit,
			mode:
				nextInputValue.trim() && ! isEditableNumericValue( nextInputValue )
					? 'raw'
					: 'unit',
		};

		setControlState( nextState );
		onChange( serializeState( nextState ) );
	};

	const handleUnitSelect = ( unit ) => {
		const nextState = {
			inputValue: controlState.inputValue,
			unit,
			mode:
				controlState.inputValue.trim() &&
				! isEditableNumericValue( controlState.inputValue )
					? 'raw'
					: 'unit',
		};

		setControlState( nextState );
		onChange( serializeState( nextState ) );
	};

	return (
		<BaseControl
			label={ label }
			help={ help }
			className={ [ 'axb-unit-control', className ].filter( Boolean ).join( ' ' ) }
		>
			<div className="axb-unit-control__field">
				<input
					type="text"
					className="components-text-control__input axb-unit-control__input"
					value={ controlState.inputValue }
					onChange={ handleInputChange }
					placeholder={ inputPlaceholder }
					aria-label={ typeof label === 'string' ? label : __( 'Value', 'axcelersblocks' ) }
				/>
				{ hasSelectableUnits ? (
					<Dropdown
						className="axb-unit-control__dropdown"
						contentClassName="axb-unit-control__popover-content"
						popoverProps={ {
							className: 'axb-unit-control__popover-wrapper',
							noArrow: false,
						} }
						position="bottom right"
						renderToggle={ ( { isOpen, onToggle } ) => (
							<Button
								className={
									isOpen
										? 'axb-unit-control__toggle is-open'
										: 'axb-unit-control__toggle'
								}
								onClick={ onToggle }
								aria-expanded={ isOpen }
								aria-label={ __( 'Select unit', 'axcelersblocks' ) }
							>
								{ controlState.unit }
							</Button>
						) }
						renderContent={ ( { onClose } ) => (
							<div className="axb-unit-control__popover">
								<div className="axb-unit-control__units">
									{ availableUnits.map( ( unit ) => (
										<Button
											key={ unit }
											className={
												unit === controlState.unit
													? 'axb-unit-control__unit-button is-selected'
													: 'axb-unit-control__unit-button'
											}
											onClick={ () => {
												handleUnitSelect( unit );
												onClose();
											} }
											aria-pressed={ unit === controlState.unit }
										>
											{ unit }
										</Button>
									) ) }
								</div>
							</div>
						) }
					/>
				) : (
					<span className="axb-unit-control__toggle axb-unit-control__toggle--static">
						{ controlState.unit }
					</span>
				) }
			</div>
		</BaseControl>
	);
}
