import * as React from 'react'

import Editor from 'react-simple-code-editor'

import {
	highlight,
	languages,
	// @ts-expect-error
} from 'prismjs/components/prism-core'

import 'prismjs/components/prism-markup-templating'
import 'prismjs/components/prism-markup'

import 'prismjs/components/prism-css'
import 'prismjs/components/prism-php'
import 'prismjs/components/prism-clike' // Dependency for JS
import 'prismjs/components/prism-javascript'
import 'prismjs/components/prism-typescript'
import 'prismjs/components/prism-json'
import 'prismjs/components/prism-jsx'

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

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

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

import cx from 'classnames'

import './style.scss'

let focusTrapTimeout: ReturnType<typeof setTimeout>
const handleKeyDown = (e: KeyboardEvent) => {

	if(e.code === 'Tab') {

		e.preventDefault()

		clearTimeout(focusTrapTimeout)

		/** In a modal the `e.preventDefault` doesn't appear to work, return focus to textarea. Modal can be closed with `esc` to not get trapped permanently. */
		focusTrapTimeout = setTimeout(() => {
			// @ts-ignore
			if(!e.target || !e.target.ownerDocument) {
				return
			}
			// @ts-ignore
			if(e.target.ownerDocument.activeElement !== e.target) {
				// @ts-ignore
				e.target?.focus()
			}
		}, 50)
	}

	/** Don't undo the editor. */
	if(e.key === 'z' && e.ctrlKey) {
		e.stopPropagation()
	}
}

const getEditorElementById = (selector: string) => {

	const editorCanvas: HTMLIFrameElement | null = document.querySelector('iframe[name="editor-canvas"]')
	const doc = editorCanvas ? editorCanvas.contentDocument : document

	return doc?.getElementById(selector)
}

export interface CodeEditorProps extends Omit<React.TextareaHTMLAttributes<HTMLTextAreaElement>, 'onChange'> {
	type?: string
	value: string
	defaultValue?: string
	onChange: (nextValue: string) => void
}

const CodeEditor: React.FC<CodeEditorProps> = ({
	type = 'markup',
	value = '',
	onChange,
	...textareaProps
}) => {

	const id = useInstanceId(CodeEditor, 'ska-code-editor')

	useEffect(() => {

		const el = getEditorElementById(id)
		if(!el) {
			return
		}

		el.addEventListener('keydown', handleKeyDown)

		return () => {
			el.removeEventListener('keydown', handleKeyDown)
		}
	}, [id])

	return <>
		{/* @ts-ignore textareaProps */}
		<Editor
			{...textareaProps}
			className={cx('ska-code-editor', textareaProps.className)}
			value={value}
			onValueChange={onChange}
			highlight={(code = '') => {
				if(type) {
					if(type in languages) {
						return highlight(code, languages[type])
					}
					// eslint-disable-next-line no-console
					console.warn('Prism language not loaded', type, languages)
				}
				return highlight(code, languages['markup'])
			}}
			insertSpaces={false}
			tabSize={1}
			textareaId={id}
			padding={10}
			style={{
				fontFamily: 'Menlo, Consolas, Monaco, Liberation Mono, Lucida Console, monospace',
				fontSize: 14,
				lineHeight: 1.5,
			}}
		/>
	</>
}

interface BaseControlProps {
	label?: string
	hideLabelFromVision?: boolean
	help?: string
}

export const CodeEditorControl: React.FC<BaseControlProps & CodeEditorProps> = props => {

	const {
		label = '',
		hideLabelFromVision = false,
		help = '',
		...codeEditorProps
	} = props

	const id = useInstanceId(CodeEditorControl, 'ska-code-editor-control')

	return (
		<BaseControl
			id={id}
			className='ska-code-editor__wrapper'
			label={label}
			hideLabelFromVision={hideLabelFromVision}
			help={help}
			__nextHasNoMarginBottom
		>
			<CodeEditor {...codeEditorProps} />
		</BaseControl>
	)
}

export default CodeEditor
