/**
 * WordPress-like defaults for ColorPalette / FontSizePicker / font weight.
 */
import { __ } from '@wordpress/i18n';

/**
 * Default block body font (self-hosted Inter under assets/fonts/inter/).
 * Applied via --bm-font-family on library templates and as CSS var fallback.
 */
export const DEFAULT_BLOCK_FONT_FAMILY = '"Inter", sans-serif';

/** JOC Blockora Morph brand palette (matches ThemeConfigController::defaults). */
export const DEFAULT_COLORS = [
	{ name: __( 'Primary', 'joc-blockora-morph' ), slug: 'primary', color: '#3525cd' },
	{
		name: __( 'Primary Container', 'joc-blockora-morph' ),
		slug: 'primary-container',
		color: '#4f46e5',
	},
	{
		name: __( 'Primary Surface', 'joc-blockora-morph' ),
		slug: 'primary-surface',
		color: '#eaedff',
	},
	{ name: __( 'Secondary', 'joc-blockora-morph' ), slug: 'secondary', color: '#4648d4' },
	{
		name: __( 'Secondary Container', 'joc-blockora-morph' ),
		slug: 'secondary-container',
		color: '#6a6be7',
	},
	{
		name: __( 'Secondary Surface', 'joc-blockora-morph' ),
		slug: 'secondary-surface',
		color: '#eef0ff',
	},
	{ name: __( 'Success', 'joc-blockora-morph' ), slug: 'success', color: '#16a34a' },
	{
		name: __( 'Success Container', 'joc-blockora-morph' ),
		slug: 'success-container',
		color: '#4ade80',
	},
	{
		name: __( 'Success Surface', 'joc-blockora-morph' ),
		slug: 'success-surface',
		color: '#dcfce7',
	},
	{ name: __( 'Warning', 'joc-blockora-morph' ), slug: 'warning', color: '#d97706' },
	{
		name: __( 'Warning Container', 'joc-blockora-morph' ),
		slug: 'warning-container',
		color: '#f59e0b',
	},
	{
		name: __( 'Warning Surface', 'joc-blockora-morph' ),
		slug: 'warning-surface',
		color: '#fef3c7',
	},
	{ name: __( 'Error', 'joc-blockora-morph' ), slug: 'error', color: '#ba1a1a' },
	{
		name: __( 'Error Container', 'joc-blockora-morph' ),
		slug: 'error-container',
		color: '#dc2626',
	},
	{
		name: __( 'Error Surface', 'joc-blockora-morph' ),
		slug: 'error-surface',
		color: '#fee2e2',
	},
	{ name: __( 'On Primary', 'joc-blockora-morph' ), slug: 'on-primary', color: '#ffffff' },
	{ name: __( 'Surface', 'joc-blockora-morph' ), slug: 'surface', color: '#faf8ff' },
	{
		name: __( 'Surface Container', 'joc-blockora-morph' ),
		slug: 'surface-container',
		color: '#eaedff',
	},
	{ name: __( 'On Surface', 'joc-blockora-morph' ), slug: 'on-surface', color: '#131b2e' },
	{
		name: __( 'On Surface Variant', 'joc-blockora-morph' ),
		slug: 'on-surface-variant',
		color: '#464555',
	},
	{ name: __( 'Outline', 'joc-blockora-morph' ), slug: 'outline', color: '#777587' },
];

/** Default Gutenberg font-size presets (rem values stored in CSS vars). */
export const DEFAULT_FONT_SIZES = [
	{
		name: __( 'X-Small', 'joc-blockora-morph' ),
		slug: 'x-small',
		size: '0.75rem',
		lineHeight: '1.4',
	},
	{
		name: __( 'Small', 'joc-blockora-morph' ),
		slug: 'small',
		size: '0.875rem',
		lineHeight: '1.4',
	},
	{
		name: __( 'Medium', 'joc-blockora-morph' ),
		slug: 'medium',
		size: '1rem',
		lineHeight: '1.5',
	},
	{
		name: __( 'Large', 'joc-blockora-morph' ),
		slug: 'large',
		size: '1.5rem',
		lineHeight: '1.3',
	},
	{
		name: __( 'X-Large', 'joc-blockora-morph' ),
		slug: 'x-large',
		size: '2.25rem',
		lineHeight: '1.2',
	},
	{
		name: __( 'XX-Large', 'joc-blockora-morph' ),
		slug: 'xx-large',
		size: '3rem',
		lineHeight: '1.15',
	},
	{
		name: __( 'XXX-Large', 'joc-blockora-morph' ),
		slug: 'xxx-large',
		size: '3.75rem',
		lineHeight: '1.1',
	},
];

/** Built-in font-size slugs that cannot be edited or removed in Global Settings. */
export const LOCKED_FONT_SIZE_SLUGS = [
	'x-small',
	'small',
	'medium',
	'large',
	'x-large',
	'xx-large',
	'xxx-large',
];

/**
 * @param {{ slug?: string }|null|undefined} size
 * @return {boolean}
 */
export function isLockedFontSize( size ) {
	const slug = String( size?.slug || '' ).toLowerCase();
	return LOCKED_FONT_SIZE_SLUGS.includes( slug );
}

/**
 * Built-in Inter cannot be edited or removed in Global Settings.
 *
 * @param {{ slug?: string, family?: string }|null|undefined} font
 * @return {boolean}
 */
export function isLockedFont( font ) {
	const slug = String( font?.slug || '' ).toLowerCase();
	const family = String( font?.family || '' ).trim().toLowerCase();
	return slug === 'inter' || family === 'inter';
}

export const FONT_WEIGHT_OPTIONS = [
	{ label: __( 'Default', 'joc-blockora-morph' ), value: '' },
	{ label: __( 'Thin (100)', 'joc-blockora-morph' ), value: '100' },
	{ label: __( 'Extra Light (200)', 'joc-blockora-morph' ), value: '200' },
	{ label: __( 'Light (300)', 'joc-blockora-morph' ), value: '300' },
	{ label: __( 'Regular (400)', 'joc-blockora-morph' ), value: '400' },
	{ label: __( 'Medium (500)', 'joc-blockora-morph' ), value: '500' },
	{ label: __( 'Semi Bold (600)', 'joc-blockora-morph' ), value: '600' },
	{ label: __( 'Bold (700)', 'joc-blockora-morph' ), value: '700' },
	{ label: __( 'Extra Bold (800)', 'joc-blockora-morph' ), value: '800' },
	{ label: __( 'Black (900)', 'joc-blockora-morph' ), value: '900' },
];

/** Inline format-toolbar weights (headings — named 300–900). */
export const INLINE_FONT_WEIGHT_OPTIONS = FONT_WEIGHT_OPTIONS.filter( ( opt ) =>
	[ '300', '400', '500', '600', '700', '800', '900' ].includes( opt.value )
);

/** Units used by Gutenberg spacing / dimension controls. */
export const SPACING_UNITS = [
	{ value: 'px', label: 'px', default: 16 },
	{ value: 'em', label: 'em', default: 1 },
	{ value: 'rem', label: 'rem', default: 1 },
	{ value: '%', label: '%', default: 10 },
	{ value: 'vw', label: 'vw', default: 5 },
];

/** Theme.json-style spacing size presets. */
export const SPACING_PRESETS = [
	{ name: __( 'None', 'joc-blockora-morph' ), slug: 'none', size: '0' },
	{ name: __( '2X-Small', 'joc-blockora-morph' ), slug: '2-x-small', size: '0.25rem' },
	{ name: __( 'X-Small', 'joc-blockora-morph' ), slug: 'x-small', size: '0.5rem' },
	{ name: __( 'Small', 'joc-blockora-morph' ), slug: 'small', size: '1rem' },
	{ name: __( 'Medium', 'joc-blockora-morph' ), slug: 'medium', size: '1.5rem' },
	{ name: __( 'Large', 'joc-blockora-morph' ), slug: 'large', size: '2rem' },
	{ name: __( 'X-Large', 'joc-blockora-morph' ), slug: 'x-large', size: '3rem' },
	{ name: __( '2X-Large', 'joc-blockora-morph' ), slug: '2-x-large', size: '4rem' },
	{ name: __( '3X-Large', 'joc-blockora-morph' ), slug: '3-x-large', size: '5rem' },
];

/** Content stack alignment → CSS var patch (text / flex / media margins only). */
export const CONTENT_ALIGN_OPTIONS = [
	{ label: __( 'Center', 'joc-blockora-morph' ), value: 'center' },
	{ label: __( 'Left', 'joc-blockora-morph' ), value: 'left' },
	{ label: __( 'Right', 'joc-blockora-morph' ), value: 'right' },
];

export const CONTENT_ALIGN_VARS = {
	center: {
		'--bm-content-align': 'center',
		'--bm-text-align': 'center',
		'--bm-align-items': 'center',
		'--bm-justify-content': 'center',
		'--bm-media-margin-inline-start': 'auto',
		'--bm-media-margin-inline-end': 'auto',
	},
	left: {
		'--bm-content-align': 'left',
		'--bm-text-align': 'left',
		'--bm-align-items': 'flex-start',
		'--bm-justify-content': 'flex-start',
		'--bm-media-margin-inline-start': '0',
		'--bm-media-margin-inline-end': 'auto',
	},
	right: {
		'--bm-content-align': 'right',
		'--bm-text-align': 'right',
		'--bm-align-items': 'flex-end',
		'--bm-justify-content': 'flex-end',
		'--bm-media-margin-inline-start': 'auto',
		'--bm-media-margin-inline-end': '0',
	},
};

/**
 * Per-section content alignment (testimonials quote / text / author).
 * Falls back to Card Surface Content alignment when unset in CSS.
 *
 * @param {string} prefix quote | text | author
 * @param {string} align  left | center | right
 * @return {Record<string, string>}
 */
export function elementContentAlignVars( prefix, align ) {
	const p = String( prefix || '' )
		.trim()
		.replace( /[^a-z0-9-]/gi, '' );
	if ( ! p ) {
		return {};
	}
	const table = {
		center: {
			[ `--bm-${ p }-align` ]: 'center',
			[ `--bm-${ p }-text-align` ]: 'center',
			[ `--bm-${ p }-align-self` ]: 'center',
			[ `--bm-${ p }-justify-content` ]: 'center',
		},
		left: {
			[ `--bm-${ p }-align` ]: 'left',
			[ `--bm-${ p }-text-align` ]: 'left',
			[ `--bm-${ p }-align-self` ]: 'flex-start',
			[ `--bm-${ p }-justify-content` ]: 'flex-start',
		},
		right: {
			[ `--bm-${ p }-align` ]: 'right',
			[ `--bm-${ p }-text-align` ]: 'right',
			[ `--bm-${ p }-align-self` ]: 'flex-end',
			[ `--bm-${ p }-justify-content` ]: 'flex-end',
		},
	};
	return table[ align ] || table.left;
}

/** Avatar placement relative to name/description in testimonial author row. */
export const AUTHOR_IMAGE_ALIGN_OPTIONS = [
	{ label: __( 'Left', 'joc-blockora-morph' ), value: 'left' },
	{ label: __( 'Top', 'joc-blockora-morph' ), value: 'top' },
	{ label: __( 'Bottom', 'joc-blockora-morph' ), value: 'bottom' },
	{ label: __( 'Right', 'joc-blockora-morph' ), value: 'right' },
];

/**
 * @param {string} align left | top | bottom | right
 * @return {Record<string, string>}
 */
export function authorImageAlignVars( align ) {
	// Use `order` (not row-/column-reverse) so Author alignment
	// justify-content still packs toward the left/center/right of the card.
	const table = {
		left: {
			'--bm-author-image-align': 'left',
			'--bm-author-image-flex-direction': 'row',
			'--bm-author-image-align-items': 'center',
			'--bm-author-image-order': '0',
		},
		right: {
			'--bm-author-image-align': 'right',
			'--bm-author-image-flex-direction': 'row',
			'--bm-author-image-align-items': 'center',
			'--bm-author-image-order': '1',
		},
		top: {
			'--bm-author-image-align': 'top',
			'--bm-author-image-flex-direction': 'column',
			'--bm-author-image-align-items':
				'var(--bm-author-align-self, flex-start)',
			'--bm-author-image-order': '0',
		},
		bottom: {
			'--bm-author-image-align': 'bottom',
			'--bm-author-image-flex-direction': 'column',
			'--bm-author-image-align-items':
				'var(--bm-author-align-self, flex-start)',
			'--bm-author-image-order': '1',
		},
	};
	return table[ align ] || table.left;
}

/**
 * Rewrite legacy row-/column-reverse image placement to order-based vars.
 *
 * @param {Record<string, string>} flat
 * @return {Record<string, string>}
 */
export function normalizeAuthorImageAlignVars( flat = {} ) {
	const map = { ...( flat && typeof flat === 'object' ? flat : {} ) };
	const dir = String( map[ '--bm-author-image-flex-direction' ] ?? '' ).trim();
	const align = String( map[ '--bm-author-image-align' ] ?? '' ).trim();
	const order = String( map[ '--bm-author-image-order' ] ?? '' ).trim();

	// Legacy reverse axes flipped the whole author group via justify-content.
	if ( dir === 'row-reverse' ) {
		Object.assign( map, authorImageAlignVars( 'right' ) );
		return map;
	}
	if ( dir === 'column-reverse' ) {
		Object.assign( map, authorImageAlignVars( 'bottom' ) );
		return map;
	}
	if (
		( align === 'left' ||
			align === 'top' ||
			align === 'right' ||
			align === 'bottom' ) &&
		order === ''
	) {
		Object.assign( map, authorImageAlignVars( align ) );
	}
	return map;
}

/** Content container (.bm-hero__inner / .bm-hero__grid) horizontal placement via margin. */
export const CONTAINER_ALIGN_OPTIONS = [
	{ label: __( 'Center', 'joc-blockora-morph' ), value: 'center' },
	{ label: __( 'Left', 'joc-blockora-morph' ), value: 'left' },
	{ label: __( 'Right', 'joc-blockora-morph' ), value: 'right' },
];

export const CONTAINER_ALIGN_VARS = {
	center: {
		'--bm-container-align': 'center',
		'--bm-inner-margin': '0 auto',
	},
	left: {
		'--bm-container-align': 'left',
		'--bm-inner-margin': '0',
	},
	right: {
		'--bm-container-align': 'right',
		'--bm-inner-margin': '0 0 0 auto',
	},
};

/**
 * Fill media inline margins from content alignment when missing.
 * Narrow images need these (hit-wrap is full-row; img margins alone do not center).
 *
 * @param {Record<string, string>} flat
 * @return {Record<string, string>}
 */
export function backfillContentAlignMediaMargins( flat = {} ) {
	const map = { ...( flat && typeof flat === 'object' ? flat : {} ) };
	const hasStart = String( map[ '--bm-media-margin-inline-start' ] ?? '' ).trim();
	const hasEnd = String( map[ '--bm-media-margin-inline-end' ] ?? '' ).trim();
	if ( hasStart && hasEnd ) {
		return map;
	}

	let align = String( map[ '--bm-content-align' ] ?? '' ).trim();
	if ( ! CONTENT_ALIGN_VARS[ align ] ) {
		const items = String( map[ '--bm-align-items' ] ?? '' ).trim();
		if ( items === 'center' ) {
			align = 'center';
		} else if ( items === 'flex-end' ) {
			align = 'right';
		} else if ( items === 'flex-start' ) {
			align = 'left';
		} else {
			// Sparse breakpoint maps must not invent center margins — that would
			// override parent alignment when merged in effective().
			return map;
		}
	}

	const patch = CONTENT_ALIGN_VARS[ align ] || CONTENT_ALIGN_VARS.center;
	if ( ! hasStart ) {
		map[ '--bm-media-margin-inline-start' ] =
			patch[ '--bm-media-margin-inline-start' ];
	}
	if ( ! hasEnd ) {
		map[ '--bm-media-margin-inline-end' ] =
			patch[ '--bm-media-margin-inline-end' ];
	}
	return map;
}

/**
 * Whether a style-schema field should show for the active breakpoint tab.
 *
 * @param {object} field
 * @param {string} breakpoint desktop|tablet|mobile
 * @return {boolean}
 */
export function isStyleFieldVisibleAtBreakpoint( field, breakpoint = 'desktop' ) {
	if ( ! field ) {
		return false;
	}
	const bp = breakpoint || 'desktop';
	// Shared (global) layout settings are editable from any tab.
	if ( field.shared ) {
		return true;
	}
	if ( Array.isArray( field.visibleBreakpoints ) && field.visibleBreakpoints.length ) {
		return field.visibleBreakpoints.includes( bp );
	}
	if ( field.desktopOnly ) {
		return bp === 'desktop';
	}
	return true;
}

/**
 * Whether edits to this field always store on desktop (one value for all viewports).
 *
 * @param {object} field
 * @return {boolean}
 */
export function isStyleFieldShared( field ) {
	return Boolean( field?.shared );
}

/** Content container max-width presets (hero inner / grid). */
export const CONTENT_MAX_WIDTH_OPTIONS = [
	{ label: __( 'Full width', 'joc-blockora-morph' ), value: 'none' },
	{ label: __( 'Wide (72rem)', 'joc-blockora-morph' ), value: '72rem' },
	{ label: __( 'Content (48rem)', 'joc-blockora-morph' ), value: '48rem' },
	{ label: __( 'Narrow (36rem)', 'joc-blockora-morph' ), value: '36rem' },
];

/** Units for custom content max-width. */
export const CONTENT_MAX_WIDTH_UNITS = [
	{ value: 'rem', label: 'rem', default: 48 },
	{ value: 'px', label: 'px', default: 1100 },
	{ value: '%', label: '%', default: 100 },
	{ value: 'vw', label: 'vw', default: 100 },
	{ value: 'ch', label: 'ch', default: 65 },
];

/** Two-column stack breakpoint presets (matches CssVars MEDIA widths). */
export const COLUMNS_BREAKPOINT_TABLET = '1024px';
export const COLUMNS_BREAKPOINT_MOBILE = '480px';

export const COLUMNS_BREAKPOINT_OPTIONS = [
	{
		label: __( 'Tablet (1024px)', 'joc-blockora-morph' ),
		value: COLUMNS_BREAKPOINT_TABLET,
	},
	{
		label: __( 'Mobile (480px)', 'joc-blockora-morph' ),
		value: COLUMNS_BREAKPOINT_MOBILE,
	},
];

export const COLUMNS_BREAKPOINT_UNITS = [
	{ value: 'px', label: 'px', default: 1024 },
];

/** Card grid column count presets (Features Blogs / Features Icon). */
export const CARD_COLUMNS_OPTIONS = [
	{ label: __( '4 columns', 'joc-blockora-morph' ), value: '4' },
	{ label: __( '3 columns', 'joc-blockora-morph' ), value: '3' },
	{ label: __( '2 columns', 'joc-blockora-morph' ), value: '2' },
	{ label: __( '1 column', 'joc-blockora-morph' ), value: '1' },
];

export const CARD_COLUMNS_DEFAULT = '4';

/** Section min-height presets (hero section). */
export const MIN_HEIGHT_NONE = '0';

export const MIN_HEIGHT_OPTIONS = [
	{ label: __( 'None', 'joc-blockora-morph' ), value: MIN_HEIGHT_NONE },
	{ label: __( 'Small (20rem)', 'joc-blockora-morph' ), value: '20rem' },
	{ label: __( 'Medium (28rem)', 'joc-blockora-morph' ), value: '28rem' },
	{ label: __( 'Large (36rem)', 'joc-blockora-morph' ), value: '36rem' },
	{ label: __( 'Full screen (100vh)', 'joc-blockora-morph' ), value: '100vh' },
];

/** Units for custom section min-height. */
export const MIN_HEIGHT_UNITS = [
	{ value: 'px', label: 'px', default: 400 },
	{ value: 'rem', label: 'rem', default: 28 },
	{ value: 'vh', label: 'vh', default: 100 },
	{ value: '%', label: '%', default: 100 },
];

/** Order of image vs text after columns stack (toggle: default | reverse). */
export const COLUMNS_STACK_ORDER_OPTIONS = [
	{ label: __( 'Keep original order', 'joc-blockora-morph' ), value: 'default' },
	{ label: __( 'Reverse order', 'joc-blockora-morph' ), value: 'reverse' },
];

/** Side media frame mode for image-left / image-right heroes. */
export const MEDIA_FRAME_OPTIONS = [
	{ label: __( 'Framed', 'joc-blockora-morph' ), value: 'framed' },
	{ label: __( 'Fullscreen', 'joc-blockora-morph' ), value: 'fullscreen' },
];

/** WordPress-like image width presets (always capped at max-width: 100%). */
export const IMAGE_WIDTH_OPTIONS = [
	{ label: __( '25%', 'joc-blockora-morph' ), value: '25%' },
	{ label: __( '50%', 'joc-blockora-morph' ), value: '50%' },
	{ label: __( '75%', 'joc-blockora-morph' ), value: '75%' },
	{ label: __( '100%', 'joc-blockora-morph' ), value: '100%' },
];

/** CSS object-fit for hero/banner (and other) media images. */
export const IMAGE_OBJECT_FIT_OPTIONS = [
	{ label: __( 'Cover', 'joc-blockora-morph' ), value: 'cover' },
	{ label: __( 'Contain', 'joc-blockora-morph' ), value: 'contain' },
	{ label: __( 'Fill', 'joc-blockora-morph' ), value: 'fill' },
	{ label: __( 'Scale down', 'joc-blockora-morph' ), value: 'scale-down' },
	{ label: __( 'None', 'joc-blockora-morph' ), value: 'none' },
];

/** Shadow style presets — geometry only; color is applied via composeBoxShadow. */
export const SHADOW_STYLE_OPTIONS = [
	{ label: __( 'None', 'joc-blockora-morph' ), value: 'none' },
	{ label: __( 'Natural', 'joc-blockora-morph' ), value: 'natural' },
	{ label: __( 'Deep', 'joc-blockora-morph' ), value: 'deep' },
	{ label: __( 'Sharp', 'joc-blockora-morph' ), value: 'sharp' },
	{ label: __( 'Soft', 'joc-blockora-morph' ), value: 'soft' },
];

export const SHADOW_PRESET_CSS = {
	none: 'none',
	natural: '0 4px 12px {color}',
	deep: '0 12px 28px {color}',
	sharp: '6px 6px 0 {color}',
	soft: '0 2px 8px {color}',
};

export const DEFAULT_SHADOW_COLOR = 'rgba(0, 0, 0, 0.16)';

export const DEFAULT_CUSTOM_SHADOW = { x: 0, y: 4, blur: 12, spread: 0 };

/**
 * Build a box-shadow string from preset + color.
 *
 * @param {string} preset none|natural|deep|sharp|soft|custom
 * @param {string} color  CSS color
 * @param {string} [customCss] Used when preset is custom
 * @return {string}
 */
export function composeBoxShadow( preset, color, customCss = '' ) {
	if ( ! preset || preset === 'none' ) {
		return 'none';
	}
	if ( preset === 'custom' ) {
		return customCss || composeCustomShadow( DEFAULT_CUSTOM_SHADOW, color );
	}
	const tpl = SHADOW_PRESET_CSS[ preset ];
	if ( ! tpl || tpl === 'none' ) {
		return 'none';
	}
	return tpl.split( '{color}' ).join( color || DEFAULT_SHADOW_COLOR );
}

/**
 * Compose a custom box-shadow from offsets + shared shadow color.
 *
 * @param {{ x?: number, y?: number, blur?: number, spread?: number }} offsets
 * @param {string} color
 * @return {string}
 */
export function composeCustomShadow( offsets = {}, color = DEFAULT_SHADOW_COLOR ) {
	const x = Number.isFinite( Number( offsets.x ) )
		? Number( offsets.x )
		: DEFAULT_CUSTOM_SHADOW.x;
	const y = Number.isFinite( Number( offsets.y ) )
		? Number( offsets.y )
		: DEFAULT_CUSTOM_SHADOW.y;
	const blur = Number.isFinite( Number( offsets.blur ) )
		? Math.max( 0, Number( offsets.blur ) )
		: DEFAULT_CUSTOM_SHADOW.blur;
	const spread = Number.isFinite( Number( offsets.spread ) )
		? Number( offsets.spread )
		: DEFAULT_CUSTOM_SHADOW.spread;
	return `${ x }px ${ y }px ${ blur }px ${ spread }px ${ color || DEFAULT_SHADOW_COLOR }`;
}

/**
 * Parse X / Y / blur / spread from a box-shadow CSS string (color is ignored).
 *
 * @param {string} css
 * @return {{ x: number, y: number, blur: number, spread: number }}
 */
export function parseCustomShadow( css ) {
	const value = String( css || '' ).trim();
	if ( ! value || value === 'none' ) {
		return { ...DEFAULT_CUSTOM_SHADOW };
	}
	const stripped = value
		.replace( /\binset\b/gi, ' ' )
		.replace( /rgba?\([^)]*\)/gi, ' ' )
		.replace( /hsla?\([^)]*\)/gi, ' ' )
		.replace( /#[0-9a-f]{3,8}\b/gi, ' ' )
		.replace( /\b(?:var|calc)\([^)]*\)/gi, ' ' );

	const lengths = [];
	const re = /-?[\d.]+/g;
	let match;
	while ( ( match = re.exec( stripped ) ) && lengths.length < 4 ) {
		lengths.push( parseFloat( match[ 0 ] ) );
	}

	return {
		x: Number.isFinite( lengths[ 0 ] ) ? lengths[ 0 ] : DEFAULT_CUSTOM_SHADOW.x,
		y: Number.isFinite( lengths[ 1 ] ) ? lengths[ 1 ] : DEFAULT_CUSTOM_SHADOW.y,
		blur: Number.isFinite( lengths[ 2 ] )
			? Math.max( 0, lengths[ 2 ] )
			: DEFAULT_CUSTOM_SHADOW.blur,
		spread: Number.isFinite( lengths[ 3 ] )
			? lengths[ 3 ]
			: DEFAULT_CUSTOM_SHADOW.spread,
	};
}

/**
 * Guess preset key from a stored box-shadow CSS value.
 *
 * @param {string} css
 * @return {string} none|natural|deep|sharp|soft|custom
 */
export function inferShadowPreset( css ) {
	const value = String( css || '' ).trim();
	if ( ! value || value === 'none' ) {
		return 'none';
	}
	const normalized = value.replace( /\s+/g, ' ' );
	const keys = Object.keys( SHADOW_PRESET_CSS ).filter( ( k ) => k !== 'none' );
	for ( let i = 0; i < keys.length; i++ ) {
		const key = keys[ i ];
		const tpl = SHADOW_PRESET_CSS[ key ];
		const pattern = tpl
			.split( '{color}' )
			.map( ( part ) =>
				part.replace( /[.*+?^${}()|[\]\\]/g, '\\$&' ).replace( /\s+/g, '\\s+' )
			)
			.join( '.+?' );
		if ( new RegExp( `^${ pattern }$`, 'i' ).test( normalized ) ) {
			return key;
		}
	}
	return 'custom';
}

/**
 * Infer control type from schema type or CSS var key.
 *
 * Background fills may set `enableGradient: true` to show Solid | Gradient
 * tabs above Theme | Default. Use `enableGradient: false` to force solid-only
 * even for `--bm-bg` / overlay / fill fields.
 */
export function resolveStyleFieldType( field ) {
	const type = field?.type || 'text';
	if (
		type === 'color' ||
		type === 'fontSize' ||
		type === 'fontWeight' ||
		type === 'fontFamily' ||
		type === 'spacing' ||
		type === 'boxSpacing' ||
		type === 'boxBorder' ||
		type === 'boxRadius' ||
		type === 'boxShadow' ||
		type === 'contentAlign' ||
		type === 'elementContentAlign' ||
		type === 'containerAlign' ||
		type === 'contentMaxWidth' ||
		type === 'minHeight' ||
		type === 'columnsBreakpoint' ||
		type === 'columnsStackOrder' ||
		type === 'cardColumns' ||
		type === 'mediaFrame' ||
		type === 'cssFlag' ||
		type === 'opacity' ||
		type === 'overlay' ||
		type === 'fill' ||
		type === 'imageBackground' ||
		type === 'select'
	) {
		return type;
	}
	const key = String( field?.key || '' );
	if ( key.includes( 'media-frame' ) ) {
		return 'mediaFrame';
	}
	if (
		key.includes( 'closing-border' ) ||
		key.endsWith( '-flag' ) ||
		key.endsWith( '-enabled' )
	) {
		return 'cssFlag';
	}
	if ( key.includes( 'columns-stack-order' ) ) {
		return 'columnsStackOrder';
	}
	if ( key.includes( 'columns-breakpoint' ) ) {
		return 'columnsBreakpoint';
	}
	if ( key.includes( 'card-columns' ) ) {
		return 'cardColumns';
	}
	if ( key.includes( 'min-height' ) ) {
		return 'minHeight';
	}
	if ( key.includes( 'content-max-width' ) ) {
		return 'contentMaxWidth';
	}
	if ( key.includes( 'container-align' ) ) {
		return 'containerAlign';
	}
	if ( key.includes( 'content-align' ) ) {
		return 'contentAlign';
	}
	if ( key.includes( 'opacity' ) ) {
		return 'opacity';
	}
	if ( key.includes( 'overlay' ) ) {
		return 'overlay';
	}
	if ( key.includes( 'bg-image' ) || key.includes( 'background-image' ) ) {
		return 'imageBackground';
	}
	if ( /--bm-.+-border$/i.test( key ) ) {
		return 'boxBorder';
	}
	if ( /--bm-.+-border-radius$/i.test( key ) || /--bm-.+-radius$/i.test( key ) ) {
		return 'boxRadius';
	}
	if ( /--bm-.+-shadow$/i.test( key ) ) {
		return 'boxShadow';
	}
	if ( key.includes( 'font-family' ) ) {
		return 'fontFamily';
	}
	if ( key.includes( 'font-size' ) ) {
		return 'fontSize';
	}
	if ( key.includes( 'font-weight' ) ) {
		return 'fontWeight';
	}
	if (
		key.includes( 'padding' ) ||
		key.includes( 'margin' ) ||
		key.includes( 'gap' ) ||
		key.includes( 'spacing' )
	) {
		return 'spacing';
	}
	if ( key.includes( 'color' ) || key.endsWith( '-bg' ) || key.endsWith( '-text' ) || key.endsWith( '-accent' ) ) {
		return 'color';
	}
	return type;
}

/**
 * Pair padding/margin -y + -x fields into one BoxControl entry.
 * Gap and other single spacing fields stay as-is.
 *
 * @param {Array} fields Style schema fields.
 * @return {Array} Fields ready for StyleField (may include type boxSpacing).
 */
export function groupStyleFields( fields = [] ) {
	const list = Array.isArray( fields ) ? fields : [];
	const byKey = new Map( list.map( ( f ) => [ f.key, f ] ) );
	const used = new Set();
	const out = [];

	list.forEach( ( field ) => {
		if ( ! field?.key || used.has( field.key ) ) {
			return;
		}

		if ( field.type === 'boxSpacing' && field.verticalKey && field.horizontalKey ) {
			used.add( field.key );
			used.add( field.verticalKey );
			used.add( field.horizontalKey );
			out.push( field );
			return;
		}

		const key = String( field.key );
		const axial = key.match( /^(.*(?:padding|margin))-(y|x)$/i );
		if ( axial ) {
			const base = axial[ 1 ];
			const yKey = `${ base }-y`;
			const xKey = `${ base }-x`;
			const yField = byKey.get( yKey );
			const xField = byKey.get( xKey );

			if ( yField && xField && ! used.has( yKey ) && ! used.has( xKey ) ) {
				used.add( yKey );
				used.add( xKey );
				const prop = /margin/i.test( base )
					? __( 'Margin', 'joc-blockora-morph' )
					: __( 'Padding', 'joc-blockora-morph' );
				out.push( {
					key: base,
					label: prop,
					type: 'boxSpacing',
					verticalKey: yKey,
					horizontalKey: xKey,
				} );
				return;
			}
		}

		used.add( field.key );
		out.push( field );
	} );

	return out;
}
