const {
	defaultHighlightStyle,
	oneDarkHighlightStyle,
	tags,
	Config,
	EditorView,
	Extension,
	Language,
	LanguageSupport,
	LintSource
} = require( 'ext.CodeMirror.lib' );
const CodeMirrorWorker = require( '../workers/codemirror.worker.js' );

/* eslint-disable jsdoc/valid-types */
/**
 * Abstract interface for CodeMirror modes.
 *
 * Clients can unpack individual modes from the
 * {@link module:ext.CodeMirror.modes ext.CodeMirror.modes} module.
 * Each mode is exposed as a method with the same name as the mode in the form of a
 * {@link LanguageSupport}-like instance that is consumable by {@link CodeMirror}.
 *
 * @example
 * const CodeMirror = require( 'ext.CodeMirror' );
 * const { javascript } = require( 'ext.CodeMirror.modes' );
 * const cm = new CodeMirror( myTextArea, javascript() );
 * cm.initialize();
 * @implements LanguageSupport
 */
class CodeMirrorMode {
	/* eslint-enable jsdoc/valid-types */

	/**
	 * @param {string} name
	 * @internal
	 */
	constructor( name ) {
		/**
		 * The name of the mode.
		 *
		 * @type {string}
		 */
		this.name = name;

		/**
		 * The web worker for the mode, if any.
		 *
		 * @type {CodeMirrorWorker|undefined}
		 */
		this.worker = this.hasWorker ? new CodeMirrorWorker( this.name ) : undefined;
	}

	/**
	 * The complete set of extensions for this mode, including the language and any
	 * supporting extensions.
	 *
	 * @return {Extension}
	 */
	get extension() {
		return [ this.language, this.support ];
	}

	/**
	 * The language object.
	 *
	 * @type {Language}
	 */
	get language() {
		throw new Error( 'Not implemented' );
	}

	/**
	 * The function to lint the code in the editor.
	 *
	 * @type {LintSource|undefined}
	 */
	get lintSource() {
		return undefined;
	}

	/**
	 * The function to lint the code in the editor using a MediaWiki API.
	 *
	 * @type {LintSource|undefined}
	 */
	get lintApi() {
		return undefined;
	}

	/**
	 * Whether the mode should load a web worker.
	 *
	 * @return {boolean}
	 */
	get hasWorker() {
		return !!this.lintSource;
	}

	/**
	 * The configuration for bracket matching.
	 *
	 * @type {Config|undefined}
	 */
	get bracketMatchingConfig() {
		return undefined;
	}

	/**
	 * Supporting extensions.
	 *
	 * @type {Extension}
	 */
	get support() {
		return [];
	}

	/**
	 * This extension adds extra highlighting styles for doc tags in JavaScript/Lua.
	 *
	 * @type {Extension}
	 * @protected
	 * @internal
	 */
	get docTagExtension() {
		const getColor = ( style, target ) => style.specs.find(
			( { tag } ) => tag === target || Array.isArray( tag ) && tag.includes( target )
		).color;
		const doctag = tags.labelName;
		const doctagType = tags.typeName;
		const doctagVar = tags.special( tags.variableName );

		return EditorView.baseTheme( {
			'&light .cm-doctag > *': {
				color: getColor( defaultHighlightStyle, doctag )
			},
			'&dark .cm-doctag > *': {
				color: getColor( oneDarkHighlightStyle, doctag )
			},
			'&light .cm-doctag-type > *': {
				color: getColor( defaultHighlightStyle, doctagType )
			},
			'&dark .cm-doctag-type > *': {
				color: getColor( oneDarkHighlightStyle, doctagType )
			},
			'&light .cm-doctag-var > *': {
				color: getColor( defaultHighlightStyle, doctagVar )
			},
			'&dark .cm-doctag-var > *': {
				color: getColor( oneDarkHighlightStyle, doctagVar )
			}
		} );
	}
}

module.exports = CodeMirrorMode;