/**
 * Style control that mirrors Gutenberg ColorPalette / FontSizePicker / Dimensions.
 */
import {
	BaseControl,
	Button,
	ColorIndicator,
	Dropdown,
	TextControl,
	ToggleControl,
	__experimentalDropdownContentWrapper as DropdownContentWrapper,
} from '@wordpress/components';
import { __ } from '@wordpress/i18n';
import { useState } from '@wordpress/element';
import { useThemeTokens } from '../hooks/useThemeTokens';
import {
	CONTENT_ALIGN_OPTIONS,
	CONTENT_ALIGN_VARS,
	CONTAINER_ALIGN_OPTIONS,
	CONTAINER_ALIGN_VARS,
	elementContentAlignVars,
	CONTENT_MAX_WIDTH_OPTIONS,
	CONTENT_MAX_WIDTH_UNITS,
	COLUMNS_BREAKPOINT_MOBILE,
	COLUMNS_BREAKPOINT_OPTIONS,
	COLUMNS_BREAKPOINT_TABLET,
	COLUMNS_BREAKPOINT_UNITS,
	CARD_COLUMNS_DEFAULT,
	CARD_COLUMNS_OPTIONS,
	DEFAULT_BLOCK_FONT_FAMILY,
	MIN_HEIGHT_NONE,
	MIN_HEIGHT_OPTIONS,
	MIN_HEIGHT_UNITS,
	FONT_WEIGHT_OPTIONS,
	resolveStyleFieldType,
} from '../styleFieldDefaults';
import FlexibleUnitControl from './FlexibleUnitControl';
import FontSizeField from './FontSizeField';
import SearchableSelect from './SearchableSelect';
import SpacingControl from './SpacingControl';
import IntelligentSpacing from './IntelligentSpacing';
import BoxBorderField from './BoxBorderField';
import BoxRadiusField from './BoxRadiusField';
import ShadowFields from './ShadowFields';
import BmMediaUpload from './fields/BmMediaUpload';
import FillColorPalette, {
	DEFAULT_FILL_GRADIENT,
	DEFAULT_FILL_SOLID,
	DEFAULT_OVERLAY_GRADIENT,
	DEFAULT_OVERLAY_SOLID,
	isGradientValue,
	styleFieldAllowsGradient,
} from './FillColorPalette';
import {
	colorDropdownPopoverProps,
	useColorDropdownOutsideClose,
} from './colorDropdownFocus';
import { isAbsentColor } from '../cssVars';
// Styles: src/shared/shared-ui.js → build/shared-ui.css

function ContentAlignField( { field, value = '', onChange } ) {
	const fallback = field?.default && CONTENT_ALIGN_VARS[ field.default ]
		? field.default
		: 'center';
	const current = CONTENT_ALIGN_VARS[ value ] ? value : fallback;

	return (
		<SearchableSelect
			label={ field.label }
			help={ field.help }
			className="bm-style-field bm-style-field--content-align"
			value={ current }
			options={ CONTENT_ALIGN_OPTIONS }
			onChange={ ( next ) => {
				const align = CONTENT_ALIGN_VARS[ next ] ? next : fallback;
				onChange( CONTENT_ALIGN_VARS[ align ] );
			} }
		/>
	);
}

function ElementContentAlignField( { field, value = '', onChange } ) {
	const prefix = String( field?.alignPrefix || '' ).trim();
	const fallback =
		field?.default === 'center' ||
		field?.default === 'left' ||
		field?.default === 'right'
			? field.default
			: 'left';
	const current =
		value === 'center' || value === 'left' || value === 'right'
			? value
			: fallback;

	return (
		<SearchableSelect
			label={ field.label }
			help={ field.help }
			className="bm-style-field bm-style-field--element-content-align"
			value={ current }
			options={ CONTENT_ALIGN_OPTIONS }
			onChange={ ( next ) => {
				const align =
					next === 'center' || next === 'left' || next === 'right'
						? next
						: fallback;
				onChange( elementContentAlignVars( prefix, align ) );
			} }
		/>
	);
}

function ContainerAlignField( { field, value = '', onChange } ) {
	const fallback = field?.default && CONTAINER_ALIGN_VARS[ field.default ]
		? field.default
		: 'center';
	const current = CONTAINER_ALIGN_VARS[ value ] ? value : fallback;

	return (
		<SearchableSelect
			label={ field.label }
			help={ field.help }
			className="bm-style-field bm-style-field--container-align"
			value={ current }
			options={ CONTAINER_ALIGN_OPTIONS }
			onChange={ ( next ) => {
				const align = CONTAINER_ALIGN_VARS[ next ] ? next : fallback;
				onChange( CONTAINER_ALIGN_VARS[ align ] );
			} }
		/>
	);
}

function ContentMaxWidthField( { field, value = '', onChange } ) {
	const width = value || '48rem';
	const isPreset = CONTENT_MAX_WIDTH_OPTIONS.some( ( o ) => o.value === width );
	const preset = isPreset ? width : 'custom';
	const options = [
		...CONTENT_MAX_WIDTH_OPTIONS,
		{ label: __( 'Custom', 'joc-blockora-morph' ), value: 'custom' },
	];

	/** Convert a named preset into a non-preset seed so the unit field can show. */
	const seedCustomValue = ( current ) => {
		if ( current === 'none' || ! current ) {
			return '1100px';
		}
		const match = String( current ).match( /^([\d.]+)(rem)$/i );
		if ( match ) {
			return `${ Math.round( parseFloat( match[ 1 ] ) * 16 ) }px`;
		}
		return current;
	};

	return (
		<div className="bm-style-field bm-style-field--content-max-width">
			<SearchableSelect
				label={ field.label }
				help={ field.help }
				value={ preset }
				options={ options }
				onChange={ ( next ) => {
					if ( ! next ) {
						return;
					}
					if ( next === 'custom' ) {
						onChange( isPreset ? seedCustomValue( width ) : width );
						return;
					}
					onChange( next );
				} }
				searchableThreshold={ 99 }
			/>
			{ preset === 'custom' ? (
				<FlexibleUnitControl
					label={ __( 'Custom max width', 'joc-blockora-morph' ) }
					className="bm-style-field bm-style-field--content-max-width-unit"
					value={ width === 'none' ? '' : width }
					onChange={ ( next ) => onChange( next || 'none' ) }
					units={ CONTENT_MAX_WIDTH_UNITS }
				/>
			) : null }
		</div>
	);
}

function ColumnsBreakpointField( { field, value = '', onChange } ) {
	const width = value || COLUMNS_BREAKPOINT_TABLET;
	const isPreset = COLUMNS_BREAKPOINT_OPTIONS.some( ( o ) => o.value === width );
	const preset = isPreset ? width : 'custom';
	const options = [
		...COLUMNS_BREAKPOINT_OPTIONS,
		{ label: __( 'Custom', 'joc-blockora-morph' ), value: 'custom' },
	];

	return (
		<div className="bm-style-field bm-style-field--columns-breakpoint">
			<SearchableSelect
				label={ field.label }
				help={ field.help }
				value={ preset }
				options={ options }
				onChange={ ( next ) => {
					if ( ! next ) {
						return;
					}
					if ( next === 'custom' ) {
						onChange(
							isPreset
								? width === COLUMNS_BREAKPOINT_MOBILE
									? '600px'
									: '900px'
								: width
						);
						return;
					}
					onChange( next );
				} }
				searchableThreshold={ 99 }
			/>
			{ preset === 'custom' ? (
				<FlexibleUnitControl
					label={ __( 'Custom screen width', 'joc-blockora-morph' ) }
					className="bm-style-field bm-style-field--columns-breakpoint-unit"
					value={ width }
					onChange={ ( next ) =>
						onChange( next || COLUMNS_BREAKPOINT_TABLET )
					}
					units={ COLUMNS_BREAKPOINT_UNITS }
				/>
			) : null }
		</div>
	);
}

function CardColumnsField( { field, value = '', onChange } ) {
	const raw = String( value || '' ).trim();
	const current = CARD_COLUMNS_OPTIONS.some( ( o ) => o.value === raw )
		? raw
		: field?.default || CARD_COLUMNS_DEFAULT;

	return (
		<div className="bm-style-field bm-style-field--card-columns">
			<SearchableSelect
				label={ field.label }
				help={ field.help }
				value={ current }
				options={ CARD_COLUMNS_OPTIONS }
				onChange={ ( next ) => {
					if ( ! next ) {
						return;
					}
					onChange( next );
				} }
				searchableThreshold={ 99 }
			/>
		</div>
	);
}

function MinHeightField( { field, value = '', onChange } ) {
	const height = value || MIN_HEIGHT_NONE;
	const isPreset = MIN_HEIGHT_OPTIONS.some( ( o ) => o.value === height );
	const preset = isPreset ? height : 'custom';
	const options = [
		...MIN_HEIGHT_OPTIONS,
		{ label: __( 'Custom', 'joc-blockora-morph' ), value: 'custom' },
	];

	/** Convert a named preset into a non-preset seed so the unit field can show. */
	const seedCustomValue = ( current ) => {
		if ( current === MIN_HEIGHT_NONE || ! current ) {
			return '400px';
		}
		if ( current === '100vh' ) {
			return '100vh';
		}
		const rem = String( current ).match( /^([\d.]+)(rem)$/i );
		if ( rem ) {
			return `${ Math.round( parseFloat( rem[ 1 ] ) * 16 ) }px`;
		}
		return current;
	};

	return (
		<div className="bm-style-field bm-style-field--min-height">
			<SearchableSelect
				label={ field.label }
				help={ field.help }
				value={ preset }
				options={ options }
				onChange={ ( next ) => {
					if ( ! next ) {
						return;
					}
					if ( next === 'custom' ) {
						onChange( isPreset ? seedCustomValue( height ) : height );
						return;
					}
					onChange( next );
				} }
				searchableThreshold={ 99 }
			/>
			{ preset === 'custom' ? (
				<FlexibleUnitControl
					label={ __( 'Custom min height', 'joc-blockora-morph' ) }
					className="bm-style-field bm-style-field--min-height-unit"
					value={ height === MIN_HEIGHT_NONE ? '' : height }
					onChange={ ( next ) => onChange( next || MIN_HEIGHT_NONE ) }
					units={ MIN_HEIGHT_UNITS }
				/>
			) : null }
		</div>
	);
}

function ColumnsStackOrderField( { field, value = '', onChange } ) {
	const checked =
		value === 'reverse' ||
		value === 'image-last' ||
		value === '1' ||
		value === 'true';

	return (
		<ToggleControl
			label={ field.label }
			help={ field.help }
			className="bm-style-field bm-style-field--columns-stack-order"
			checked={ checked }
			onChange={ ( next ) => onChange( next ? 'reverse' : 'default' ) }
			__nextHasNoMarginBottom
		/>
	);
}

function MediaFrameField( { field, value = '', onChange } ) {
	const checked =
		value === 'fullscreen' ||
		value === 'full' ||
		value === '1' ||
		value === 'true';

	return (
		<ToggleControl
			label={ field.label }
			help={ field.help }
			className="bm-style-field bm-style-field--media-frame"
			checked={ checked }
			onChange={ ( next ) => onChange( next ? 'fullscreen' : 'framed' ) }
			__nextHasNoMarginBottom
		/>
	);
}

function CssFlagField( { field, value = '', onChange } ) {
	const checked =
		value === '1' ||
		value === 'on' ||
		value === 'true' ||
		value === 'yes' ||
		value === true;
	const varsOn =
		field?.varsOn && typeof field.varsOn === 'object' ? field.varsOn : null;
	const varsOff =
		field?.varsOff && typeof field.varsOff === 'object'
			? field.varsOff
			: null;

	return (
		<ToggleControl
			label={ field.label }
			help={ field.help }
			className="bm-style-field bm-style-field--css-flag"
			checked={ checked }
			onChange={ ( next ) => {
				if ( varsOn || varsOff ) {
					const key = String( field?.key || '' );
					const patch = next
						? { ...( varsOn || {} ), ...( key ? { [ key ]: '1' } : {} ) }
						: {
								...( varsOff || {} ),
								...( key ? { [ key ]: '0' } : {} ),
						  };
					onChange( patch );
					return;
				}
				onChange( next ? '1' : '0' );
			} }
			__nextHasNoMarginBottom
		/>
	);
}

/**
 * Derive top/right/bottom/left CSS var keys from a boxSpacing field.
 *
 * @param {object} field
 * @return {{ top: string, right: string, bottom: string, left: string }}
 */
function boxSpacingSideKeys( field ) {
	const fromY = String( field?.verticalKey || '' ).replace( /-y$/i, '' );
	const rawKey = String( field?.key || '' );
	const root =
		rawKey && ! /-(?:y|x|top|right|bottom|left)$/i.test( rawKey )
			? rawKey
			: fromY;
	return {
		top: `${ root }-top`,
		right: `${ root }-right`,
		bottom: `${ root }-bottom`,
		left: `${ root }-left`,
	};
}

/**
 * 4-side container spacing (padding / margin) with intelligent link states.
 */
function BoxSpacingField( { field, values = {}, onChange } ) {
	const verticalKey = field.verticalKey;
	const horizontalKey = field.horizontalKey;
	const sideKeys = boxSpacingSideKeys( field );
	const modeKey = `${ String( sideKeys.top ).replace( /-top$/i, '' ) }-link-mode`;

	const sides = [
		{ key: sideKeys.top, label: __( 'Top', 'joc-blockora-morph' ) },
		{ key: sideKeys.right, label: __( 'Right', 'joc-blockora-morph' ) },
		{ key: sideKeys.bottom, label: __( 'Bottom', 'joc-blockora-morph' ) },
		{ key: sideKeys.left, label: __( 'Left', 'joc-blockora-morph' ) },
	];

	const read = ( key ) => String( values[ key ] ?? '' ).trim();
	const hasOwn = ( key ) =>
		values && Object.prototype.hasOwnProperty.call( values, key );
	const yFallback = read( verticalKey );
	const xFallback = read( horizontalKey );

	// Prefer explicit side keys. Only fall back to Y/X when a side was never stored
	// (legacy blocks), so empty sides stay empty after unlinking.
	const sideValues = {
		[ sideKeys.top ]: hasOwn( sideKeys.top )
			? read( sideKeys.top )
			: yFallback,
		[ sideKeys.right ]: hasOwn( sideKeys.right )
			? read( sideKeys.right )
			: xFallback,
		[ sideKeys.bottom ]: hasOwn( sideKeys.bottom )
			? read( sideKeys.bottom )
			: yFallback,
		[ sideKeys.left ]: hasOwn( sideKeys.left )
			? read( sideKeys.left )
			: xFallback,
		[ modeKey ]: read( modeKey ),
	};

	const handleChange = ( patch ) => {
		const next = { ...sideValues, ...patch };
		const top = String( next[ sideKeys.top ] ?? '' ).trim();
		const right = String( next[ sideKeys.right ] ?? '' ).trim();
		const bottom = String( next[ sideKeys.bottom ] ?? '' ).trim();
		const left = String( next[ sideKeys.left ] ?? '' ).trim();
		const out = {
			[ sideKeys.top ]: top,
			[ sideKeys.right ]: right,
			[ sideKeys.bottom ]: bottom,
			[ sideKeys.left ]: left,
			// Keep axial keys for older markup / CSS fallbacks.
			[ verticalKey ]: top || bottom || '',
			[ horizontalKey ]: left || right || '',
		};
		if ( Object.prototype.hasOwnProperty.call( patch || {}, modeKey ) ) {
			out[ modeKey ] = String( patch[ modeKey ] ?? '' ).trim();
		} else if ( read( modeKey ) ) {
			out[ modeKey ] = read( modeKey );
		}
		onChange( out );
	};

	const kind = field.label || __( 'Padding', 'joc-blockora-morph' );

	return (
		<div className="bm-style-field bm-style-field--box-spacing">
			<IntelligentSpacing
				kind={ kind }
				modeKey={ modeKey }
				axialVerticalLabel={ __( 'Top / Bottom', 'joc-blockora-morph' ) }
				axialHorizontalLabel={ __( 'Left / Right', 'joc-blockora-morph' ) }
				sides={ sides }
				values={ sideValues }
				onChange={ handleChange }
			/>
		</div>
	);
}

function ColorField( {
	field,
	value = '',
	onChange,
	id,
	colorPalettes,
	enableGradient = false,
	solidDefault,
	gradientDefault,
} ) {
	const label = value || __( 'None', 'joc-blockora-morph' );

	return (
		<BaseControl
			id={ id }
			label={ field.label }
			className={ `bm-style-field bm-style-field--color${
				enableGradient ? ' bm-style-field--fill' : ''
			}` }
		>
			<ColorDropdown
				value={ value }
				onChange={ onChange }
				colorPalettes={ colorPalettes }
				label={ label }
				enableGradient={ enableGradient }
				solidDefault={ solidDefault }
				gradientDefault={ gradientDefault }
			/>
		</BaseControl>
	);
}

function ColorDropdown( {
	value = '',
	onChange,
	colorPalettes,
	label,
	enableGradient = false,
	solidDefault = DEFAULT_FILL_SOLID,
	gradientDefault = DEFAULT_FILL_GRADIENT,
} ) {
	const [ open, setOpen ] = useState( false );
	const close = () => setOpen( false );
	const hostRef = useColorDropdownOutsideClose( open, close );
	const display = label || value || __( 'None', 'joc-blockora-morph' );
	const gradient = isGradientValue( value );

	return (
		<div ref={ hostRef } className="bm-color-dropdown-host">
			<Dropdown
				open={ open }
				onToggle={ setOpen }
				onClose={ close }
				className="bm-color-dropdown"
				contentClassName="bm-color-dropdown__content"
				popoverProps={ colorDropdownPopoverProps( close ) }
				renderToggle={ ( { isOpen, onToggle } ) => (
					<Button
						className="bm-color-dropdown__toggle"
						onClick={ onToggle }
						aria-expanded={ isOpen }
						aria-label={ __( 'Select color', 'joc-blockora-morph' ) }
						variant="secondary"
						__next40pxDefaultSize
					>
						{ gradient ? (
							<span
								className="bm-color-dropdown__indicator bm-color-dropdown__indicator--gradient"
								style={ { background: value } }
								aria-hidden="true"
							/>
						) : (
							<ColorIndicator
								className="bm-color-dropdown__indicator"
								colorValue={ value || undefined }
							/>
						) }
						<span className="bm-color-dropdown__label">
							{ display }
						</span>
					</Button>
				) }
				renderContent={ () => (
					<DropdownContentWrapper paddingSize="medium">
						<div className="bm-color-dropdown__panel">
							<FillColorPalette
								palettes={ colorPalettes }
								value={ value || '' }
								onChange={ ( next ) => onChange( next ?? '' ) }
								enableGradient={ enableGradient }
								solidDefault={ solidDefault }
								gradientDefault={ gradientDefault }
								clearable
								enableAlpha
							/>
						</div>
					</DropdownContentWrapper>
				) }
			/>
		</div>
	);
}

function ImageBackgroundField( { field, value = '', onChange } ) {
	const raw = String( value || '' ).trim();
	const url = raw
		.replace( /^url\(["']?|["']?\)$/g, '' )
		.replace( /^none$/i, '' );

	return (
		<BaseControl
			label={ field.label }
			className="bm-style-field bm-style-field--image-bg"
		>
			<BmMediaUpload
				onSelect={ ( attachment ) => {
					if ( attachment?.url ) {
						onChange( `url("${ attachment.url }")` );
					}
				} }
				allowedTypes={ [ 'image' ] }
				title={ __( 'Select background image', 'joc-blockora-morph' ) }
				render={ ( { open } ) => (
					<div className="bm-media-field__picker">
						{ url ? (
							<button
								type="button"
								className="bm-media-field__thumb"
								onClick={ open }
								aria-label={ __(
									'Replace background image',
									'joc-blockora-morph'
								) }
							>
								<img src={ url } alt="" />
							</button>
						) : (
							<button
								type="button"
								className="bm-media-field__thumb bm-media-field__thumb--empty"
								onClick={ open }
								aria-label={ __(
									'Select background image',
									'joc-blockora-morph'
								) }
							>
								<span className="dashicons dashicons-format-image" aria-hidden="true" />
							</button>
						) }
						<div className="bm-media-field__side">
							<span className="bm-media-field__side-label">
								{ url
									? __( 'Image', 'joc-blockora-morph' )
									: __( 'No selected image', 'joc-blockora-morph' ) }
							</span>
							<div className="bm-media-field__actions">
								<Button variant="secondary" onClick={ open }>
									{ url
										? __( 'Replace image', 'joc-blockora-morph' )
										: __( 'Select image', 'joc-blockora-morph' ) }
								</Button>
								{ url ? (
									<Button
										variant="tertiary"
										isDestructive
										onClick={ () => onChange( 'none' ) }
									>
										{ __( 'Remove', 'joc-blockora-morph' ) }
									</Button>
								) : null }
							</div>
						</div>
					</div>
				) }
			/>
		</BaseControl>
	);
}

export default function StyleField( {
	field,
	value = '',
	values = {},
	onChange,
	onVarsPatch,
} ) {
	const { colorPalettes, fontSizes, fontFamilyOptions } = useThemeTokens();
	const type = resolveStyleFieldType( field );
	const id = `bm-style-${ String( field.key || field.label || 'field' ).replace( /[^a-z0-9_-]/gi, '-' ) }`;

	if ( type === 'color' ) {
		if ( field.key === '--bm-bg' && styleFieldAllowsGradient( field, type ) ) {
			const gradientRaw = String( values[ '--bm-bg-gradient' ] || '' ).trim();
			const gradientLast = String(
				values[ '--bm-bg-gradient-last' ] || ''
			).trim();
			const gradientActive =
				gradientRaw &&
				gradientRaw.toLowerCase() !== 'none' &&
				isGradientValue( gradientRaw );
			const rememberedGradient = isGradientValue( gradientLast )
				? gradientLast
				: isGradientValue( gradientRaw )
				? gradientRaw
				: DEFAULT_FILL_GRADIENT;
			const fillValue = gradientActive ? gradientRaw : value;
			const commitBgFill = ( next ) => {
				if ( typeof onVarsPatch === 'function' ) {
					if ( isGradientValue( next ) ) {
						const gradient = next || rememberedGradient;
						onVarsPatch( {
							'--bm-bg-gradient': gradient,
							'--bm-bg-gradient-last': gradient,
						} );
						return;
					}
					// Solid mode: keep last gradient stops for when the user returns.
					// Allow fully transparent colors (alpha 0) — only fall back when cleared.
					const solid = isAbsentColor( next )
						? DEFAULT_FILL_SOLID
						: next;
					onVarsPatch( {
						'--bm-bg': solid,
						'--bm-bg-gradient': 'none',
						...( isGradientValue( rememberedGradient )
							? {
									'--bm-bg-gradient-last':
										rememberedGradient,
							  }
							: {} ),
					} );
					return;
				}
				onChange( next );
			};
			return (
				<ColorField
					field={ field }
					value={ fillValue }
					onChange={ commitBgFill }
					id={ id }
					colorPalettes={ colorPalettes }
					enableGradient
					solidDefault={ DEFAULT_FILL_SOLID }
					gradientDefault={ rememberedGradient }
				/>
			);
		}
		const allowGradient = styleFieldAllowsGradient( field, type );
		return (
			<ColorField
				field={ field }
				value={ value }
				onChange={ onChange }
				id={ id }
				colorPalettes={ colorPalettes }
				enableGradient={ allowGradient }
				solidDefault={ DEFAULT_FILL_SOLID }
				gradientDefault={ DEFAULT_FILL_GRADIENT }
			/>
		);
	}

	if ( type === 'opacity' ) {
		// Legacy color-related opacity fields are migrated into color alpha.
		return null;
	}

	if ( type === 'overlay' || type === 'fill' ) {
		const isOverlay =
			type === 'overlay' ||
			String( field.key || '' ).includes( 'overlay' );
		const allowGradient = styleFieldAllowsGradient( field, type );
		return (
			<ColorField
				field={ field }
				value={ value }
				onChange={ onChange }
				id={ id }
				colorPalettes={ colorPalettes }
				enableGradient={ allowGradient }
				solidDefault={
					isOverlay ? DEFAULT_OVERLAY_SOLID : DEFAULT_FILL_SOLID
				}
				gradientDefault={
					isOverlay ? DEFAULT_OVERLAY_GRADIENT : DEFAULT_FILL_GRADIENT
				}
			/>
		);
	}

	if ( type === 'imageBackground' ) {
		return (
			<ImageBackgroundField
				field={ field }
				value={ value }
				onChange={ onChange }
			/>
		);
	}

	if ( type === 'fontFamily' ) {
		return (
			<SearchableSelect
				label={ field.label }
				className="bm-style-field bm-style-field--font-family"
				value={ value || DEFAULT_BLOCK_FONT_FAMILY }
				options={ fontFamilyOptions }
				onChange={ ( next ) =>
					onChange( next || DEFAULT_BLOCK_FONT_FAMILY )
				}
				help={ __(
					'Inter is the default Blockora Morph font. Theme fonts are included automatically.',
					'joc-blockora-morph'
				) }
				searchableThreshold={ 1 }
				previewFontFamily
			/>
		);
	}

	if ( type === 'fontSize' ) {
		return (
			<FontSizeField
				label={ field.label }
				help={ field.help }
				className="bm-style-field bm-style-field--font-size"
				fontSizes={ fontSizes }
				value={ value || '' }
				onChange={ ( next ) => onChange( next || '' ) }
			/>
		);
	}

	if ( type === 'fontWeight' ) {
		return (
			<SearchableSelect
				label={ field.label }
				className="bm-style-field bm-style-field--font-weight"
				value={ value || '' }
				options={ FONT_WEIGHT_OPTIONS }
				onChange={ ( next ) => onChange( next || '' ) }
				searchableThreshold={ 8 }
			/>
		);
	}

	if ( type === 'boxSpacing' ) {
		return (
			<BoxSpacingField
				field={ field }
				values={ values || {} }
				onChange={ onChange }
			/>
		);
	}

	if ( type === 'boxBorder' ) {
		return (
			<BoxBorderField
				field={ field }
				values={ values || {} }
				onChange={ onChange }
			/>
		);
	}

	if ( type === 'boxRadius' ) {
		return (
			<BoxRadiusField
				field={ field }
				values={ values || {} }
				onChange={ onChange }
			/>
		);
	}

	if ( type === 'boxShadow' ) {
		const shadowKey = String( field.key || '' );
		const presetKey = field.presetKey || `${ shadowKey }-preset`;
		const colorKey = field.colorKey || `${ shadowKey }-color`;
		const map = values || {};
		return (
			<ShadowFields
				label={ field.label || __( 'Box shadow', 'joc-blockora-morph' ) }
				preset={ map[ presetKey ] || '' }
				color={ map[ colorKey ] || '' }
				css={ map[ shadowKey ] || 'none' }
				colorPalettes={ colorPalettes }
				onChange={ ( next ) => {
					const patch = {
						[ shadowKey ]: next.css,
						[ presetKey ]: next.preset,
						[ colorKey ]: next.color,
					};
					if ( typeof onVarsPatch === 'function' ) {
						onVarsPatch( patch );
						return;
					}
					if ( typeof onChange === 'function' ) {
						onChange( patch );
					}
				} }
			/>
		);
	}

	if ( type === 'contentAlign' ) {
		return (
			<ContentAlignField
				field={ field }
				value={ value }
				onChange={ onChange }
			/>
		);
	}

	if ( type === 'elementContentAlign' ) {
		return (
			<ElementContentAlignField
				field={ field }
				value={ value }
				onChange={ onChange }
			/>
		);
	}

	if ( type === 'containerAlign' ) {
		return (
			<ContainerAlignField
				field={ field }
				value={ value }
				onChange={ onChange }
			/>
		);
	}

	if ( type === 'contentMaxWidth' ) {
		return (
			<ContentMaxWidthField
				field={ field }
				value={ value }
				onChange={ onChange }
			/>
		);
	}

	if ( type === 'minHeight' ) {
		return (
			<MinHeightField
				field={ field }
				value={ value }
				onChange={ onChange }
			/>
		);
	}

	if ( type === 'columnsBreakpoint' ) {
		return (
			<ColumnsBreakpointField
				field={ field }
				value={ value }
				onChange={ onChange }
			/>
		);
	}

	if ( type === 'cardColumns' ) {
		return (
			<CardColumnsField
				field={ field }
				value={ value }
				onChange={ onChange }
			/>
		);
	}

	if ( type === 'columnsStackOrder' ) {
		return (
			<ColumnsStackOrderField
				field={ field }
				value={ value }
				onChange={ onChange }
			/>
		);
	}

	if ( type === 'mediaFrame' ) {
		return (
			<MediaFrameField
				field={ field }
				value={ value }
				onChange={ onChange }
			/>
		);
	}

	if ( type === 'cssFlag' ) {
		return (
			<CssFlagField
				field={ field }
				value={ value }
				onChange={ onChange }
			/>
		);
	}

	if ( type === 'select' ) {
		const options = Array.isArray( field.options ) ? field.options : [];
		const fallback = field.default || options[ 0 ]?.value || '';
		const current = options.some( ( option ) => option.value === value )
			? value
			: fallback;
		return (
			<SearchableSelect
				label={ field.label }
				help={ field.help }
				className="bm-style-field bm-style-field--select"
				value={ current }
				options={ options }
				onChange={ ( next ) => onChange( next || fallback ) }
				searchableThreshold={ 99 }
			/>
		);
	}

	if ( type === 'spacing' ) {
		return (
			<SpacingControl
				id={ id }
				label={ field.label }
				value={ value }
				onChange={ onChange }
			/>
		);
	}

	return (
		<TextControl
			label={ field.label }
			className="bm-style-field bm-style-field--text"
			value={ value || '' }
			onChange={ ( next ) => onChange( next || '' ) }
			__nextHasNoMarginBottom
			__next40pxDefaultSize
		/>
	);
}
