import * as React from 'react'
import cx from 'classnames'

import {
	createContext,
	useContext,
	useId,
} from '@wordpress/element'

import {
	BaseControl,
	TextControl,
	Button,
} from '@wordpress/components'

import {
	chevronUp,
	chevronDown,
	plus,
} from '@wordpress/icons'

import {
	ButtonGrid,
	ButtonGridButtonProps,
} from '@ska/components'

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

import './style.scss'

export interface ArrayEditorStrings {
	valuePlaceholder: string
	moveUpLabel: string
	moveDownLabel: string
	removeLabel: string
	addLabel: string
}
export const ARRAY_EDITOR_STRINGS = {
	valuePlaceholder: 'Value…',
	moveUpLabel: 'Move up',
	moveDownLabel: 'Move down',
	removeLabel: 'Remove',
	addLabel: 'Add',
}
export const ArrayEditorStringsContext = createContext<ArrayEditorStrings>(ARRAY_EDITOR_STRINGS)

export type ArrayEditorAction =
	| 'update-row'
	| 'add-row'
	| 'remove-row'
	| 'move-up'
	| 'move-down'

export interface ArrayValueControlProps {
	placeholder?: string
	value: string
	onChange: (nextValue: string) => void
	disabled?: boolean
}

export interface ArrayEditorProps {
	id?: string
	label?: string
	hideLabelFromVision?: boolean
	help?: string
	className?: string
	size?: 'large'
	value: string[]
	defaultValue?: string
	/** Show a "+" button when the provided array is empty (default: true). */
	emptyPlaceholder?: boolean
	onChange?: (nextValue: string[], index: number, action: ArrayEditorAction) => void
	showIndex?: boolean
	ValueControl?: React.FC<ArrayValueControlProps>
	getButtons?: (buttons: ButtonGridButtonProps[], value: string, index: number) => ButtonGridButtonProps[]
	locked?: string[]
}

const noop = () => {}
const defaultGetButtons = (buttons: ButtonGridButtonProps[]) => buttons

const ArrayEditor: React.FC<ArrayEditorProps> = ({
	id: providedId,
	label,
	hideLabelFromVision = false,
	help,
	className,
	size,
	value: array,
	defaultValue,
	emptyPlaceholder = true,
	onChange = noop,
	showIndex = true,
	ValueControl = TextControl,
	getButtons = defaultGetButtons,
	locked = [],
}) => {

	const fallbackId = useId().replaceAll('«', '_').replaceAll('»', '_').replaceAll(':', '_')
	const id = providedId || fallbackId

	const {
		valuePlaceholder,
		moveUpLabel,
		moveDownLabel,
		removeLabel,
		addLabel,
	} = useContext(ArrayEditorStringsContext)

	const updateRow = (index: number, nextValue: string) => {
		onChange([
			...array.slice(0, index),
			nextValue,
			...array.slice(index + 1),
		], index, 'update-row')
	}

	const addRow = (index: number) => {
		onChange([
			...array.slice(0, index + 1),
			defaultValue || '',
			...array.slice(index + 1),
		], index, 'add-row')
	}

	const removeRow = (index: number) => {
		onChange([
			...array.slice(0, index),
			...array.slice(index + 1),
		], index, 'remove-row')
	}

	const moveUp = (index: number) => {
		onChange([
			...array.slice(0, index - 1),
			array[index],
			array[index - 1],
			...array.slice(index + 1),
		], index, 'move-up')
	}

	const moveDown = (index: number) => {
		onChange([
			...array.slice(0, index),
			array[index + 1],
			array[index],
			...array.slice(index + 2),
		], index, 'move-down')
	}

	const keyIdCx = `ska-array-${id}` // Unique ID but BaseControl doesn't support wrapper props so it's passed as a class as well.

	return (
		<BaseControl
			key={keyIdCx}
			id={keyIdCx}
			className={cx('ska-array-editor', {'ska-array-editor--large': size === 'large'}, keyIdCx, className)}
			label={label}
			help={help}
			hideLabelFromVision={hideLabelFromVision}
			__nextHasNoMarginBottom
		>
			{array.length === 0 && emptyPlaceholder && (
				<div>
					<Button
						icon={plus}
						label={addLabel}
						onClick={() => addRow(0)}
					/>
				</div>
			)}
			{array.map((value, index) => {

				const isFirst = index === 0
				const isLast = index + 1 === array.length
				const isOnly = array.length === 1
				const isBlank = !value
				const isLocked = locked.includes(value)
				const hasNextRow = array[index + 1] !== undefined
				const buttons = getButtons([
					{
						key: 'move-up',
						label: moveUpLabel,
						icon: chevronUp,
						onClick: () => moveUp(index),
						disabled: isFirst,
					},
					{
						key: 'remove-row',
						label: removeLabel,
						icon: plus,
						iconProps: {
							style: {
								transform: 'rotate(45deg)',
								pointerEvents: 'none',
							},
						},
						onClick: () => removeRow(index),
						disabled: isLocked || (isOnly && isBlank),
					},
					{
						key: 'move-down',
						label: moveDownLabel,
						icon: chevronDown,
						onClick: () => moveDown(index),
						disabled: isLast,
					},
					{
						key: 'add-row',
						label: addLabel,
						icon: plus,
						onClick: () => addRow(index),
					},
				], value, index)

				return (
					<div
						key={index}
						className='ska-array-editor__row'
						data-index={index}
					>
						{showIndex && (
							<TextControl
								className='ska-array-editor__row__key'
								disabled
								value={index}
								onChange={noop}
								__nextHasNoMarginBottom
								__next40pxDefaultSize
							/>
						)}
						<ValueControl
							placeholder={valuePlaceholder}
							value={value}
							disabled={isLocked}
							onChange={(nextValue: string) => updateRow(index, nextValue)}
							onKeyDown={(e: React.KeyboardEvent) => {

								if(e.code === 'Enter') {
									if(!(isLast && isBlank)) {
										e.preventDefault()
										addRow(index)
										focusElement(`.${keyIdCx} .ska-array-editor__row[data-index="${index + 1}"] input[data-type="value"]`, 150)
									}
								}

								if(e.code === 'Backspace') {
									if(isBlank && !isOnly) {
										e.preventDefault()
										removeRow(index)
										focusElement(`.${keyIdCx} .ska-array-editor__row[data-index="${index - 1}"] input[data-type="value"]`, 150)
									}
								}
							}}
							data-type='value'
							__nextHasNoMarginBottom
							__next40pxDefaultSize
						/>
						<ButtonGrid
							columns={Math.ceil(buttons.length / 2)}
							width={size === 'large' ? 20 : 16}
							buttons={buttons}
						/>
					</div>
				)
			})}
		</BaseControl>
	)
}

export default ArrayEditor
