import * as React from 'react'
import {__, _x, sprintf} from '@wordpress/i18n'

import {
	useBlockProps,
	InspectorControls,
	InspectorAdvancedControls,
	BlockControls,
	MediaPlaceholder,
	MediaReplaceFlow,
	// @ts-expect-error
} from '@wordpress/block-editor'

import {
	useEffect,
	useMemo,
	useState,
} from '@wordpress/element'

import {
	Button,
	ToolbarButton,
	BaseControl,
	TextControl,
	TextareaControl,
	ToggleControl,
	RangeControl,
	Notice,
	__experimentalHStack as HStack,
	__experimentalVStack as VStack,
} from '@wordpress/components'

import {
	select,
	useSelect,
} from '@wordpress/data'

import {
	store as coreDataStore,
	useEntityProp,
	type Attachment,
} from '@wordpress/core-data'

import {
	createBlock,
	// @ts-expect-error
} from '@wordpress/blocks'

import {
	__unstableStripHTML as stripHTML,
} from '@wordpress/dom'

import {
	Player as LottiePlayer,
	Controls as LottiePlayerControls,
} from '@lottiefiles/react-lottie-player'

import {
	ButtonGroup,
	skaSvgIcon,
	WithTooltip,
} from '@ska/components'

import {
	SkaPanelBody,
} from '@ska/plugin'

import {
	IS_DEBUG,
	sanitizeSVG,
	usePostMeta,
} from '@ska/utils'

import {
	IconPicker,
	PlaceholderImageControl,
	SVGEditModal,
	ImageSizeControl,
} from '../../components'

import {
	placeholderImageUrl,
} from '../../data'

import InlineImageButton from './InlineImageButton'

// @ts-ignore
import metadata from './block.json'

import {
	useLinkWrapper,
	LinkAttributes,
	LinkControls,
} from '../../supports/link'

import {
	useAttributes,
	AttributesAttributes,
} from '../../supports'

import {
	usePluginPreference,
} from '../../store'

import SVGStringRenderer, {SVGStringRendererNoRef} from './SVGStringRenderer'

import {
	html_beautify,
	// @ts-expect-error
} from 'js-beautify'

const HTML_BEAUTIFY_SETTINGS = {
	'indent_size': '1',
	'indent_char': '\t',
	'max_preserve_newlines': '-1',
	'preserve_newlines': false,
	'end_with_newline': false,
}

import type {
	SkaBlocks,
} from '../../types'

import {
	type BlockModule,
	type tImage,
	type tBlockEditProps,
	type tBlockSaveProps,
	type tFile,
	getPlaceholderSvg,
	getAttachmentUrlBySizeSlug,
	getAttachmentSizeSlugByUrl,
	selectImage,
} from '@ska/shared'

import type {
	CoreAlignAttributes,
} from '..'

import './style.scss'

const MODES = [
	{
		label: _x('File', 'Image block mode', 'ska-blocks'),
		value: 'file',
	},
	{
		label: _x('SVG', 'Image block mode', 'ska-blocks'),
		value: 'svg',
	},
	{
		label: _x('Icon', 'Image block mode', 'ska-blocks'),
		value: 'icon',
	},
	{
		label: _x('Lottie', 'Image block mode', 'ska-blocks'),
		value: 'lottie',
	},
	{
		label: _x('Placeholder', 'Image block mode', 'ska-blocks'),
		value: 'placeholder',
	},
]

const ROLES = [
	{
		label: _x('Figure', 'Image block role', 'ska-blocks'),
		tooltip: _x(`Apply role="figure" to wrapper element, image should have an alt text provided.`, 'Image block role description', 'ska-blocks'),
		value: 'figure',
	},
	{
		label: _x('Presentation', 'Image block role', 'ska-blocks'),
		tooltip: _x('Hide the image from assistive technologies.', 'Image block role description', 'ska-blocks'),
		value: 'presentation',
	},
	{
		label: _x('None', 'Image block role', 'ska-blocks'),
		tooltip: _x(`Don't apply any semantics.`, 'Image block role description', 'ska-blocks'),
		value: 'none',
	},
]

const LINK_ROLES = ROLES.map(role => ({
	...role,
	...(role.value === 'figure' && {
		label: _x('Link', 'Image block role', 'ska-blocks'),
		tooltip: _x(`Image is wrapped in a link, provide an "aria-label" or "title" attribute, or fall back to the image's alt text.`, 'Image block role description', 'ska-blocks'),
	}),
}))

const LOADING = [
	{
		label: _x('Lazy', 'Image loading attribute', 'ska-blocks'),
		tooltip: _x(`Lazyload the image.`, 'Image loading description', 'ska-blocks'),
		value: 'lazy',
	},
	{
		label: _x('Eager', 'Image loading attribute', 'ska-blocks'),
		tooltip: _x(`Don't lazyload the image.`, 'Image loading description', 'ska-blocks'),
		value: 'eager',
	},
	{
		label: _x('LCP', 'Image loading attribute', 'ska-blocks'),
		tooltip: _x(`Enable high fetchpriority and synchronous decoding.`, 'Image loading description', 'ska-blocks'),
		value: 'lcp',
	},
	{
		label: _x('Preload', 'Image loading attribute', 'ska-blocks'),
		tooltip: _x(`Preload the image in document head.`, 'Image loading description', 'ska-blocks'),
		value: 'preload',
	},
]

/** These need to match whatever is defined in `DynamicLinks.php` as `manual=true` links. */
const IMAGE_DYNAMIC_LINKS = [
	{
		label: _x('Attachment page', 'Image dynamic link name', 'ska-blocks'),
		value: '#ska-link--media-attachment',
	},
	{
		label: _x('Full size image', 'Image dynamic link name', 'ska-blocks'),
		value: '#ska-link--media-file',
	},
]

export interface ImageBlockAttributes extends LinkAttributes, AttributesAttributes, CoreAlignAttributes {
	mode?: 'file' | 'svg' | 'icon' | 'lottie' | 'placeholder'
	role?: 'figure' | 'presentation' | 'none'
	/** Use featured image. */
	featured?: boolean
	featuredSize: string
	featuredCrop: boolean
	/** Whether to show placeholder on front end when featured image is missing. */
	featuredPlaceholder: boolean
	/** Skips rendering the block when post content contains `ska/image` with `featured` `true`. */
	featuredExclusive: boolean
	/** Meta key that contains image ID. */
	metaKey?: string
	width?: number
	height?: number
	svg?: string
	id?: number
	src?: string
	alt?: string
	/** Icon collection name. */
	collection?: string
	/** Icon name. */
	icon?: string
	/** Image loading mode. */
	loading?: 'lazy' | 'eager' | 'lcp' | 'preload'
	isPlaceholder?: boolean
	/** Display larger image when clicking on the image. */
	lightbox?: boolean
	/** Wrap image in role="figure". */
	wrap?: boolean
	/** Shorthand to add `w-full h-full object-cover rounded-[inherit] aspect-[inherit]` to image when it has a wrapper. */
	cover?: boolean
	/** Add `srcset` to image when possible. */
	srcset?: boolean
	/** Add low res placeholder background to the image. */
	placeholder?: boolean
	/** Whether to preload low res placeholder in head. */
	preloadPlaceholder?: boolean
	/** Placeholder image index. */
	placeholderIndex?: number
	/** Sanitize svg. */
	sanitize?: boolean

	lottieControls?: boolean
	lottieAutoplay?: boolean
	lottieLoop?: boolean
	lottieHover?: boolean
	lottieMode?: 'normal' | 'bounce'
	lottieSpeed?: number
	lottieDirection?: 1 | -1
}

export const COVER_CLASSES = 'w-full h-full object-cover rounded-[inherit] aspect-[inherit]'

const FEATURED_IMAGE_PLACEHOLDER = '%SKA_FEATURED_IMAGE%'
const PLACEHOLDER_IMAGE_PLACEHOLDER = '%SKA_PLACEHOLDER_IMAGE%'

export const getPlaceholder = (args: {width?: string | number, height?: string | number, cover?: boolean, placeholderIndex?: number} = {}) => {

	const {
		width = '100%',
		height = '100%',
		cover = false,
		placeholderIndex = 0,
	} = args

	if(placeholderIndex > 0 && placeholderImageUrl) {
		// @ts-expect-error
		return `<img class="ska-placeholder${cover ? ` ${COVER_CLASSES}` : ''}" src="${sprintf(placeholderImageUrl, placeholderIndex)}" aria-hidden="true" />`
	}

	return getPlaceholderSvg({width, height, className: cover ? COVER_CLASSES : ''})
}

const getPlaceholderIcon = () => skaSvgIcon

const ACCEPT_LOTTIE = undefined
const ALLOWED_TYPES_LOTTIE = ['application/json', 'text/plain']

const LOTTIE_MODES = [
	{
		label: __('Normal', 'ska-blocks'),
		value: 'normal',
	},
	{
		label: __('Bounce', 'ska-blocks'),
		value: 'bounce',
	},
]

const LottiePlayerElement = 'lottie-player'

const getRoleProps = ({
	role = 'figure',
	mode = 'file',
	isLink = false,
	wrap = false,
}: {
	role: ImageBlockAttributes['role'],
	mode: ImageBlockAttributes['mode'],
	isLink: boolean,
	/** Are we wrapping or not, this isn't the block attribute `wrap` but the computed `shouldWrap`, e.g. in lightbox/link/icon mode we wrap regardless. */
	wrap: boolean,
}) => {

	// Plain svg is aria-hidden by default, but can be overridden with custom attribute.
	if(mode === 'svg' && !wrap) {
		return [
			{'aria-hidden': true},
			{},
		]
	}

	// Icon is always wrapped.
	if(mode === 'icon' && !isLink && role === 'figure') {
		return [
			{
				role,
				'aria-hidden': true, // For back-compat, otherwise would remove
			},
			{},
		]
	}

	// Plain images with role=presentation.
	if(mode === 'file' && !wrap && role === 'presentation') {
		return [
			{'aria-hidden': true},
			{},
		]
	}

	// Wrapped images with role=presentation.
	if(mode === 'file' && wrap && !isLink && role === 'presentation') {
		return [
			{'aria-hidden': true},
			{},
		]
	}

	if(role === 'presentation' && wrap && !isLink) {
		// Hide the wrapper element, hiding the contained media as well.
		return [
			{'aria-hidden': true},
			{},
		]
	}

	if(mode === 'placeholder' && wrap && !isLink) {
		return [
			{
				...(role === 'figure' && {role}), // Back-compat
				...(role === 'presentation' && {
					'aria-hidden': true,
				}),
			},
			{},
		]
	}

	return [
		{
			// Apply the role for non-links.
			...(wrap && !isLink && role !== 'none' && {
				role,
			}),
		},
		{
			// Props for the media element inside the wrapper.
			...wrap && {
				// Hide media inside <a>
				...(role === 'presentation' && isLink && {
					'aria-hidden': true,
				}),
			},
		},
	]
}

const ImageInspectorControls: React.FC<tBlockEditProps<ImageBlockAttributes>> = props => {

	const [isOpen, setIsOpen] = useState<boolean>(false)

	const {
		attributes,
		setAttributes,
		context,
	} = props

	const {
		postId,
		postType,
	} = context

	const {
		mode = 'placeholder',
		role = 'figure',
		featured = false,
		featuredSize = 'full',
		featuredCrop = true,
		featuredPlaceholder = false,
		metaKey = '',
		width,
		height,
		id,
		svg = '',
		src,
		alt = '',
		collection,
		icon,
		loading,
		lightbox = false,
		wrap = true,
		cover = false,
		srcset = true,
		placeholder = false,
		preloadPlaceholder = true,
		placeholderIndex = 0,
		sanitize = true,
		lottieControls = false,
		lottieAutoplay = true,
		lottieLoop = true,
		lottieHover = false,
		lottieMode = 'normal',
		lottieSpeed = 1,
		lottieDirection = 1,
	} = attributes

	const [_featuredImageId] = useEntityProp('postType', postType, 'featured_media', postId)
	const postMeta = usePostMeta(postType, postId)
	const metaValue = featured && metaKey && Object(postMeta).hasOwnProperty(metaKey) && !isNaN(postMeta[metaKey]) ? parseInt(postMeta[metaKey]) : undefined
	const featuredImageId = metaKey ? metaValue : _featuredImageId

	const media = useSelect(select => {

		const mediaId = featured ? featuredImageId : id

		if(mode !== 'file' || !mediaId) {
			return undefined
		}

		return select(coreDataStore).getEntityRecord<Attachment>('postType', 'attachment', mediaId)
	}, [mode, id, featured, featuredImageId])

	/** Automatically set image width and height when they are missing but available. */
	useEffect(() => {
		if(media?.media_details?.sizes && src && !width && !height) {
			const found = Object.keys(media.media_details.sizes).find(size => {
				return media.media_details.sizes[size].source_url === src
			})
			if(found) {
				const details = media.media_details.sizes[found]
				const {
					width: imageWidth = 0,
					height: imageHeight = 0,
				} = details
				if(imageWidth > 0 && imageHeight > 0) {
					setAttributes({width: imageWidth, height: imageHeight})
				}
			}
		}
	}, [media, src, width, height, setAttributes])

	const isDataURL = src && src.startsWith('data:image')
	const supportsResponsive = mode === 'file' && ((id && id > 0) || featured) && !isDataURL
	const supportsLoading = mode === 'file' && ((src && !isDataURL) || featured)
	const supportsLightbox = mode === 'file' && !isDataURL
	const supportsPlaceholder = supportsResponsive && supportsLoading && !!media?.media_details?.sizes?.medium
	const isLightbox = supportsLightbox && lightbox
	const supportsSvgEdit = mode === 'svg' || (mode === 'icon' && svg)

	const [Element] = useLinkWrapper(isLightbox ? 'a' : 'div', props)
	const isLink = Element === 'a'
	const shouldWrap = isLightbox || isLink || mode === 'icon' || mode === 'placeholder' ? true : wrap

	const sanitizedSvg = useMemo(() => sanitize ? sanitizeSVG(svg) : svg, [svg, sanitize])
	const svgIsAutoAriaHidden = svg && !shouldWrap && (
		!('aria-hidden' in (attributes?.skaBlocksAttributes?.record || {}))
		&& svg.indexOf('aria-hidden="false"') < 1
	)
	const svgInvalid = svg && !sanitizedSvg

	return <>
		<SkaPanelBody
			id='ska/image'
			title={__('Image options', 'ska-blocks')}
		>
			<ButtonGroup.Radio
				pills
				label={__('Mode', 'ska-blocks')}
				options={MODES}
				value={mode}
				onChange={value => {
					value && setAttributes({
						mode: value as ImageBlockAttributes['mode'],
						/** Reset src when it's a placeholder value. */
						...(src && src.indexOf('%SKA_') === 0 && {
							src: '',
						}),
						/** Reset when switching between file/lottie mode. */
						...(((mode === 'lottie' && value === 'file') || (mode === 'file' && value === 'lottie')) && {
							id: undefined,
							src: '',
						}),
					})
				}}
			/>
			{/* Role is not applied to `<a>`-s and unwrapped `<svg>`-s are always `aria-hidden=true` (can be overridden with custom attributes though). */}
			{(mode !== 'svg' || shouldWrap) && <>
				<ButtonGroup.Radio
					fluid
					pills
					label={__('Role', 'ska-blocks')}
					options={isLink ? LINK_ROLES : ROLES}
					value={role}
					onChange={value => setAttributes({role: (value || 'figure') as ImageBlockAttributes['role']})}
					useDefaultButtons
				/>
			</>}
			{isLink && !isLightbox && (
				<BaseControl __nextHasNoMarginBottom>
					<Notice
						className='ska-image-block__notice'
						isDismissible={false}
						status='info'
						children={__(`The image is wrapped in a link, an "aria-label" or "title" attribute is recommended.`, 'ska-blocks')}
					/>
				</BaseControl>
			)}
			{supportsSvgEdit && <>
				<BaseControl __nextHasNoMarginBottom>
					<HStack>
						<Button
							variant='primary'
							size='small'
							onClick={() => setIsOpen(true)}
							children={svg ? __('Edit SVG', 'ska-blocks') : __('Set SVG', 'ska-blocks')}
						/>
						{svg && (
							<Button
								size='small'
								isDestructive
								onClick={() => setAttributes({svg: ''})}
								children={__('Clear SVG', 'ska-blocks')}
								disabled={mode === 'icon'}
							/>
						)}
					</HStack>
					{isOpen && (
						<SVGEditModal
							sanitize={sanitize}
							svg={svg}
							onChange={nextSvg => setAttributes({svg: nextSvg})}
							onRequestClose={() => setIsOpen(false)}
						/>
					)}
				</BaseControl>
			</>}
			{mode === 'svg' && <>
				<ToggleControl
					label={__('Wrap', 'ska-blocks')}
					help={isLink ? __('The SVG is wrapped in a link. Remove the link if you need to remove the wrapper.', 'ska-blocks') : __('Wrap the SVG in an element.', 'ska-blocks')}
					checked={isLink ? true : wrap}
					disabled={isLink}
					onChange={() => setAttributes({wrap: !wrap})}
					__nextHasNoMarginBottom
				/>
				{svgIsAutoAriaHidden && (
					<BaseControl __nextHasNoMarginBottom>
						<Notice
							className='ska-image-block__notice'
							isDismissible={false}
							status='info'
							children={__(`The SVG is "aria-hidden" by default, add a custom attribute to override it if needed.`, 'ska-blocks')}
						/>
					</BaseControl>
				)}
				{svgInvalid && (
					<BaseControl __nextHasNoMarginBottom>
						<Notice
							className='ska-image-block__notice'
							isDismissible={false}
							status='warning'
							children={__(`Current SVG contains potentially unsafe attributes, check the contents or disable Advanced -> Sanitize SVG option to allow rendering it.`, 'ska-blocks')}
						/>
					</BaseControl>
				)}
			</>}
			{mode === 'icon' && <>
				<IconPicker
					collection={collection}
					icon={icon}
					onChange={({collection, icon, svg}) => {
						setAttributes({
							collection,
							icon,
							svg: html_beautify(svg, HTML_BEAUTIFY_SETTINGS),
						})
					}}
				/>
			</>}
			{mode === 'lottie' && <>
				<ToggleControl
					label={__('Show controls', 'ska-blocks')}
					checked={lottieControls}
					onChange={() => setAttributes({lottieControls: !lottieControls})}
					__nextHasNoMarginBottom
				/>
				<ToggleControl
					label={__('Autoplay', 'ska-blocks')}
					checked={lottieAutoplay}
					onChange={() => setAttributes({lottieAutoplay: !lottieAutoplay})}
					__nextHasNoMarginBottom
				/>
				<ToggleControl
					label={__('Loop', 'ska-blocks')}
					checked={lottieLoop}
					onChange={() => setAttributes({lottieLoop: !lottieLoop})}
					__nextHasNoMarginBottom
				/>
				<ToggleControl
					label={__('Play on mouse hover', 'ska-blocks')}
					checked={lottieHover}
					onChange={() => setAttributes({lottieHover: !lottieHover})}
					__nextHasNoMarginBottom
				/>
				<ButtonGroup.Radio
					label={__('Mode', 'ska-blocks')}
					pills
					isSmall
					options={LOTTIE_MODES}
					value={lottieMode}
					onChange={(value: string) => setAttributes({lottieMode: value as ImageBlockAttributes['lottieMode']})}
				/>
				<RangeControl
					label={__('Playback speed', 'ska-blocks')}
					value={lottieSpeed}
					onChange={nextSpeed => setAttributes({lottieSpeed: nextSpeed})}
					min={0.1}
					max={5}
					step={0.1}
					__nextHasNoMarginBottom
					__next40pxDefaultSize
				/>
				<ToggleControl
					label={__('Play in reverse', 'ska-blocks')}
					checked={lottieDirection === -1}
					onChange={() => setAttributes({lottieDirection: lottieDirection === 1 ? -1 : 1})}
					__nextHasNoMarginBottom
				/>
			</>}
			{mode === 'file' && <>
				{(id || src || featured) && role !== 'presentation' && (
					<WithTooltip tooltip={__(`Provide a clear and concise text replacement for the image's content or switch its' "Role" to "Presentation".`, 'ska-blocks')}>
						<TextareaControl
							label={__('Alternative text', 'ska-blocks')}
							placeholder={__('Add text or change the "Role" option to "Presentation"…', 'ska-blocks')}
							value={alt}
							onChange={(value: string) => setAttributes({alt: value})}
							__nextHasNoMarginBottom
						/>
					</WithTooltip>
				)}
				<WithTooltip tooltip={__(`Display current post's featured image.`, 'ska-blocks')}>
					<ToggleControl
						label={__('Featured', 'ska-blocks')}
						checked={featured}
						onChange={() => setAttributes({featured: !featured})}
						__nextHasNoMarginBottom
					/>
				</WithTooltip>
				{featured && <>
					<WithTooltip tooltip={__('Show placeholder image when featured image is not set.', 'ska-blocks')}>
						<ToggleControl
							label={__('Placeholder', 'ska-blocks')}
							checked={featuredPlaceholder}
							onChange={() => setAttributes({featuredPlaceholder: !featuredPlaceholder})}
							__nextHasNoMarginBottom
						/>
					</WithTooltip>
				</>}
				{supportsLightbox && <>
					<WithTooltip tooltip={__('Display image full-screen when clicking on it.', 'ska-blocks')}>
						<ToggleControl
							label={__('Lightbox', 'ska-blocks')}
							checked={lightbox}
							onChange={() => setAttributes({lightbox: !lightbox})}
							__nextHasNoMarginBottom
						/>
					</WithTooltip>
				</>}
				{!isLightbox && <>
					<WithTooltip tooltip={__('Wrap the image in an element.', 'ska-blocks')}>
						<ToggleControl
							label={__('Wrap', 'ska-blocks')}
							checked={wrap}
							onChange={() => setAttributes({wrap: !wrap})}
							__nextHasNoMarginBottom
						/>
					</WithTooltip>
				</>}
			</>}
			{(mode === 'file' || mode === 'placeholder') && <>
				{shouldWrap && <>
					<WithTooltip tooltip={__(`Make the image fill its' wrapper.`, 'ska-blocks')}>
						<ToggleControl
							label={__('Cover', 'ska-blocks')}
							checked={cover}
							onChange={() => setAttributes({cover: !cover})}
							__nextHasNoMarginBottom
						/>
					</WithTooltip>
				</>}
			</>}
			{supportsResponsive && <>
				<WithTooltip tooltip={__('Add "srcset" and "sizes" attributes to images when possible.', 'ska-blocks')}>
					<ToggleControl
						label={__('Responsive', 'ska-blocks')}
						checked={srcset}
						onChange={() => setAttributes({srcset: !srcset})}
						__nextHasNoMarginBottom
					/>
				</WithTooltip>
			</>}
			{supportsPlaceholder && <>
				<WithTooltip tooltip={__('Add a low quality version of the image as a placeholder background.', 'ska-blocks')}>
					<ToggleControl
						label={__('Low-res placeholder', 'ska-blocks')}
						checked={placeholder}
						onChange={() => setAttributes({placeholder: !placeholder})}
						__nextHasNoMarginBottom
					/>
				</WithTooltip>
				{placeholder && <>
					<WithTooltip tooltip={__('Pre-load low-res placeholder image in document head.', 'ska-blocks')}>
						<ToggleControl
							label={__('Preload placeholder', 'ska-blocks')}
							checked={preloadPlaceholder}
							onChange={() => setAttributes({preloadPlaceholder: !preloadPlaceholder})}
							__nextHasNoMarginBottom
						/>
					</WithTooltip>
				</>}
			</>}
			{supportsLoading && <>
				<ButtonGroup.Radio
					fluid
					label={_x('Loading', 'Label of image "loading" attribute', 'ska-blocks')}
					options={LOADING}
					value={loading}
					onChange={(value: string) => setAttributes({loading: value as ImageBlockAttributes['loading']})}
					useDefaultButtons
				/>
			</>}
			{mode === 'file' && <>
				<ImageSizeControl
					id={featured ? undefined : id}
					src={featured ? undefined : src}
					slug={featured ? featuredSize : undefined}
					onChange={({src, slug, width, height}) => {
						if(featured) {
							setAttributes({
								...(slug && {
									featuredSize: slug,
								}),
								...(width && height && {
									width,
									height,
								}),
							})
						} else {
							setAttributes({
								src,
								...(width && height && {
									width,
									height,
								}),
							})
						}
					}}
					resizable={!featured}
					custom={featured}
				/>
			</>}
			{mode === 'file' && (
				<VStack className='ska-image-size-controls'>
					<HStack>
						<TextControl
							type='number'
							label={__('Width', 'ska-blocks')}
							value={width || 0}
							min={1}
							onChange={(value: string) => setAttributes({width: Number(value) || 0})}
							__nextHasNoMarginBottom
							__next40pxDefaultSize
						/>
						<TextControl
							type='number'
							label={__('Height', 'ska-blocks')}
							value={height || 0}
							min={1}
							onChange={(value: string) => setAttributes({height: Number(value) || 0})}
							__nextHasNoMarginBottom
							__next40pxDefaultSize
						/>
						{featured && featuredSize === 'custom' && (
							<ToggleControl
								label={__('Crop', 'ska-blocks')}
								checked={featuredCrop}
								onChange={() => setAttributes({featuredCrop: !featuredCrop})}
								__nextHasNoMarginBottom
							/>
						)}
					</HStack>
				</VStack>
			)}
			{mode === 'placeholder' && <>
				<PlaceholderImageControl
					value={placeholderIndex}
					onChange={placeholderIndex => setAttributes({placeholderIndex})}
					showPlaceholderImageToggle
				/>
			</>}
		</SkaPanelBody>
	</>
}

const Edit: React.FC<tBlockEditProps<ImageBlockAttributes>> = props => {

	const {
		attributes,
		setAttributes,
		context,
		isSelected,
	} = props

	const {
		postId,
		postType,
	} = context

	const {
		mode = 'placeholder',
		role = 'figure',
		featured = false,
		featuredSize = 'full',
		featuredExclusive = false,
		metaKey = '',
		width,
		height,
		id,
		svg = '',
		src,
		alt = '',
		loading,
		lightbox = false,
		wrap = true,
		cover = false,
		placeholderIndex = 0,
		sanitize = true,
		lottieControls = false,
		lottieAutoplay = true,
		lottieLoop = true,
		lottieHover = false,
		lottieSpeed = 1,
		lottieDirection = 1,
	} = attributes

	const {
		children,
		...blockProps
	} = useBlockProps({className: 'image'})

	const attributesProps = useAttributes(props)

	const [_featuredImageId] = useEntityProp('postType', postType, 'featured_media', postId)
	const postMeta = usePostMeta(postType, postId)
	const metaValue = featured && metaKey && Object(postMeta).hasOwnProperty(metaKey) && !isNaN(postMeta[metaKey]) ? parseInt(postMeta[metaKey]) : undefined
	const featuredImageId = metaKey ? metaValue : _featuredImageId

	const [showPlaceholderImage] = usePluginPreference('showPlaceholderImage')

	const media = useSelect(select => {

		const mediaId = featured ? featuredImageId : id

		if(mode !== 'file' || !mediaId) {
			return undefined
		}

		return select(coreDataStore).getEntityRecord<Attachment>('postType', 'attachment', mediaId)
	}, [mode, id, featured, featuredImageId])

	const isDataURL = src && src.startsWith('data:image')
	const supportsInlining = mode === 'file' && src && !isDataURL
	const supportsLoading = mode === 'file' && ((src && !isDataURL) || featured)
	const supportsLightbox = mode === 'file' && !isDataURL
	const isLightbox = supportsLightbox && lightbox

	const [Element, linkProps] = useLinkWrapper(isLightbox ? 'a' : 'div', props)
	const isLink = Element === 'a'

	const imgProps = {
		src,
		...(mode === 'file' && featured && {
			src: '',
			...(media && {
				src: getAttachmentUrlBySizeSlug(media, featuredSize),
			}),
		}),
		...(!!alt && {alt}),
		...(width && width > 0 && {width}),
		...(height && height > 0 && {height}),
		...(supportsLoading && loading === 'lazy' && {loading}),
		...((wrap && cover) && {className: COVER_CLASSES}),
	}

	const hasFile = !!imgProps.src

	const onSelect = (selectedImage: tImage) => {
		const image = selectImage(selectedImage, getAttachmentSizeSlugByUrl(media, src))
		setAttributes({
			id: image.id,
			src: image.url,
			width: image.width,
			height: image.height,
			mode: 'file',
			...(image.alt && {alt: image.alt}),
		})
	}

	const onSelectURL = (url: string) => {
		setAttributes({
			id: undefined,
			src: url,
			mode: 'file',
		})
	}

	const onSelectLottie = (file: tFile) => {
		setAttributes({
			id: file.id,
			src: file.url,
			mode: 'lottie',
		})
	}

	const onSelectLottieURL = (url: string) => {
		setAttributes({
			id: undefined,
			src: url,
			mode: 'lottie',
		})
	}

	const onClear = () => {
		setAttributes({
			id: undefined,
			src: undefined,
			width: undefined,
			height: undefined,
		})
	}

	const hasLottie = !!src

	const shouldWrap = isLightbox || isLink || mode === 'icon' || mode === 'placeholder' ? true : wrap
	const shouldCover = (mode === 'file' || mode === 'placeholder') && shouldWrap && cover

	const [roleProps, imgRoleProps] = getRoleProps({role, mode, isLink, wrap: shouldWrap})

	const rootProps = {
		...linkProps,
		...attributesProps,
		...blockProps,
	}

	const sanitizedSvg = useMemo(() => sanitize ? sanitizeSVG(svg) : svg, [svg, sanitize])

	return <>
		<InspectorControls>
			<ImageInspectorControls {...props} />
		</InspectorControls>
		<InspectorAdvancedControls>
			{mode === 'file' && featured && <>
				<TextControl
					label={__('Meta key', 'ska-blocks')}
					help={__('Override featured image ID with a value from post meta.', 'ska-blocks')}
					value={metaKey}
					onChange={(value: string) => setAttributes({metaKey: value})}
					__nextHasNoMarginBottom
					__next40pxDefaultSize
				/>
				<ToggleControl
					label={__('Exclusive featured image', 'ska-blocks')}
					help={__(`Don't render this block when the current post content contains a "ska/image" block that renders the featured image.`, 'ska-blocks')}
					checked={featuredExclusive}
					onChange={() => setAttributes({featuredExclusive: !featuredExclusive})}
					__nextHasNoMarginBottom
				/>
			</>}
			{mode === 'svg' && <>
				<ToggleControl
					label={__('Sanitize SVG', 'ska-blocks')}
					help={__('Prevent rendering the SVG when potentially unsafe contents are detected.', 'ska-blocks')}
					checked={sanitize}
					onChange={() => setAttributes({sanitize: !sanitize})}
					__nextHasNoMarginBottom
				/>
			</>}
			{supportsInlining && <>
				<InlineImageButton
					src={src}
					onClick={dataURL => {
						setAttributes({
							id: undefined,
							src: dataURL,
						})
					}}
				/>
			</>}
		</InspectorAdvancedControls>
		{!isLightbox && (
			<LinkControls
				{...props}
				additionalDynamicLinks={IMAGE_DYNAMIC_LINKS}
			/>
		)}
		{((mode === 'file' && !featured) || mode === 'placeholder') && (
			<BlockControls group='other'>
				{(!id && !src) && <>
					<MediaReplaceFlow
						mediaId={id}
						mediaURL={src}
						allowedTypes={['image']}
						accept='image/*'
						onSelect={onSelect}
						onSelectURL={onSelectURL}
						name={__('Select image', 'ska-blocks')}
					/>
				</>}
				{(id || src) && <>
					<MediaReplaceFlow
						mediaId={id}
						mediaURL={src}
						allowedTypes={['image']}
						accept='image/*'
						onSelect={onSelect}
						onSelectURL={onSelectURL}
						name={__('Replace image', 'ska-blocks')}
					/>
					<ToolbarButton
						title={__('Clear image', 'ska-blocks')}
						children={__('Remove', 'ska-blocks')}
						isActive={false}
						onClick={onClear}
					/>
				</>}
			</BlockControls>
		)}
		{mode === 'lottie' && hasLottie && (
			<BlockControls group='other'>
				<MediaReplaceFlow
					mediaId={id}
					mediaURL={src}
					accept={ACCEPT_LOTTIE}
					allowedTypes={ALLOWED_TYPES_LOTTIE}
					onSelect={onSelectLottie}
					onSelectURL={onSelectLottieURL}
					name={__('Replace lottie', 'ska-blocks')}
				/>
				<ToolbarButton
					title={__('Clear lottie', 'ska-blocks')}
					children={__('Remove', 'ska-blocks')}
					isActive={false}
					onClick={onClear}
				/>
			</BlockControls>
		)}
		{mode === 'svg' && <>
			{shouldWrap && <>
				<Element
					{...roleProps}
					{...rootProps}
					dangerouslySetInnerHTML={{__html: sanitizedSvg || getPlaceholder()}}
				/>
			</>}
			{!shouldWrap && <>
				<SVGStringRenderer
					{...rootProps}
					svg={sanitizedSvg || getPlaceholder()}
				/>
			</>}
		</>}
		{mode === 'icon' && <>
			<Element
				{...roleProps}
				{...rootProps}
				dangerouslySetInnerHTML={{__html: sanitizedSvg || getPlaceholderIcon()}}
			/>
		</>}
		{mode === 'file' && hasFile && <>
			{shouldWrap && (
				<Element
					{...roleProps}
					{...rootProps}
				>
					<img
						{...imgRoleProps}
						{...imgProps}
					/>
				</Element>
			)}
			{!shouldWrap && (
				<img
					{...roleProps}
					{...attributesProps}
					{...blockProps}
					{...imgProps}
					children={undefined}
				/>
			)}
		</>}
		{((mode === 'file' && featured && !hasFile) || (mode === 'placeholder' && (!isSelected || showPlaceholderImage))) && (
			<Element
				{...roleProps}
				{...blockProps}
				dangerouslySetInnerHTML={{__html: mode === 'file' ? getPlaceholder({width, height, cover: shouldCover}) : getPlaceholder({cover: shouldCover, placeholderIndex})}}
			/>
		)}
		{((mode === 'placeholder' && isSelected && !showPlaceholderImage) || (mode === 'file' && !featured && !hasFile)) && (
			<div {...blockProps}>
				<MediaPlaceholder
					{...(shouldCover && {className: COVER_CLASSES})}
					multiple={false}
					allowedTypes={['image']}
					onSelect={onSelect}
					onSelectURL={onSelectURL}
				/>
			</div>
		)}
		{mode === 'lottie' && !hasLottie && (
			<div {...blockProps}>
				<MediaPlaceholder
					multiple={false}
					accept={ACCEPT_LOTTIE}
					allowedTypes={ALLOWED_TYPES_LOTTIE}
					onSelect={onSelectLottie}
					onSelectURL={onSelectLottieURL}
				/>
			</div>
		)}
		{mode === 'lottie' && hasLottie && (
			<div {...blockProps}>
				<LottiePlayer
					key={`${lottieAutoplay}${lottieLoop}${lottieHover}${lottieSpeed}${lottieDirection}${src}`}
					autoplay={lottieAutoplay}
					loop={lottieLoop}
					hover={lottieHover}
					speed={lottieSpeed}
					direction={lottieDirection}
					src={src}
				>
					<LottiePlayerControls
						visible={lottieControls}
						buttons={['play', 'repeat', 'frame', 'debug']}
					/>
				</LottiePlayer>
			</div>
		)}
	</>
}

const Save: React.FC<tBlockSaveProps<ImageBlockAttributes>> = props => {

	const {
		attributes,
	} = props

	const {
		mode = 'placeholder',
		role = 'figure',
		featured = false,
		width,
		height,
		svg,
		src,
		alt,
		loading,
		lightbox = false,
		wrap = true,
		cover = false,
		placeholderIndex = 0,
		sanitize = true,
		lottieControls = false,
		lottieAutoplay = true,
		lottieLoop = true,
		lottieHover = false,
		lottieMode = 'normal',
		lottieSpeed = 1,
		lottieDirection = 1,
	} = attributes

	const {
		children,
		...blockProps
	} = useBlockProps.save()

	const isDataURL = src && src.startsWith('data:image')
	const supportsLoading = mode === 'file' && ((src && !isDataURL) || featured)
	const supportsLightbox = mode === 'file' && !isDataURL
	const isLightbox = supportsLightbox && lightbox

	const [Element, linkProps] = useLinkWrapper.save(isLightbox ? 'a' : 'div', props)
	const isLink = Element === 'a'
	const shouldWrap = lightbox || isLink || mode === 'icon' || mode === 'placeholder' ? true : wrap

	const [roleProps, imgRoleProps] = getRoleProps({role, mode, isLink, wrap: shouldWrap})

	const attributesProps = useAttributes.save(props)

	if(mode === 'lottie') {

		if(!src) {
			return null
		}

		return (
			<Element
				{...roleProps}
				{...linkProps}
				{...lottieHover && {
					'x-data': '',
					'x-on:mouseenter': `$el.querySelector('lottie-player').play()`,
					'x-on:mouseleave': `$el.querySelector('lottie-player').stop()`,
				}}
				{...attributesProps}
				{...blockProps}
			>
				{/* @ts-ignore */}
				<LottiePlayerElement
					{...(lottieAutoplay && {
						autoplay: true,
					})}
					{...(lottieLoop && {
						loop: true,
					})}
					{...(lottieControls && {
						controls: true,
					})}
					speed={lottieSpeed}
					direction={lottieDirection}
					mode={lottieMode}
					src={src}
				/>
			</Element>
		)
	}

	if(['placeholder', 'svg', 'icon'].includes(mode) && placeholderIndex === 0) {

		const sanitizedSvg = sanitize ? sanitizeSVG(svg) : svg

		if(mode === 'svg' && !shouldWrap) {
			return (
				<SVGStringRendererNoRef
					{...roleProps}
					{...attributesProps}
					{...blockProps}
					svg={sanitizedSvg || getPlaceholder()}
				/>
			)
		}

		return (
			<Element
				{...roleProps}
				{...linkProps}
				{...attributesProps}
				{...blockProps}
				dangerouslySetInnerHTML={{
					__html: mode === 'placeholder'
						? getPlaceholder({cover})
						: sanitizedSvg || getPlaceholder(),
				}}
			/>
		)
	}

	const imgProps = {
		src,
		...(mode === 'file' && featured && {
			src: FEATURED_IMAGE_PLACEHOLDER,
		}),
		...(mode === 'placeholder' && placeholderIndex > 0 && {
			src: PLACEHOLDER_IMAGE_PLACEHOLDER,
		}),
		...(!!alt && {alt}),
		...(width && width > 0 && {width}),
		...(height && height > 0 && {height}),
		...(supportsLoading && loading === 'lazy' && {loading}),
		...((wrap && cover) && {className: COVER_CLASSES}),
	}

	if(!imgProps.src) {
		return null
	}

	return <>
		{shouldWrap && (
			<Element
				{...roleProps}
				{...linkProps}
				{...attributesProps}
				{...blockProps}
			>
				<img
					{...imgRoleProps}
					{...imgProps}
				/>
			</Element>
		)}
		{!shouldWrap && (
			<img
				{...roleProps}
				{...attributesProps}
				{...blockProps}
				{...imgProps}
			/>
		)}
	</>
}

const defaultAttributes = {}

export default (skaBlocks: SkaBlocks): BlockModule => ({
	metadata,
	settings: {
		edit: Edit,
		save: Save,
		variations: [
			{
				isDefault: true,
				attributes: defaultAttributes,
			},
			{
				name: 'icon',
				title: __('Icon', 'ska-blocks'),
				description: __('Scalable vector graphics that can be used as visual symbols.', 'ska-blocks'),
				attributes: {
					mode: 'icon',
					role: 'presentation',
					collection: 'heroicons',
					...skaBlocks.tailwind.createBlockAttributes(
						'grid place-items-center size-6 text-current',
						`.grid{display:grid}.place-items-center{place-items:center}.size-6{width:var(--spacing-6);height:var(--spacing-6)}.text-current{color:currentColor}`
					),
				},
				isActive: ({mode}) => mode === 'icon',
			},
			{
				name: 'featured',
				title: __('Featured image', 'ska-blocks'),
				attributes: {
					mode: 'file',
					role: 'presentation',
					featured: true,
				},
				// @ts-ignore
				isActive: ({mode, featured, metaKey}) => mode === 'file' && featured && !metaKey,
			},
		],
		transforms: {
			from: [
				{
					type: 'block',
					blocks: ['core/image'],
					transform: ({id, url, alt, caption, width, height, align}) => {
						return createBlock('ska/image', {
							...defaultAttributes,
							id,
							src: url,
							alt: alt || stripHTML(caption),
							width,
							height,
							mode: 'file',
							align,
						})
					},
				},
				{
					type: 'block',
					blocks: ['core/post-featured-image', 'woocommerce/product-image'],
					transform: () => {
						return createBlock('ska/image', {
							...defaultAttributes,
							mode: 'file',
							featured: true,
						})
					},
				},
			],
			to: [
				{
					type: 'block',
					blocks: ['core/image'],
					transform: (attributes) => {
						const {id, src, alt, width, height, align} = attributes as ImageBlockAttributes
						return createBlock('core/image', {
							id,
							url: src,
							alt,
							width,
							height,
							align,
						})
					},
				},
			],
		},
		__experimentalLabel: (attributes, {context}) => {

			const customName = attributes?.metadata?.name
			if(context === 'list-view' && customName) {
				return customName
			}

			const {
				mode,
				className,
			} = attributes

			if(context === 'list-view') {

				if(mode === 'file') {

					const {
						id,
						src,
						featured = false,
					} = attributes

					if(src && src.startsWith('data:image')) {
						return _x('Inlined image', 'Block name in list view - Image block', 'ska-blocks')
					}

					/** Notify when ID is specified but not found in media library (debug only). */
					if(IS_DEBUG && !featured && id) {
						const args = ['postType', 'attachment', id]
						// @ts-expect-error
						const media = select(coreDataStore).getEntityRecord<Attachment>(...args)
						// @ts-expect-error
						const resolutionStatus = select(coreDataStore).getResolutionState('getEntityRecord', args)?.status
						if(resolutionStatus === 'error') {
							return `${_x('Image', 'Block name in list view - Image block', 'ska-blocks')} ❗ ${sprintf(_x('Media (%d) not found', 'ID', 'ska-blocks'), id)}`
						}
					}
				}

				if(mode === 'icon') {

					const {
						collection,
						icon,
					} = attributes

					if(collection && icon) {
						const fullIconName = `${collection}/${icon}`
						const parts = fullIconName.split('/')
						const iconName = parts[parts.length - 1]
						if(iconName) {
							return sprintf(_x(`Icon (%s)`, 'Block name in list view - Image block (icon name)', 'ska-blocks'), iconName)
						}
					}
				}

				if(mode === 'svg') {
					const renderClassName = className ? `.${className.split(' ').join('.')}` : ''
					const tagName = `svg${renderClassName}`
					return sprintf(_x(`&lt;%s&gt;`, 'Block name in list view - HTML element', 'ska-blocks'), tagName)
				}

				if(mode === 'placeholder') {
					return _x('Placeholder image', 'Block name in list view - Image block', 'ska-blocks')
				}

				if(mode === 'lottie') {
					return _x('Lottie', 'Block name in list view - Image block', 'ska-blocks')
				}
			}
		},
	},
})
