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

import {
	useContext,
	useMemo,
} from '@wordpress/element'

import {
	plus as plusIcon,
	redo as redoIcon,
	brush as brushIcon,
} from '@wordpress/icons'

import {
	store as noticesStore,
} from '@wordpress/notices'

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

import {
	Search as SkaSearch,
} from '@ska/components'

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

import Fuse from 'fuse.js'

import {
	INSTANTIATED_DEFAULT_VALUE,
} from '../../attributes'

import {
	useParse,
} from '../parse'

import {
	useSkaBlocks,
} from '../../hooks'

import {
	useSkaBlocksDispatch,
} from '../../reducer'

import {
	ApplyTailwindContext,
} from '../../applyTailwind'

import {
	useBlocksPresetSlugs,
	usePresets,
} from '../../../supports/presets'

import {
	useNavigateToSelector,
} from '../../../supports/selectors'

import {
	handlePasteVariables,
} from '../../../supports/variables'

import type {
	tBlockAttributes,
	tBlockEditProps,
	tBlockName,
} from '@ska/shared'

import './style.scss'

export interface SearchProps {
	/** Block name. */
	name: tBlockName | undefined
	/** Block attributes. */
	attributes: tBlockAttributes
	/** Block attributes setter. */
	setAttributes: tBlockEditProps['setAttributes']
}

export const TAILWIND_VALUE_CLIPBOARD_PREFIX = 'ska-blocks:'

export const SEARCH_PLACEHOLDER = __('Add classes or utility…', 'ska-blocks')

const Search: React.FC<SearchProps> = ({
	name,
	attributes,
	setAttributes,
}) => {

	const navigateToSelector = useNavigateToSelector()
	const shouldApplyTailwind = useContext(ApplyTailwindContext) // Don't offer presets when `false`.

	const {
		createSuccessNotice,
		createErrorNotice,
	} = useDispatch(noticesStore)

	const features = useSkaBlocks().tailwind.getFeaturesList(attributes)
	const presets = usePresets()
	const currentPresets = useBlocksPresetSlugs(attributes)

	const [parse, ParseErrors] = useParse({name, attributes, setAttributes})
	const dispatch = useSkaBlocksDispatch(attributes, setAttributes)

	const [fuse, fuseKey] = useMemo(() => {

		const searchablePresets = shouldApplyTailwind ? Object
			.entries(presets)
			.filter(([slug]) => !currentPresets.includes(slug))
			.map(([slug, preset]) => ({
				type: 'preset',
				presetSlug: slug,
				presetTitle: preset.title,
			})) : []

		// @ts-ignore
		const searchData = features.concat(searchablePresets)

		return [
			new Fuse(searchData, {
				keys: [
					'label',
					'groupLabel',
					'presetTitle',
					'presetSlug',
				],
			}),
			searchData.length,
		]
	}, [features, presets, currentPresets, shouldApplyTailwind])

	const getResults = (searchTerm: string) => {

		if(!searchTerm) {
			return []
		}

		const searchResults = fuse.search(searchTerm).map(r => r.item).map((result: any) => {

			switch(result.type) {

				case 'preset':
					return {
						id: result.presetSlug,
						presetSlug: result.presetSlug,
						title: result.presetTitle,
						content: __('Add preset', 'ska-blocks'),
					}

				default:
					return {
						id: result.id,
						title: result.label,
						content: result.groupLabel,
						hasValue: result.hasValue,
					}
			}
		})

		const probablySelector = searchTerm.endsWith(':')

		const addClasses = {
			id: 'parse',
			title: __('Add classes', 'ska-blocks'),
			content: searchTerm,
			hasValue: false,
		}

		const addSelector = {
			id: 'add-selector',
			title: __('Add selector', 'ska-blocks'),
			content: searchTerm,
			hasValue: false,
		}

		const isVariable = searchTerm.startsWith('--')

		return [
			...(isVariable ? [{
				id: 'add-variable',
				title: __('Add variable', 'ska-blocks'),
				content: searchTerm,
				hasValue: false,
			}] : []),
			...(probablySelector ? [addSelector, addClasses] : [addClasses, addSelector]),
			...searchResults.slice(0, 15),
		]
	}

	const applyPastedValue = (text: string) => {

		const invalidError = () => createErrorNotice(__('Invalid value.', 'ska-blocks'), {type: 'snackbar'})

		if(text.indexOf(TAILWIND_VALUE_CLIPBOARD_PREFIX) !== 0) {
			invalidError()
			return
		}

		let pastedAttributes: tBlockAttributes = {}
		try {
			pastedAttributes = JSON.parse(text.replace(TAILWIND_VALUE_CLIPBOARD_PREFIX, ''))
		} catch(e) {
			invalidError()
			return
		}

		const {
			skaBlocksSelectors = {},
			...rest
		} = pastedAttributes

		dispatch.setAttributes({
			skaBlocksSelectors: {
				...attributes.skaBlocksSelectors,
				...skaBlocksSelectors,
			},
			...rest,
		})

		createSuccessNotice(__('Pasted value.', 'ska-blocks'), {type: 'snackbar'})
	}

	const onPaste = (e: React.ClipboardEvent<HTMLInputElement>) => {

		const text = e?.clipboardData?.getData('Text')

		if(!text) {
			return
		}

		if(handlePasteVariables({
			content: text,
			apply: pastedRecord => {

				const {
					skaBlocksVariables = {},
				} = attributes

				const {
					record = {},
				} = skaBlocksVariables

				dispatch.setAttributes({
					skaBlocksVariables: {
						...skaBlocksVariables,
						record: {
							...record,
							...pastedRecord,
						},
					},
				})
			},
			onSuccess: () => {
				createSuccessNotice(__('Pasted variables.', 'ska-blocks'), {type: 'snackbar'})
			},
			onError: () => {
				createErrorNotice(__('Invalid value.', 'ska-blocks'), {type: 'snackbar'})
			},
		})) {
			e.preventDefault()
			return
		}

		if(text.indexOf(TAILWIND_VALUE_CLIPBOARD_PREFIX) !== 0) {
			return
		}

		e.preventDefault()

		applyPastedValue(text)
	}

	return <>
		<SkaSearch
			key={`ska-search-${fuseKey}`}
			className='ska-blocks-search ska-blocks--use-code-inputs'
			getResults={getResults}
			onSelect={result => {

				const {
					id,
					content,
					presetSlug,
				} = result

				if(id === 'parse') {
					parse(content)
					return
				}

				if(id === 'add-selector') {
					dispatch.addSelector(content)
					navigateToSelector(content)
					return
				}

				if(id === 'add-variable') {
					dispatch.addVariable(content)
					return
				}

				if(presetSlug) {
					dispatch.addPreset(presetSlug)
					return
				}

				const feature = features.find(f => f.id === id)
				if(!feature) {
					return
				}

				if(feature.hasValue) {
					focusElement(`#${feature.id}`)
					return
				}

				dispatch.setAttributes({[feature.id]: INSTANTIATED_DEFAULT_VALUE})
				focusElement(`#${feature.id}`, 50)
			}}
			onPaste={onPaste}
			getResultIcon={({hasValue, presetSlug}) => {
				if(presetSlug) {
					return brushIcon
				}
				return hasValue ? redoIcon : plusIcon
			}}
			actionInputProps={{
				label: __('Search for Tailwind features to add the the block', 'ska-blocks'),
				placeholder: SEARCH_PLACEHOLDER,
				actionLabel: __('Search', 'ska-blocks'),
			}}
		/>
		{ParseErrors}
	</>
}

export default Search
