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

import {
	Modal,
	CheckboxControl,
	__experimentalVStack as VStack,
} from '@wordpress/components'

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

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

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

import {
	ActionInput,
} from '@ska/components'

import {
	usePresetOptionsEdit,
	type PresetBlockAttributes,
} from '..'

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

import {
	InspectorControlsContext,
} from '../../../tailwind/inspector-controls'

import type {
	TailwindFeatureAttributes,
	SelectorsValue,
	VariablesValue,
} from '../../../types'

export interface CreatePresetProps {
	onRequestClose: () => void
}

const CreatePreset: React.FC<CreatePresetProps> = ({
	onRequestClose,
}) => {

	const [, presetDispatch] = usePresetOptionsEdit()

	const {
		attributes,
		setAttributes,
		features,
	} = useContext(InspectorControlsContext)

	const dispatch = useSkaBlocksDispatch(attributes, setAttributes)

	const {
		skaBlocksSelectors = {},
		skaBlocksVariables = {},
	} = attributes

	const {
		record: variablesRecord = {},
	} = skaBlocksVariables

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

	const [presetName, setPresetName] = useState('')
	const [isLoading, setIsLoading] = useState(false)

	const [excludedFeatures, setExcludedFeatures] = useState<string[]>([])
	const usedFeatures = features.filter(({hasValue}) => hasValue)

	const selectors = Object.keys(skaBlocksSelectors)
	const [excludedSelectors, setExcludedSelectors] = useState<string[]>([])

	const variables = Object.keys(variablesRecord)
	const [excludedVariables, setExcludedVariables] = useState<string[]>([])

	const onCreatePreset = async (name: string) => {

		if(isLoading) {
			return
		}

		if(!name) {
			createErrorNotice(__('Preset name is required', 'ska-blocks'), {type: 'snackbar'})
			return
		}

		const includedFeatures = usedFeatures.filter(({id}) => !excludedFeatures.includes(id))
		const includedSelectors = selectors.filter(s => !excludedSelectors.includes(s))
		const includedVariables = variables.filter(v => !excludedVariables.includes(v))
		if(
			!includedFeatures.length
			&& !includedSelectors.length
			&& !includedVariables.length
		) {
			createErrorNotice(__('Cannot create empty preset', 'ska-blocks'), {type: 'snackbar'})
			return
		}

		const presetAttributes = {

			...includedFeatures.reduce((acc, {id}) => {
				acc[id] = attributes[id]
				return acc
			}, {} as PresetBlockAttributes),

			...(includedSelectors.length > 0 && {
				skaBlocksSelectors: includedSelectors.reduce((acc, cur) => {
					acc[cur] = skaBlocksSelectors[cur]
					return acc
				}, {} as PresetBlockAttributes),
			}),

			...(includedVariables.length > 0 && {
				skaBlocksVariables: {
					record: includedVariables.reduce((acc, cur) => {
						acc[cur] = variablesRecord[cur]
						return acc
					}, {} as PresetBlockAttributes),
				},
			}),
		}

		setIsLoading(true)

		try {

			const slug = await presetDispatch.addPreset({title: name, attributes: presetAttributes})

			dispatch.batch([
				dispatch.actions.addPreset(slug),
				dispatch.actions.setAttributes({

					/** Unset features that got added to the preset. */
					...includedFeatures.reduce((acc, cur) => {
						acc[cur.id] = undefined
						return acc
					}, {} as TailwindFeatureAttributes),

					...(includedSelectors.length > 0 && {

						/** Retain selectors that didn't get added to the preset. */
						skaBlocksSelectors: selectors.reduce((acc, cur) => {
							if(!includedSelectors.includes(cur)) {
								acc[cur] = skaBlocksSelectors[cur]
							}
							return acc
						}, {} as SelectorsValue),

						/** All selectors got added, reset selectors. */
						...(includedSelectors.length === selectors.length && {
							skaBlocksSelectors: undefined,
						}),
					}),

					...(includedVariables.length > 0 && {

						/** Retain variables that didn't get added to the preset. */
						skaBlocksVariables: {
							...skaBlocksVariables,
							record: variables.reduce((acc, cur) => {
								if(!includedVariables.includes(cur)) {
									acc[cur] = variablesRecord[cur]
								}
								return acc
							}, {} as NonNullable<VariablesValue['record']>),
						},

						/** All variables got added, reset variables. */
						...(includedVariables.length === variables.length && {
							skaBlocksVariables: undefined,
						}),
					}),
				}),
			])

			createSuccessNotice(sprintf(__('Created preset "%s"', 'ska-blocks'), name), {type: 'snackbar'})

		} catch(error: any) {

			const errorMessage = typeof error === 'object' && Object.hasOwnProperty('message') && error?.code !== 'unknown_error'
				? error.message
				: __('An error occurred while saving the preset.', 'ska-blocks')

			createErrorNotice(errorMessage, {type: 'snackbar'})

		} finally {

			setExcludedFeatures([])
			setExcludedSelectors([])
			setPresetName('')
			setIsLoading(false)
			onRequestClose()
		}
	}

	return (
		<Modal
			className='ska-blocks__modal ska-blocks__modal--create-preset'
			title={__('Create preset', 'ska-blocks')}
			onRequestClose={onRequestClose}
		>
			<VStack spacing='4'>
				<ActionInput
					placeholder={__('Preset name…', 'ska-blocks')}
					actionLabel={__('Create', 'ska-blocks')}
					clearLabel={_x('Clear value', 'action-input', 'ska-blocks')}
					value={presetName}
					onChange={setPresetName}
					onClear={() => setPresetName('')}
					onSubmit={onCreatePreset}
					disabled={isLoading}
				/>
				{usedFeatures.length > 0 && (
					<fieldset>
						<legend>{__('Features', 'ska-blocks')}</legend>
						<VStack>
							{usedFeatures.map(({id, label}) => (
								<CheckboxControl
									key={id}
									label={label}
									checked={!excludedFeatures.includes(id)}
									onChange={() => setExcludedFeatures(
										excludedFeatures.includes(id)
											? excludedFeatures.filter(eid => eid !== id)
											: excludedFeatures.concat(id)
									)}
									__nextHasNoMarginBottom
								/>
							))}
						</VStack>
					</fieldset>
				)}
				{selectors.length > 0 && (
					<fieldset>
						<legend>{__('Selectors', 'ska-blocks')}</legend>
						<VStack>
							{selectors.map(selector => (
								<CheckboxControl
									key={selector}
									label={selector}
									checked={!excludedSelectors.includes(selector)}
									onChange={() => setExcludedSelectors(
										excludedSelectors.includes(selector)
											? excludedSelectors.filter(s => s !== selector)
											: excludedSelectors.concat(selector)
									)}
									__nextHasNoMarginBottom
								/>
							))}
						</VStack>
					</fieldset>
				)}
				{variables.length > 0 && (
					<fieldset>
						<legend>{__('Variables', 'ska-blocks')}</legend>
						<VStack>
							{variables.map(variable => (
								<CheckboxControl
									key={variable}
									label={variable}
									checked={!excludedVariables.includes(variable)}
									onChange={() => setExcludedVariables(
										excludedVariables.includes(variable)
											? excludedVariables.filter(v => v !== variable)
											: excludedVariables.concat(variable)
									)}
									__nextHasNoMarginBottom
								/>
							))}
						</VStack>
					</fieldset>
				)}
			</VStack>
		</Modal>
	)
}

export default CreatePreset
