/**
 * WordPress dependencies
 */
/**
 * External dependencies
 */
import * as eslint from 'eslint-linter-browserify';
import { esLint, javascript } from '@codemirror/lang-javascript';
import { linter, lintGutter } from '@codemirror/lint';
import { autocompletion } from '@codemirror/autocomplete';
import CodeMirror, { EditorView } from '@uiw/react-codemirror';
import js from '@eslint/js';
import globals from 'globals';

import { mapKeys, mapValues } from 'lodash';

import type { CSSProperties } from 'react';

/**
 * Internal dependencies
 */
import type { Props } from './props';
import { usePageAttribute } from '@nab/data';

export const JavaScriptEditor = ( {
	value,
	className,
	before,
	after,
	placeholder,
	config: inputConfig,
	readOnly,
	onBlur,
	onChange,
	onCreateEditor,
	onFocus,
}: Props ): JSX.Element => {
	const config = useConfig( inputConfig );
	return (
		<CodeMirror
			className={ className }
			value={ value }
			placeholder={ placeholder }
			indentWithTab={ true }
			{ ...{ indentunit: '  ' } }
			readOnly={ readOnly }
			editable={ ! readOnly }
			theme="light"
			onCreateEditor={ onCreateEditor }
			basicSetup={ {
				lineNumbers: false,
				foldGutter: false,
				highlightActiveLine: ! readOnly,
			} }
			extensions={ [
				EditorView.lineWrapping,
				javascript(),
				...[
					readOnly
						? []
						: [
								autocompletion( {
									override: inputConfig?.completions,
								} ),
								linter( esLint( new eslint.Linter(), config ) ),
								lintGutter(),
						  ],
				],
			] }
			onChange={ readOnly ? undefined : onChange }
			onFocus={ onFocus }
			onBlur={ onBlur }
			style={
				{
					'--nab-code-before': JSON.stringify( before ),
					'--nab-code-after': JSON.stringify( after ),
				} as unknown as CSSProperties
			}
		/>
	);
};

const useConfig = ( config: Props[ 'config' ] ) => {
	const customGlobals = [
		...usePageAttribute( 'components/javascriptGlobals', [] )[ 0 ],
		...( config?.globals ?? [] ),
	];
	return [
		js.configs.recommended,
		{
			plugins: {
				nab: {
					rules: mapValues( config?.rules, ( x ) => x.module ),
				},
			},
		},
		{
			languageOptions: {
				ecmaVersion: 'latest',
				globals: {
					...globals.browser,
					...customGlobals.reduce(
						( r, x ) => {
							r[ x ] = false;
							return r;
						},
						{} as Record< string, false >
					),
				},
			},
			rules: {
				...mapKeys(
					mapValues( config?.rules, ( x ) => x.level ),
					( _, key ) => `nab/${ key }`
				),
				'no-console': 'off',
				'no-shadow': 'warn',
				'no-undef': 'warn',
				'no-unused-vars': 'warn',
				'no-useless-escape': 'off',
				eqeqeq: 'warn',
			},
		},
	];
};
