import {
	useBlockProps,
	InspectorControls,
	PanelColorSettings,
} from '@wordpress/block-editor';
import {
	PanelBody,
	SelectControl,
	RangeControl,
	TextControl,
	Flex,
	FlexItem,
	FlexBlock,
} from '@wordpress/components';
import { __, sprintf } from '@wordpress/i18n';
import { MediaUpload, MediaUploadCheck } from '@wordpress/block-editor';
import { useSelect } from '@wordpress/data';
import { useRef, useCallback } from '@wordpress/element';

const UNIT_OPTIONS = [
	{ value: 'px', label: 'px' },
	{ value: 'em', label: 'em' },
	{ value: 'rem', label: 'rem' },
];

function NumberUnitControl( {
	label,
	value,
	unit,
	onChangeValue,
	onChangeUnit,
	min,
	max,
	step = 1,
} ) {
	return (
		<div className="image-split-block-number-unit">
			<Flex gap={ 4 } align="flex-end" wrap="wrap">
				<FlexBlock>
					<TextControl
						label={ label }
						type="number"
						value={ value }
						min={ min }
						max={ max }
						step={ step }
						onChange={ ( v ) =>
							onChangeValue( parseFloat( v ) || 0 )
						}
						__next40pxDefaultSize
						__nextHasNoMarginBottom
					/>
				</FlexBlock>
				<FlexItem>
					<SelectControl
						label={ __( 'Unit', 'image-split' ) }
						value={ unit }
						options={ UNIT_OPTIONS }
						onChange={ onChangeUnit }
						__next40pxDefaultSize
						__nextHasNoMarginBottom
					/>
				</FlexItem>
			</Flex>
		</div>
	);
}

function Edit( { attributes, setAttributes } ) {
	const {
		beforeId,
		afterId,
		orientation,
		offset,
		rulerStyle,
		rulerArrowStyle = 'none',
		rulerArrowSize = 10,
		rulerArrowSizeUnit = 'px',
		rulerColor,
		rulerWidth = 4,
		rulerWidthUnit = 'px',
		labelBefore,
		labelAfter,
		labelPosition,
		labelAfterPosition,
		labelColor,
		labelBgColor,
		labelFontSize = 14,
		labelFontSizeUnit = 'px',
		objectPositionBefore = 'top left',
		objectPositionAfter = 'top left',
		baseImage = 'before',
	} = attributes;

	const blockProps = useBlockProps( { className: 'image-split-editor' } );

	const beforeMedia = useSelect(
		( select ) => ( beforeId ? select( 'core' ).getMedia( beforeId ) : null ),
		[ beforeId ]
	);
	const afterMedia = useSelect(
		( select ) => ( afterId ? select( 'core' ).getMedia( afterId ) : null ),
		[ afterId ]
	);
	const beforeUrl = beforeMedia?.source_url ?? null;
	const afterUrl = afterMedia?.source_url ?? null;

	const beforeDetails = beforeMedia?.media_details;
	const afterDetails = afterMedia?.media_details;
	const beforeW = beforeDetails?.width ?? 0;
	const beforeH = beforeDetails?.height ?? 0;
	const afterW = afterDetails?.width ?? 0;
	const afterH = afterDetails?.height ?? 0;
	const dimensionsDiffer =
		beforeW && beforeH && afterW && afterH && ( beforeW !== afterW || beforeH !== afterH );

	function aspectRatioString( w, h ) {
		if ( ! w || ! h ) return '—';
		const gcd = ( a, b ) => ( b ? gcd( b, a % b ) : a );
		const g = gcd( w, h );
		const rw = w / g;
		const rh = h / g;
		if ( rw <= 20 && rh <= 20 ) return rw + ':' + rh;
		return w + '×' + h;
	}
	const ratioBefore = aspectRatioString( beforeW, beforeH );
	const ratioAfter = aspectRatioString( afterW, afterH );
	const sizeRatioBefore =
		beforeW && beforeH
			? beforeW + '×' + beforeH + ' (' + ratioBefore + ')'
			: '—';
	const sizeRatioAfter =
		afterW && afterH
			? afterW + '×' + afterH + ' (' + ratioAfter + ')'
			: '—';
	const aspectBefore = beforeW && beforeH ? beforeW / beforeH : 0;
	const aspectAfter = afterW && afterH ? afterW / afterH : 0;
	const aspectsDiffer =
		aspectBefore &&
		aspectAfter &&
		Math.abs( aspectBefore - aspectAfter ) > 0.001;
	const showAlignmentHint = dimensionsDiffer || aspectsDiffer;

	const objectPositionOptions = [
		{ value: 'top left', label: __( 'Top left', 'image-split' ) },
		{ value: 'top center', label: __( 'Top center', 'image-split' ) },
		{ value: 'top right', label: __( 'Top right', 'image-split' ) },
		{ value: 'center left', label: __( 'Center left', 'image-split' ) },
		{ value: 'center center', label: __( 'Center center', 'image-split' ) },
		{ value: 'center right', label: __( 'Center right', 'image-split' ) },
		{ value: 'bottom left', label: __( 'Bottom left', 'image-split' ) },
		{ value: 'bottom center', label: __( 'Bottom center', 'image-split' ) },
		{ value: 'bottom right', label: __( 'Bottom right', 'image-split' ) },
	];

	const innerRef = useRef( null );
	const handleRulerMouseDown = useCallback(
		( e ) => {
			e.preventDefault();
			const el = innerRef.current;
			if ( ! el ) return;
			const isHorizontal = orientation !== 'vertical';
			const onMove = ( moveEvent ) => {
				const rect = el.getBoundingClientRect();
				const frac = isHorizontal
					? ( moveEvent.clientX - rect.left ) / rect.width
					: ( moveEvent.clientY - rect.top ) / rect.height;
				setAttributes( { offset: Math.max( 0, Math.min( 1, frac ) ) } );
			};
			const onUp = () => {
				document.removeEventListener( 'mousemove', onMove );
				document.removeEventListener( 'mouseup', onUp );
			};
			document.addEventListener( 'mousemove', onMove );
			document.addEventListener( 'mouseup', onUp );
			onMove( e );
		},
		[ orientation, setAttributes ]
	);

	const labelFontCss = `${ labelFontSize }${ labelFontSizeUnit }`;
	const rulerWcss = `${ rulerWidth }${ rulerWidthUnit }`;
	const rulerArrowCss = `${ rulerArrowSize }${ rulerArrowSizeUnit }`;

	const ImageButton = ( { id, onSelect } ) => (
		<MediaUploadCheck>
			<MediaUpload
				onSelect={ ( media ) => onSelect( media.id ) }
				allowedTypes={ [ 'image' ] }
				value={ id }
				render={ ( { open } ) => (
					<button type="button" className="button button-large" onClick={ open }>
						{ id
							? __( 'Replace image', 'image-split' )
							: __( 'Select image', 'image-split' ) }
					</button>
				) }
			/>
		</MediaUploadCheck>
	);

	const hasAnyImage = Boolean( beforeId || afterId );
	const hasBothImages = Boolean( beforeId && afterId );
	const innerPaddingBottom =
		beforeW && beforeH
			? ( beforeH / beforeW ) * 100 + '%'
			: afterW && afterH
				? ( afterH / afterW ) * 100 + '%'
				: undefined;

	function renderBeforeLayerInner() {
		if ( beforeId && ! beforeUrl ) {
			return <span>{ __( 'Loading…', 'image-split' ) }</span>;
		}
		if ( beforeUrl ) {
			return (
				<img
					src={ beforeUrl }
					alt={ __( 'Before', 'image-split' ) }
					style={ {
						width: '100%',
						height: '100%',
						objectFit: baseImage === 'before' ? 'contain' : 'cover',
						objectPosition: objectPositionBefore,
					} }
				/>
			);
		}
		return (
			<div className="image-split-editor-empty-slot">
				<p className="image-split-editor-empty-slot__label">
					{ __( 'Before', 'image-split' ) }
				</p>
				<ImageButton
					id={ beforeId }
					onSelect={ ( id ) => setAttributes( { beforeId: id } ) }
				/>
			</div>
		);
	}

	function renderAfterLayerInner() {
		if ( afterId && ! afterUrl ) {
			return <span>{ __( 'Loading…', 'image-split' ) }</span>;
		}
		if ( afterUrl ) {
			return (
				<img
					src={ afterUrl }
					alt={ __( 'After', 'image-split' ) }
					style={ {
						width: '100%',
						height: '100%',
						objectFit: baseImage === 'after' ? 'contain' : 'cover',
						objectPosition: objectPositionAfter,
					} }
				/>
			);
		}
		return (
			<div className="image-split-editor-empty-slot">
				<p className="image-split-editor-empty-slot__label">
					{ __( 'After', 'image-split' ) }
				</p>
				<ImageButton
					id={ afterId }
					onSelect={ ( id ) => setAttributes( { afterId: id } ) }
				/>
			</div>
		);
	}

	let content;
	if ( ! hasAnyImage ) {
		content = (
			<div
				style={ {
					padding: 24,
					background: '#f0f0f0',
					textAlign: 'center',
					minHeight: 200,
				} }
			>
				<p>{ __( 'Select Before and After images in the sidebar.', 'image-split' ) }</p>
				<div
					style={ {
						display: 'flex',
						gap: 16,
						justifyContent: 'center',
						flexWrap: 'wrap',
					} }
				>
					<div>
						<p>
							<strong>{ __( 'Before', 'image-split' ) }</strong>
						</p>
						<ImageButton
							id={ beforeId }
							onSelect={ ( id ) => setAttributes( { beforeId: id } ) }
						/>
					</div>
					<div>
						<p>
							<strong>{ __( 'After', 'image-split' ) }</strong>
						</p>
						<ImageButton
							id={ afterId }
							onSelect={ ( id ) => setAttributes( { afterId: id } ) }
						/>
					</div>
				</div>
			</div>
		);
	} else {
		content = (
			<>
				<div
					className={
						'image-split image-split--' +
						orientation +
						' image-split-editor-preview'
					}
					style={ { '--split-pos': Math.round( offset * 100 ) } }
				>
					<div
						className="image-split__inner"
						ref={ innerRef }
						style={
							innerPaddingBottom
								? { paddingBottom: innerPaddingBottom }
								: undefined
						}
					>
						<div className="image-split__before">
							<div
								style={ {
									position: 'absolute',
									inset: 0,
									background: '#ddd',
									display: 'flex',
									alignItems: 'center',
									justifyContent: 'center',
									overflow: 'hidden',
								} }
							>
								{ renderBeforeLayerInner() }
							</div>
							<span
								className={
									'image-split__label image-split__label--' +
									( labelPosition || 'bottom-left' )
								}
								style={ {
									color: labelColor,
									background: labelBgColor,
									fontSize: labelFontCss,
								} }
							>
								{ labelBefore }
							</span>
						</div>
						<div className="image-split__after">
							<div
								style={ {
									position: 'absolute',
									inset: 0,
									background: '#eee',
									display: 'flex',
									alignItems: 'center',
									justifyContent: 'center',
									overflow: 'hidden',
								} }
							>
								{ renderAfterLayerInner() }
							</div>
							<span
								className={
									'image-split__label image-split__label--' +
									( labelAfterPosition || 'bottom-right' )
								}
								style={ {
									color: labelColor,
									background: labelBgColor,
									fontSize: labelFontCss,
								} }
							>
								{ labelAfter }
							</span>
						</div>
						<div
							className={
								'image-split__ruler image-split__ruler--' + rulerStyle
							}
							style={ {
								left:
									orientation === 'horizontal'
										? offset * 100 + '%'
										: '50%',
								top:
									orientation === 'vertical'
										? offset * 100 + '%'
										: '50%',
								transform: 'translate(-50%, -50%)',
								'--ruler-color': rulerColor,
								'--ruler-width': rulerWcss,
								'--ruler-arrow-size': rulerArrowCss,
							} }
							onMouseDown={ handleRulerMouseDown }
							role="slider"
							tabIndex={ 0 }
							aria-label={ __( 'Drag to compare', 'image-split' ) }
						>
							{ rulerArrowStyle !== 'none' && (
								<span
									className={
										'image-split__ruler-arrows image-split__ruler-arrows--' +
										rulerArrowStyle
									}
									aria-hidden="true"
								>
									<span className="image-split__ruler-arrow image-split__ruler-arrow--primary" />
									<span className="image-split__ruler-arrow image-split__ruler-arrow--secondary" />
								</span>
							) }
						</div>
					</div>
				</div>
				<p style={ { marginTop: 8, fontSize: 12, color: '#666' } }>
					{ hasBothImages
						? __(
								'Preview. Drag the ruler on the front-end to compare.',
								'image-split'
							)
						: __(
								'Preview with slider. Select the other image in the preview or sidebar to finish.',
								'image-split'
							) }
				</p>
			</>
		);
	}

	const offsetPct = Math.round( offset * 100 );

	return (
		<>
			<InspectorControls>
				<PanelBody title={ __( 'Images', 'image-split' ) } initialOpen={ true }>
					<p>
						<strong>{ __( 'Before', 'image-split' ) }</strong>
					</p>
					{ beforeUrl && (
						<div style={ { marginBottom: 8 } }>
							<img
								src={ beforeUrl }
								alt=""
								style={ {
									maxWidth: '100%',
									height: 'auto',
									display: 'block',
									borderRadius: 4,
								} }
							/>
						</div>
					) }
					<ImageButton
						id={ beforeId }
						onSelect={ ( id ) => setAttributes( { beforeId: id } ) }
					/>
					<p style={ { marginTop: 12 } }>
						<strong>{ __( 'After', 'image-split' ) }</strong>
					</p>
					{ afterUrl && (
						<div style={ { marginBottom: 8 } }>
							<img
								src={ afterUrl }
								alt=""
								style={ {
									maxWidth: '100%',
									height: 'auto',
									display: 'block',
									borderRadius: 4,
								} }
							/>
						</div>
					) }
					<ImageButton
						id={ afterId }
						onSelect={ ( id ) => setAttributes( { afterId: id } ) }
					/>
				</PanelBody>
				<PanelBody
					title={ __( 'Image alignment', 'image-split' ) }
					initialOpen={ showAlignmentHint }
				>
					{ showAlignmentHint && (
						<p
							style={ {
								marginTop: 0,
								marginBottom: 12,
								fontSize: 12,
								color: '#1e1e1e',
							} }
						>
							{ aspectsDiffer
								? sprintf(
										/* translators: 1: Before size and ratio, 2: After size and ratio */
										__(
											'Images have different aspect ratios. Before: %1$s, After: %2$s. Use the options below to align the important part (e.g. center).',
											'image-split'
										),
										sizeRatioBefore,
										sizeRatioAfter
									)
								: sprintf(
										/* translators: 1: Before size and ratio, 2: After size and ratio */
										__(
											'Images have different dimensions. Before: %1$s, After: %2$s. Use the options below to align the important part (e.g. center).',
											'image-split'
										),
										sizeRatioBefore,
										sizeRatioAfter
									) }
						</p>
					) }
					<SelectControl
						label={ __( 'Before image position', 'image-split' ) }
						value={ objectPositionBefore }
						options={ objectPositionOptions }
						onChange={ ( v ) =>
							setAttributes( { objectPositionBefore: v } )
						}
						__nextHasNoMarginBottom
					/>
					<SelectControl
						label={ __( 'After image position', 'image-split' ) }
						value={ objectPositionAfter }
						options={ objectPositionOptions }
						onChange={ ( v ) =>
							setAttributes( { objectPositionAfter: v } )
						}
						__nextHasNoMarginBottom
					/>
					<SelectControl
						label={
							typeof window !== 'undefined' &&
							window.imageSplitBlockUi?.baseImageLabel
								? window.imageSplitBlockUi.baseImageLabel
								: __( 'Base image', 'image-split' )
						}
						help={
							typeof window !== 'undefined' &&
							window.imageSplitBlockUi?.baseImageDescription
								? window.imageSplitBlockUi.baseImageDescription
								: ''
						}
						value={ baseImage }
						options={ [
							{
								value: 'before',
								label: __( 'Before image', 'image-split' ),
							},
							{
								value: 'after',
								label: __( 'After image', 'image-split' ),
							},
						] }
						onChange={ ( v ) => setAttributes( { baseImage: v } ) }
						__nextHasNoMarginBottom
					/>
				</PanelBody>
				<PanelBody title={ __( 'Layout', 'image-split' ) }>
					<SelectControl
						label={ __( 'Orientation', 'image-split' ) }
						value={ orientation }
						options={ [
							{
								value: 'horizontal',
								label: __( 'Horizontal', 'image-split' ),
							},
							{
								value: 'vertical',
								label: __( 'Vertical', 'image-split' ),
							},
						] }
						onChange={ ( v ) => setAttributes( { orientation: v } ) }
						__nextHasNoMarginBottom
					/>
					<RangeControl
						label={ __( 'Default offset', 'image-split' ) }
						help={ __( 'Initial handle position as a percentage.', 'image-split' ) }
						value={ offsetPct }
						onChange={ ( v ) =>
							setAttributes( { offset: Math.max( 0, Math.min( 100, v ) ) / 100 } )
						}
						min={ 0 }
						max={ 100 }
						step={ 1 }
						initialPosition={ 50 }
						renderTooltipContent={ ( v ) => `${ v }%` }
					/>
				</PanelBody>
				<PanelColorSettings
					title={ __( 'Colors', 'image-split' ) }
					colorSettings={ [
						{
							value: rulerColor,
							onChange: ( v ) => setAttributes( { rulerColor: v } ),
							label: __( 'Ruler color', 'image-split' ),
						},
						{
							value: labelColor,
							onChange: ( v ) => setAttributes( { labelColor: v } ),
							label: __( 'Label text color', 'image-split' ),
						},
						{
							value: labelBgColor,
							onChange: ( v ) => setAttributes( { labelBgColor: v } ),
							label: __( 'Label background', 'image-split' ),
							enableAlpha: true,
						},
					] }
				/>
				<PanelBody title={ __( 'Ruler', 'image-split' ) }>
					<SelectControl
						label={ __( 'Ruler style', 'image-split' ) }
						value={ rulerStyle }
						options={ [
							{ value: 'solid', label: __( 'Solid', 'image-split' ) },
							{ value: 'double', label: __( 'Double', 'image-split' ) },
							{ value: 'dashed', label: __( 'Dashed', 'image-split' ) },
						] }
						onChange={ ( v ) => setAttributes( { rulerStyle: v } ) }
						__nextHasNoMarginBottom
					/>
					<SelectControl
						label={ __( 'Ruler arrow style', 'image-split' ) }
						value={ rulerArrowStyle }
						options={ [
							{ value: 'none', label: __( 'None', 'image-split' ) },
							{
								value: 'triangle',
								label: __( 'Triangles', 'image-split' ),
							},
							{ value: 'arrow', label: __( 'Arrows', 'image-split' ) },
							{ value: 'chevron', label: __( 'Chevrons', 'image-split' ) },
						] }
						onChange={ ( v ) =>
							setAttributes( { rulerArrowStyle: v } )
						}
						__nextHasNoMarginBottom
					/>
					<NumberUnitControl
						label={ __( 'Ruler line thickness', 'image-split' ) }
						value={ rulerWidth }
						unit={ rulerWidthUnit }
						onChangeValue={ ( n ) => setAttributes( { rulerWidth: n } ) }
						onChangeUnit={ ( u ) => setAttributes( { rulerWidthUnit: u } ) }
						min={ 0.1 }
						max={ 48 }
						step={ rulerWidthUnit === 'px' ? 1 : 0.05 }
					/>
					<NumberUnitControl
						label={ __( 'Ruler arrow size', 'image-split' ) }
						value={ rulerArrowSize }
						unit={ rulerArrowSizeUnit }
						onChangeValue={ ( n ) => setAttributes( { rulerArrowSize: n } ) }
						onChangeUnit={ ( u ) =>
							setAttributes( { rulerArrowSizeUnit: u } )
						}
						min={ 4 }
						max={ 64 }
						step={ rulerArrowSizeUnit === 'px' ? 1 : 0.05 }
					/>
				</PanelBody>
				<PanelBody title={ __( 'Labels', 'image-split' ) }>
					<TextControl
						label={ __( '"Before" label', 'image-split' ) }
						value={ labelBefore }
						onChange={ ( v ) => setAttributes( { labelBefore: v } ) }
						__nextHasNoMarginBottom
					/>
					<TextControl
						label={ __( '"After" label', 'image-split' ) }
						value={ labelAfter }
						onChange={ ( v ) => setAttributes( { labelAfter: v } ) }
						__nextHasNoMarginBottom
					/>
					<SelectControl
						label={ __( 'Label position (Before)', 'image-split' ) }
						value={ labelPosition }
						options={ [
							{
								value: 'top-left',
								label: __( 'Top left', 'image-split' ),
							},
							{
								value: 'top-right',
								label: __( 'Top right', 'image-split' ),
							},
							{
								value: 'bottom-left',
								label: __( 'Bottom left', 'image-split' ),
							},
							{
								value: 'bottom-right',
								label: __( 'Bottom right', 'image-split' ),
							},
							{
								value: 'center-left',
								label: __( 'Center left', 'image-split' ),
							},
							{
								value: 'center-right',
								label: __( 'Center right', 'image-split' ),
							},
						] }
						onChange={ ( v ) => setAttributes( { labelPosition: v } ) }
						__nextHasNoMarginBottom
					/>
					<SelectControl
						label={ __( 'Label position (After)', 'image-split' ) }
						value={ labelAfterPosition }
						options={ [
							{
								value: 'top-left',
								label: __( 'Top left', 'image-split' ),
							},
							{
								value: 'top-right',
								label: __( 'Top right', 'image-split' ),
							},
							{
								value: 'bottom-left',
								label: __( 'Bottom left', 'image-split' ),
							},
							{
								value: 'bottom-right',
								label: __( 'Bottom right', 'image-split' ),
							},
							{
								value: 'center-left',
								label: __( 'Center left', 'image-split' ),
							},
							{
								value: 'center-right',
								label: __( 'Center right', 'image-split' ),
							},
						] }
						onChange={ ( v ) =>
							setAttributes( { labelAfterPosition: v } )
						}
						__nextHasNoMarginBottom
					/>
					<NumberUnitControl
						label={ __( 'Label font size', 'image-split' ) }
						value={ labelFontSize }
						unit={ labelFontSizeUnit }
						onChangeValue={ ( n ) => setAttributes( { labelFontSize: n } ) }
						onChangeUnit={ ( u ) =>
							setAttributes( { labelFontSizeUnit: u } )
						}
						min={ 0.5 }
						max={ 72 }
						step={ labelFontSizeUnit === 'px' ? 1 : 0.05 }
					/>
				</PanelBody>
			</InspectorControls>
			<div { ...blockProps }>{ content }</div>
		</>
	);
}

export default Edit;
