import makeInlineReplaceExtension from './utils/makeInlineReplaceExtension';
import { SyntaxNodeRef } from '@lezer/common';
import { EditorState } from '@codemirror/state';
import referenceLinkStateField, { isReferenceLink, resolveReferenceFromLink } from '../links/referenceLinksStateField';
import { Decoration } from '@codemirror/view';

const shouldFullReplace = (node: SyntaxNodeRef, state: EditorState) => {
	const getParentName = () => node.node.parent?.name;
	const getNodeStartLine = () => state.doc.lineAt(node.from);

	if (['HeaderMark', 'CodeMark', 'EmphasisMark', 'StrikethroughMark', 'HighlightMarker', 'InsertMarker'].includes(node.name)) {
		return true;
	}

	if ((node.name === 'URL' || node.name === 'LinkMark') && getParentName() === 'Link') {
		const parent = node.node.parent!;
		const parentContent = state.sliceDoc(parent.from, parent.to);
		if (node.name === 'LinkMark') {
			if (isReferenceLink(parentContent)) {
				return !!resolveReferenceFromLink(parentContent, state);
			}
		} else if (node.name === 'URL') {
			// Find all closing link marks
			const closingBracketNodes = parent.getChildren('LinkMark').filter(mark => {
				const isClosingBracket = state.sliceDoc(mark.from, mark.to) === ']';
				return isClosingBracket;
			});

			// URLs can only be hidden if after the last ].
			const lastClosingBracketIdx = closingBracketNodes.length > 0 ? closingBracketNodes[closingBracketNodes.length - 1].from : null;
			if (!lastClosingBracketIdx || node.from < lastClosingBracketIdx) {
				return false;
			}
		}
		return true;
	}

	if (node.name === 'QuoteMark' && node.from === getNodeStartLine().from) {
		return true;
	}

	return false;
};

const hideDecoration = Decoration.replace({});

const replaceFormatCharacters = [
	// Dependency
	referenceLinkStateField,

	makeInlineReplaceExtension({
		getRevealStrategy: (node) => {
			if (node.name === 'QuoteMark') {
				return 'line';
			}
			if (node.name === 'CodeMark') {
				if (node.node.parent?.name === 'FencedCode') {
					return 'line';
				}
			}

			return 'active';
		},
		createDecoration: (node, state) => {
			if (shouldFullReplace(node, state)) {
				return hideDecoration;
			}
			return null;
		},
		getDecorationRange: (node, state) => {
			// Headers in the form "## Header" should have the "##"s and the
			// space immediately after hidden
			if (node.name === 'HeaderMark') {
				const markerLine = state.doc.lineAt(node.from);

				// Certain header styles DON'T have a space after the header mark:
				const hasRoomForSpace = node.to + 1 >= markerLine.to;
				if (hasRoomForSpace) {
					return null;
				}

				// Include the space in the hidden region, if it's available
				if (state.doc.sliceString(node.to, node.to + 1) === ' ') {
					return [node.from, node.to + 1];
				}
			}

			return null;
		},
	}),
];

export default replaceFormatCharacters;
