const {
	EditorView,
	Extension,
	ensureSyntaxTree
} = require( 'ext.CodeMirror.lib' );
const mwModeConfig = require( './codemirror.mediawiki.config.js' );
const { platform } = $.client.profile();

const isMac = platform === 'mac' || platform === 'ipad' || platform === 'iphone',
	modKey = isMac ? 'Meta' : 'Control';

/**
 * Toggle .cm-mw-open-links from all CodeMirror instances.
 *
 * @param {boolean} toggle
 * @private
 */
function toggleOpenLinks( toggle ) {
	for ( const dom of document.querySelectorAll( '.cm-content' ) ) {
		// Use .add() and .remove() instead of .toggle() for safe measure.
		dom.classList[ toggle ? 'add' : 'remove' ]( 'cm-mw-open-links' );
	}
}

document.addEventListener( 'keydown', ( e ) => {
	if ( e.key === modKey ) {
		toggleOpenLinks( true );
	}
} );
document.addEventListener( 'keyup', ( e ) => {
	if ( e.key === modKey ) {
		toggleOpenLinks( false );
	}
} );
// Ensure openLinks classes are removed when switching tabs.
document.addEventListener( 'visibilitychange', () => {
	if ( document.hidden ) {
		toggleOpenLinks( false );
	}
} );

/**
 * CodeMirror extension that opens links by modifier-clicking for the MediaWiki mode.
 * This automatically applied when using {@link CodeMirrorMediaWiki}.
 *
 * @type {Extension}
 * @internal
 * @private
 */
const openLinksExtension = [
	EditorView.domEventHandlers( {
		/**
		 * Handle the mousedown event to open links.
		 *
		 * @param {MouseEvent} e
		 * @param {EditorView} view
		 * @return {boolean}
		 * @private
		 */
		mousedown( e, view ) {
			if ( !( isMac ? e.metaKey : e.ctrlKey ) || e.button !== 0 ) {
				return false;
			}
			const position = view.posAtCoords( e );
			if ( !position ) {
				return false;
			}
			const { state } = view,
				tree = ensureSyntaxTree( state, position ),
				node = tree && tree.resolve( position, 1 );
			if ( !node ) {
				return false;
			}
			const { name, from, to } = node,
				names = name.split( '_' );
			if ( names.includes( mwModeConfig.tags.linkPageName ) ||
				names.includes( mwModeConfig.tags.templateName ) ) {
				let page = state.sliceDoc( from, to ).trim();
				const pageName = mw.config.get( 'wgPageName' );
				if ( page.startsWith( '/' ) ) {
					page = `:${ pageName }${ page }`;
				} else if ( page.startsWith( '../' ) ) {
					const [ { length } ] = /^(?:\.\.\/)*/.exec( page ),
						level = length / 3,
						parts = pageName.split( '/' );
					if ( level >= parts.length ) {
						return false;
					}
					const sub = page.slice( length );
					page = `:${ parts.slice( 0, -level ).join( '/' ) }${ sub && '/' }${ sub }`;
				}
				let ns = names.includes( mwModeConfig.tags.templateName ) ? 10 : 0;
				if ( names.includes( 'mw-tag-gallery' ) && !name.includes( 'link-ground' ) ) {
					ns = 6;
				}
				const title = mw.Title.newFromText( page, ns );
				if ( title ) {
					open( title.getUrl(), '_blank', 'noopener noreferrer' );
					return true;
				}
			} else if ( names.includes( mwModeConfig.tags.extLinkProtocol ) ||
				names.includes( mwModeConfig.tags.freeExtLinkProtocol ) ) {
				open( state.sliceDoc( from, node.nextSibling.to ), '_blank', 'noopener noreferrer' );
				return true;
			} else if ( names.includes( mwModeConfig.tags.extLink ) ||
				names.includes( mwModeConfig.tags.freeExtLink ) ) {
				open( state.sliceDoc( node.prevSibling.from, to ), '_blank', 'noopener noreferrer' );
				return true;
			} else if ( names.includes( mwModeConfig.tags.pageName ) &&
				names.includes( 'mw-ext-templatestyles' ) ) {
				const title = mw.Title.newFromText(
					state.sliceDoc( from, to ).trim(),
					// templateStylesDefaultNamespace is always set in this case
					mw.config.get( 'extCodeMirrorConfig' ).templateStylesDefaultNamespace
				);
				if ( title ) {
					open( title.getUrl(), '_blank', 'noopener noreferrer' );
					return true;
				}
			} else if ( name.includes( mwModeConfig.tags.pageName ) &&
				names.includes( mwModeConfig.tags.parserFunction ) ) {
				const ns = Number( /mw-function-(\d+)/.exec( name )[ 1 ] ),
					title = mw.Title.newFromText( state.sliceDoc( from, to ).trim(), ns );
				if ( title ) {
					open( title.getUrl(), '_blank', 'noopener noreferrer' );
					return true;
				}
			}
			return false;
		}
	} ),
	EditorView.contentAttributes.of( {
		'data-open-links': ''
	} )
];

module.exports = openLinksExtension;
