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

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

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

import {
	withNotices,
	BaseControl,
	TextControl,
	TextareaControl,
	SelectControl,
	ToggleControl,
	__experimentalHStack as HStack,
	__experimentalVStack as VStack,
	ToolbarGroup,
	ToolbarButton,
} from '@wordpress/components'

import {
	compose,
} from '@wordpress/compose'

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

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

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

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

import {
	IS_SKA_THEME,
} from '@ska/utils'

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

import {
	useAs,
	useAttributes,
	type AsAttributes,
	type AttributesAttributes,
} from '../../supports'

import {
	ImageSizeControl,
	useMediaSizeOptions,
} from '../../components'

import {
	COVER_CLASSES,
	getPlaceholder,
} from '../image'

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

import {
	getAttachmentUrlBySizeSlug,
	type BlockModule,
	type tBlockEditProps,
	type tBlockSaveProps,
} from '@ska/shared'

const ALLOWED_MEDIA_TYPES = ['image']

const LINK_TO_OPTIONS = [
	{
		label: __('None', 'ska-blocks'),
		value: 'none',
	},
	{
		label: __('Attachment page', 'ska-blocks'),
		value: 'attachment',
	},
	{
		label: __('Custom links', 'ska-blocks'),
		value: 'list',
	},
]

const ROLES = [
	{
		label: _x('Figure', 'Image block role', 'ska-blocks'),
		tooltip: _x(`Apply role="figure" to gallery images that have an alt text.`, 'Image block role description', 'ska-blocks'),
		value: 'figure',
	},
	{
		label: _x('Presentation', 'Image block role', 'ska-blocks'),
		tooltip: _x('Hide the gallery images 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(`Gallery images are wrapped in links, the image needs to have an alt text in WordPress media library.`, 'Image block role description', 'ska-blocks'),
	}),
}))

const VOID_LINK_CLICK = (e: React.MouseEvent<HTMLAnchorElement>) => (e.preventDefault(), false)

export interface GalleryBlockAttributes extends AsAttributes, AttributesAttributes {
	ids: number[]
	role?: 'figure' | 'presentation' | 'none'
	/** Shorthand to add `w-full h-full object-cover rounded-[inherit] aspect-[inherit]` to gallery images. */
	cover?: boolean
	size: string
	width: number
	height: number
	crop: boolean
	linkTo: string
	links: string
	lightbox: boolean
	/** Add `srcset` to gallery images when possible. */
	srcset: boolean
	/** Lazyload images - @deprecated All images always lazyloaded unless `eager > 0`. */
	lazyload: boolean
	/** Number of images to load eagerly (when lazyload is enabled). */
	eager: number
	/** Disable to render gallery without a wrapper element. */
	wrap: boolean
}

const getRoleProps = ({
	role = 'figure',
	isLink = false,
}: {
	role: GalleryBlockAttributes['role'],
	isLink?: boolean,
}) => {
	return {
		...(role === 'figure' && {
			role: 'figure',
		}),
		...(role === 'presentation' && {
			'aria-hidden': true,
		}),
	}
}

const GalleryInspectorControls: React.FC<tBlockEditProps<GalleryBlockAttributes>> = (props) => {

	const {
		attributes,
		setAttributes,
	} = props

	const {
		role = 'figure',
		cover = false,
		size = 'full',
		width = 0,
		height = 0,
		crop = true,
		linkTo = 'none',
		links = '',
		lightbox = false,
		srcset = true,
		eager = 0,
		wrap = true,
	} = attributes

	const [Element] = useAs(props)

	const hasLinks = (linkTo !== 'none' || lightbox) && Element !== 'a'

	const MEDIA_SIZE_OPTIONS = useMediaSizeOptions()

	return <>
		<SkaPanelBody
			id='ska/gallery'
			title={__('Gallery options', 'ska-blocks')}
		>
			<ButtonGroup.Radio
				fluid
				pills
				label={__('Role', 'ska-blocks')}
				options={hasLinks ? LINK_ROLES : ROLES}
				value={role}
				onChange={value => setAttributes({role: (value || 'figure') as GalleryBlockAttributes['role']})}
				useDefaultButtons
			/>
			<ImageSizeControl
				label={__('Image size', 'ska-blocks')}
				slug={size}
				onChange={({slug}) => setAttributes({size: slug})}
				resizable={false}
				custom
			/>
			{size === 'custom' && <>
				<BaseControl __nextHasNoMarginBottom>
					<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
							/>
							<ToggleControl
								label={__('Crop', 'ska-blocks')}
								checked={crop}
								onChange={() => setAttributes({crop: !crop})}
								__nextHasNoMarginBottom
							/>
						</HStack>
					</VStack>
				</BaseControl>
			</>}
			<SelectControl
				label={__('Link to', 'ska-blocks')}
				options={[
					...LINK_TO_OPTIONS,
					...MEDIA_SIZE_OPTIONS,
				]}
				value={linkTo}
				onChange={value => setAttributes({linkTo: value})}
				__nextHasNoMarginBottom
				__next40pxDefaultSize
			/>
			{linkTo === 'list' && <>
			<WithTooltip tooltip={__('Each line corresponds to an image in the gallery, in order. Links that start with "@" are opened in new tab. Optionally add an aria label for the link by adding a `|` after the link, e.g. "@https://google.com|Visit Google"', 'ska-blocks')}>
				<TextareaControl
					label={__('Custom links', 'ska-blocks')}
					value={links}
					onChange={value => setAttributes({links: value})}
					__nextHasNoMarginBottom
				/>
			</WithTooltip>
			</>}
			{wrap && <>
				<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>
			</>}
			<WithTooltip tooltip={__(`Make the gallery images fill their wrappers.`, 'ska-blocks')}>
				<ToggleControl
					label={__('Cover', 'ska-blocks')}
					checked={cover}
					onChange={() => setAttributes({cover: !cover})}
					__nextHasNoMarginBottom
				/>
			</WithTooltip>
			<WithTooltip tooltip={__('Add srcset to gallery images when possible.', 'ska-blocks')}>
				<ToggleControl
					label={__('Responsive', 'ska-blocks')}
					checked={srcset}
					onChange={() => setAttributes({srcset: !srcset})}
					__nextHasNoMarginBottom
				/>
			</WithTooltip>
			<WithTooltip tooltip={__('Number of images from the beginning of the gallery to not lazyload.', 'ska-blocks')}>
				<TextControl
					type='number'
					label={__('Eager loading', 'ska-blocks')}
					value={eager || 0}
					min={0}
					onChange={(value: string) => setAttributes({eager: Number(value) || 0})}
					__nextHasNoMarginBottom
					__next40pxDefaultSize
				/>
			</WithTooltip>
			<WithTooltip tooltip={__('Disable this option to only render gallery images without a wrapper element, for example when the gallery is inside a slider this can be used to remove the wrapper and have each gallery image be a slide.', 'ska-blocks')}>
				<ToggleControl
					label={__('Gallery wrapper', 'ska-blocks')}
					checked={wrap}
					onChange={() => setAttributes({wrap: !wrap})}
					__nextHasNoMarginBottom
				/>
			</WithTooltip>
		</SkaPanelBody>
	</>
}

const GalleryEdit: React.FC<tBlockEditProps<GalleryBlockAttributes>> = (props) => {

	const {
		// @ts-ignore
		noticeUI,
		// @ts-ignore
		noticeOperations,
		attributes,
		setAttributes,
	} = props

	const {
		ids = [],
		role = 'figure',
		cover = false,
		size = 'full',
		linkTo = 'none',
		lightbox = false,
	} = attributes

	const blockProps = useBlockProps()
	const [Element, asProps, {isVoidElement}] = useAs(props)
	const attributesProps = useAttributes(props)

	const onSelectImages = (images: {id: number}[]) => {
		setAttributes({ids: images.map(image => image.id)})
	}

	const onUploadError = (message: any) => {
		noticeOperations.removeAllNotices()
		noticeOperations.createErrorNotice(message)
	}

	const hasImages = !!ids.length

	const images = useSelect(select => {

		const {
			getEntityRecord,
		} = select(coreDataStore)

		return ids.map(id => getEntityRecord<Attachment>('postType', 'attachment', id))
	}, [ids])

	const galleryContent = hasImages ? images.map((image, index) => {

		const key = `image-${index}`

		if(typeof image === 'undefined') {
			return (
				<div
					key={key}
					{...getRoleProps({role})}
					className='image placeholder wp-block-ska-gallery__image wp-block-ska-gallery__image--placeholder'
					dangerouslySetInnerHTML={{__html: getPlaceholder({cover})}}
				/>
			)
		}

		const src = getAttachmentUrlBySizeSlug(image, size)

		const Image = (
			<img
				src={src}
				loading='lazy'
				alt={image.alt_text}
				{...(cover && {className: COVER_CLASSES})}
			/>
		)

		const hasLink = (linkTo !== 'none' || lightbox) && Element !== 'a'

		if(hasLink) {
			return (
				// eslint-disable-next-line jsx-a11y/anchor-is-valid
				<a
					key={key}
					className='image wp-block-ska-gallery__image'
					href='#'
					{...getRoleProps({role, isLink: true})}
					onClick={VOID_LINK_CLICK}
				>
					{Image}
				</a>
			)
		}

		return (
			<div
				key={key}
				className='image wp-block-ska-gallery__image'
				{...getRoleProps({role})}
			>
				{Image}
			</div>
		)
	}) : <>
		<MediaPlaceholder
			icon='format-gallery'
			labels={{
				title: __('Gallery images', 'ska-blocks'),
				name: __('images', 'ska-blocks'),
			}}
			accept='image/*'
			allowedTypes={ALLOWED_MEDIA_TYPES}
			notices={noticeUI}
			onSelect={onSelectImages}
			onError={onUploadError}
			multiple
		/>
	</>

	return <>
		<InspectorControls>
			<GalleryInspectorControls {...props} />
		</InspectorControls>
		{hasImages && (
			<BlockControls>
				<ToolbarGroup>
					<MediaUpload
						onSelect={onSelectImages}
						allowedTypes={ALLOWED_MEDIA_TYPES}
						multiple
						gallery
						value={ids}
						render={({open}: {open: any}) => (
							<ToolbarButton
								label={__('Edit gallery images', 'ska-blocks')}
								children={__('Edit gallery images', 'ska-blocks')}
								icon='edit'
								onClick={open}
							/>
						)}
					/>
					<ToolbarButton
						title={__('Clear images', 'ska-blocks')}
						children={__('Clear', 'ska-blocks')}
						isActive={false}
						onClick={() => setAttributes({ids: []})}
					/>
				</ToolbarGroup>
			</BlockControls>
		)}
		<Element
			{...asProps}
			{...attributesProps}
			{...blockProps}
			children={isVoidElement ? undefined : galleryContent}
		/>
	</>
}

// @ts-ignore
const Edit = compose([withNotices])(GalleryEdit)

const GALLERY_CONTENT_PLACEHOLDER = <span className='ska-gallery-content-placeholder' />

const Save: React.FC<tBlockSaveProps<GalleryBlockAttributes>> = (props) => {

	const blockProps = useBlockProps.save()
	const [Element, asProps, {isVoidElement}] = useAs.save(props)
	const attributesProps = useAttributes.save(props)

	return (
		<Element
			{...asProps}
			{...attributesProps}
			{...blockProps}
			children={isVoidElement ? undefined : GALLERY_CONTENT_PLACEHOLDER}
		/>
	)
}


export default (skaBlocks: SkaBlocks): BlockModule => {

	const defaultTailwindClassAttributes = skaBlocks.tailwind.createBlockAttributes(
		IS_SKA_THEME
			? 'grid grid-cols-auto-fit-xs gap-4'
			: 'grid grid-cols-2 sm:grid-cols-4 gap-4'
	)

	return {
		metadata,
		settings: {
			edit: Edit,
			save: Save,
			variations: [
				{
					isDefault: true,
					attributes: {
						...defaultTailwindClassAttributes,
						skaBlocksSelectors: {
							'[&>.image]': {},
						},
					},
				},
				{
					name: 'gallery-images',
					title: __('Gallery images', 'ska-blocks'),
					description: __('Displays images from the WordPress Media Library without a wrapper element on the front end.', 'ska-blocks'),
					attributes: {
						...defaultTailwindClassAttributes,
						wrap: false,
					},
					isActive: ({wrap = true}) => !wrap,
				},
			],
			transforms: {
				from: [
					{
						type: 'block',
						blocks: ['core/gallery'],
						transform: (attributes, innerBlocks) => {

							const ids = innerBlocks.map(innerBlock => {

								if(innerBlock.name !== 'core/image') {
									return false
								}

								const {
									attributes = {},
								} = innerBlock

								const {
									id,
								} = attributes

								return id
							}).filter(v => v)

							return createBlock('ska/gallery', {ids})
						},
					},
				],
			},
		},
	}
}
