/**
 * Intelligent spacing with link / unlink field count.
 *
 * - 2 sides: linked (1 field) → unlinked (2 fields)
 * - 3 sides: linked (1 field) → unlinked (3 fields)
 * - 4 sides (top, right, bottom, left): linked (1) → axial Y/X (2) → unlinked (4)
 *
 * Link mode is persisted on the block via `modeKey` (e.g. `--bm-padding-link-mode`)
 * so refresh keeps the field count the user left.
 *
 * Icons match Gutenberg BoxControl (link / linkOff + side preview strokes).
 */
import { Button } from '@wordpress/components';
import { __ } from '@wordpress/i18n';
import { link, linkOff } from '@wordpress/icons';
import { SpacingInputRow } from './SpacingControl';
import {
	inferSpacingLinkMode,
	normalizeLinkMode,
	resolveLinkModeKey,
} from '../styleLinkMode';
// Styles: src/shared/shared-ui.js → build/shared-ui.css

/**
 * @typedef {{ key: string, label: string }} SpacingSide
 */

const SIDE_ORDER = [ 'left', 'right', 'top', 'bottom' ];
const SIDE_LABELS = {
	left: 'Left',
	right: 'Right',
	top: 'Top',
	bottom: 'Bottom',
};

/**
 * Title for a spacing control from available sides.
 * 4 sides → "Padding"; 2 → "Padding: Left-Right"; 3 → "Padding: Left-Right-Top".
 *
 * @param {string}        kind  e.g. Padding | Margin
 * @param {SpacingSide[]} sides
 * @return {string}
 */
export function formatSpacingControlTitle( kind, sides ) {
	const base = kind || __( 'Spacing', 'joc-blockora-morph' );
	const found = [];
	SIDE_ORDER.forEach( ( side ) => {
		const hit = ( sides || [] ).some( ( s ) => {
			const key = String( s.key || '' ).toLowerCase();
			const label = String( s.label || '' ).toLowerCase();
			return (
				key === side ||
				key.endsWith( `-${ side }` ) ||
				label.includes( side )
			);
		} );
		if ( hit ) {
			found.push( SIDE_LABELS[ side ] );
		}
	} );
	if ( found.length >= 4 || found.length <= 1 ) {
		return base;
	}
	return `${ base }: ${ found.join( '-' ) }`;
}

/**
 * Map a side key/label to a BoxControl-style side token.
 *
 * @param {SpacingSide|string} side
 * @return {'top'|'right'|'bottom'|'left'|'all'|'vertical'|'horizontal'}
 */
function sideTokenFromSide( side ) {
	if ( typeof side === 'string' ) {
		return side;
	}
	const key = String( side?.key || '' ).toLowerCase();
	const label = String( side?.label || '' ).toLowerCase();
	for ( const name of SIDE_ORDER ) {
		if (
			key === name ||
			key.endsWith( `-${ name }` ) ||
			label.includes( name )
		) {
			return name;
		}
	}
	return 'all';
}

/**
 * Gutenberg BoxControl side preview (focused strokes).
 *
 * @param {object} props
 * @param {'all'|'top'|'right'|'bottom'|'left'|'vertical'|'horizontal'} [props.side]
 * @param {string} [props.className]
 */
function SpacingSideIcon( { side = 'all', className = '' } ) {
	const has = ( value ) =>
		side === 'all' ||
		side === value ||
		( side === 'vertical' &&
			( value === 'top' || value === 'bottom' ) ) ||
		( side === 'horizontal' &&
			( value === 'left' || value === 'right' ) );

	const strokes = [
		{ name: 'top', focused: has( 'top' ) },
		{ name: 'right', focused: has( 'right' ) },
		{ name: 'bottom', focused: has( 'bottom' ) },
		{ name: 'left', focused: has( 'left' ) },
	];

	return (
		<span
			className={ [ 'bm-spacing-side-icon', className ]
				.filter( Boolean )
				.join( ' ' ) }
			aria-hidden="true"
		>
			<span className="bm-spacing-side-icon__view">
				{ strokes.map( ( stroke ) => (
					<span
						key={ stroke.name }
						className={
							`bm-spacing-side-icon__stroke bm-spacing-side-icon__stroke--${ stroke.name }` +
							( stroke.focused ? ' is-focused' : '' )
						}
					/>
				) ) }
			</span>
		</span>
	);
}

/**
 * @param {object}          props
 * @param {SpacingSide[]}   props.sides
 * @param {Record<string, string>} props.values
 * @param {(patch: Record<string, string>) => void} props.onChange
 * @param {string}          [props.kind] Padding | Margin — drives the section title
 * @param {string}          [props.linkedLabel]
 * @param {string}          [props.className]
 * @param {string}          [props.axialVerticalLabel]
 * @param {string}          [props.axialHorizontalLabel]
 * @param {string}          [props.modeKey] CSS var / value key for persisted link mode
 */
export default function IntelligentSpacing( {
	sides = [],
	values = {},
	onChange,
	kind,
	linkedLabel,
	className = '',
	axialVerticalLabel,
	axialHorizontalLabel,
	modeKey: modeKeyProp,
} ) {
	const count = sides.length;
	const modes =
		count >= 4 ? [ 'linked', 'axial', 'sides' ] : [ 'linked', 'sides' ];
	const title = formatSpacingControlTitle(
		kind || linkedLabel || __( 'Spacing', 'joc-blockora-morph' ),
		sides
	);
	const modeKey = resolveLinkModeKey( sides, modeKeyProp );
	const storedMode = modeKey
		? normalizeLinkMode( values[ modeKey ] )
		: '';
	const mode =
		storedMode || inferSpacingLinkMode( sides, values );

	const read = ( key ) => String( values[ key ] ?? '' ).trim();

	const writePatch = ( patch ) => {
		if ( typeof onChange === 'function' ) {
			onChange( patch );
		}
	};

	const withMode = ( patch, nextMode = mode ) => {
		if ( ! modeKey ) {
			return patch;
		}
		return { ...patch, [ modeKey ]: nextMode };
	};

	const writeAll = ( next ) => {
		const value = next || '';
		const patch = {};
		sides.forEach( ( side ) => {
			patch[ side.key ] = value;
		} );
		writePatch( withMode( patch, 'linked' ) );
	};

	const writeAxial = ( vertical, horizontal ) => {
		if ( count < 4 ) {
			return;
		}
		const [ top, right, bottom, left ] = sides;
		writePatch(
			withMode(
				{
					[ top.key ]: vertical || '',
					[ bottom.key ]: vertical || '',
					[ left.key ]: horizontal || '',
					[ right.key ]: horizontal || '',
				},
				'axial'
			)
		);
	};

	const writeSide = ( key, next ) => {
		writePatch( withMode( { [ key ]: next || '' }, 'sides' ) );
	};

	const cycleMode = () => {
		const idx = modes.indexOf( mode );
		const next = modes[ ( idx < 0 ? 0 : idx + 1 ) % modes.length ];

		if ( next === 'linked' ) {
			const shared =
				sides.map( ( s ) => read( s.key ) ).find( Boolean ) || '';
			const patch = {};
			sides.forEach( ( side ) => {
				patch[ side.key ] = shared;
			} );
			writePatch( withMode( patch, next ) );
			return;
		}

		if ( next === 'axial' && count >= 4 ) {
			// Keep current side values — only persist mode.
			// Do not copy vertical onto horizontal (or vice versa).
			writePatch( withMode( {}, next ) );
			return;
		}

		// Unlinked: keep current side values — only persist the mode.
		writePatch( withMode( {}, next ) );
	};

	const linked =
		mode === 'linked'
			? sides.map( ( s ) => read( s.key ) ).find( Boolean ) || ''
			: '';

	const [ top, , , left ] =
		count >= 4 ? sides : [ null, null, null, null ];
	const axialY =
		count >= 4 ? read( top.key ) || read( sides[ 2 ].key ) || '' : '';
	const axialX =
		count >= 4 ? read( left.key ) || read( sides[ 1 ].key ) || '' : '';

	const isLinked = mode === 'linked';
	const tip =
		mode === 'linked'
			? count >= 4
				? __(
						'Linked: all sides equal. Click for vertical / horizontal pairs.',
						'joc-blockora-morph'
				  )
				: __(
						'Linked: one value for both sides. Click to edit each side.',
						'joc-blockora-morph'
				  )
			: mode === 'axial'
			? __(
					'Paired: top↔bottom and left↔right. Click to edit every side.',
					'joc-blockora-morph'
			  )
			: count >= 4
			? __(
					'Unlinked: each side separate. Click to link all sides.',
					'joc-blockora-morph'
			  )
			: __(
					'Unlinked: each side separate. Click to link sides.',
					'joc-blockora-morph'
			  );

	const renderRow = ( sideToken, label, value, onValueChange ) => (
		<div className="bm-intel-spacing__row">
			<SpacingSideIcon side={ sideToken } />
			<span className="screen-reader-text">{ label }</span>
			<SpacingInputRow
				label={ label }
				value={ value }
				onChange={ onValueChange }
			/>
		</div>
	);

	return (
		<div
			className={ [
				'bm-intel-spacing',
				`bm-intel-spacing--${ mode }`,
				`bm-intel-spacing--count-${ count }`,
				className,
			]
				.filter( Boolean )
				.join( ' ' ) }
		>
			<div className="bm-intel-spacing__toolbar">
				<strong className="bm-intel-spacing__title">{ title }</strong>
				{ count > 1 ? (
					<Button
						className={
							'component-box-control__linked-button bm-intel-spacing__link' +
							( isLinked ? ' is-linked' : '' ) +
							( mode === 'axial' ? ' is-axial' : '' )
						}
						size="small"
						icon={ isLinked ? link : linkOff }
						iconSize={ 24 }
						onClick={ cycleMode }
						aria-pressed={ isLinked }
						label={ tip }
						showTooltip
					/>
				) : null }
			</div>

			{ isLinked
				? renderRow( 'all', title, linked, writeAll )
				: null }

			{ mode === 'axial' && count >= 4 ? (
				<>
					{ renderRow(
						'vertical',
						axialVerticalLabel ||
							__( 'Vertical', 'joc-blockora-morph' ),
						axialY,
						( next ) => writeAxial( next || '', axialX )
					) }
					{ renderRow(
						'horizontal',
						axialHorizontalLabel ||
							__( 'Horizontal', 'joc-blockora-morph' ),
						axialX,
						( next ) => writeAxial( axialY, next || '' )
					) }
				</>
			) : null }

			{ mode === 'sides'
				? sides.map( ( side ) =>
						renderRow(
							sideTokenFromSide( side ),
							side.label,
							read( side.key ),
							( next ) => writeSide( side.key, next )
						)
				  )
				: null }
		</div>
	);
}
