import { DEFAULT_THEMES } from "../constants.js";
import { getFiletypeFromFileName } from "./getFiletypeFromFileName.js";
import { cleanLastNewline } from "./cleanLastNewline.js";
import { createTransformerWithState } from "./createTransformerWithState.js";
import { formatCSSVariablePrefix } from "./formatCSSVariablePrefix.js";
import { getHighlighterThemeStyles } from "./getHighlighterThemeStyles.js";
import { getLineNodes } from "./getLineNodes.js";
import { createDiffSpanDecoration, pushOrJoinSpan } from "./parseDiffDecorations.js";
import { diffChars, diffWordsWithSpace } from "diff";

//#region src/utils/renderDiffWithHighlighter.ts
function renderDiffWithHighlighter(diff, highlighter, options, forcePlainText = false) {
	const baseThemeType = (() => {
		const theme = options.theme ?? DEFAULT_THEMES;
		if (typeof theme === "string") return highlighter.getTheme(theme).type;
	})();
	const themeStyles = getHighlighterThemeStyles({
		theme: options.theme,
		highlighter
	});
	if (diff.newLines != null && diff.oldLines != null) {
		const { oldContent, newContent, oldInfo, newInfo, oldDecorations, newDecorations } = processLines({
			hunks: diff.hunks,
			oldLines: diff.oldLines,
			newLines: diff.newLines,
			lineDiffType: options.lineDiffType
		});
		return {
			code: renderTwoFiles({
				oldFile: {
					name: diff.prevName ?? diff.name,
					contents: oldContent
				},
				oldInfo,
				oldDecorations,
				newFile: {
					name: diff.name,
					contents: newContent
				},
				newInfo,
				newDecorations,
				highlighter,
				options,
				languageOverride: forcePlainText ? "text" : diff.lang
			}),
			themeStyles,
			baseThemeType
		};
	}
	const hunks = [];
	let lineIndex = 0;
	for (const hunk of diff.hunks) {
		const { oldContent, newContent, oldInfo, newInfo, oldDecorations, newDecorations, lineIndex: newLineIndex } = processLines({
			hunks: [hunk],
			lineIndex,
			lineDiffType: options.lineDiffType
		});
		const oldFile = {
			name: diff.prevName ?? diff.name,
			contents: oldContent
		};
		const newFile = {
			name: diff.name,
			contents: newContent
		};
		hunks.push(renderTwoFiles({
			oldFile,
			oldInfo,
			oldDecorations,
			newFile,
			newInfo,
			newDecorations,
			highlighter,
			options,
			languageOverride: forcePlainText ? "text" : diff.lang
		}));
		lineIndex = newLineIndex;
	}
	return {
		code: (() => {
			if (hunks.length <= 1) {
				const hunk = hunks[0] ?? {
					oldLines: [],
					newLines: []
				};
				if (hunk.newLines.length === 0 || hunk.oldLines.length === 0) return hunk;
			}
			return { hunks };
		})(),
		themeStyles,
		baseThemeType
	};
}
function computeLineDiffDecorations({ oldLine, newLine, oldLineIndex, newLineIndex, oldDecorations, newDecorations, lineDiffType }) {
	if (oldLine == null || newLine == null || lineDiffType === "none") return;
	oldLine = cleanLastNewline(oldLine);
	newLine = cleanLastNewline(newLine);
	const lineDiff = lineDiffType === "char" ? diffChars(oldLine, newLine) : diffWordsWithSpace(oldLine, newLine);
	const deletionSpans = [];
	const additionSpans = [];
	const enableJoin = lineDiffType === "word-alt";
	for (const item of lineDiff) {
		const isLastItem = item === lineDiff[lineDiff.length - 1];
		if (!item.added && !item.removed) {
			pushOrJoinSpan({
				item,
				arr: deletionSpans,
				enableJoin,
				isNeutral: true,
				isLastItem
			});
			pushOrJoinSpan({
				item,
				arr: additionSpans,
				enableJoin,
				isNeutral: true,
				isLastItem
			});
		} else if (item.removed) pushOrJoinSpan({
			item,
			arr: deletionSpans,
			enableJoin,
			isLastItem
		});
		else pushOrJoinSpan({
			item,
			arr: additionSpans,
			enableJoin,
			isLastItem
		});
	}
	let spanIndex = 0;
	for (const span of deletionSpans) {
		if (span[0] === 1) oldDecorations.push(createDiffSpanDecoration({
			line: oldLineIndex - 1,
			spanStart: spanIndex,
			spanLength: span[1].length
		}));
		spanIndex += span[1].length;
	}
	spanIndex = 0;
	for (const span of additionSpans) {
		if (span[0] === 1) newDecorations.push(createDiffSpanDecoration({
			line: newLineIndex - 1,
			spanStart: spanIndex,
			spanLength: span[1].length
		}));
		spanIndex += span[1].length;
	}
}
function processLines({ hunks, oldLines, newLines, lineIndex = 0, lineDiffType }) {
	const oldInfo = {};
	const newInfo = {};
	const oldDecorations = [];
	const newDecorations = [];
	let newLineIndex = 1;
	let oldLineIndex = 1;
	let newLineNumber = 1;
	let oldLineNumber = 1;
	let oldContent = "";
	let newContent = "";
	for (const hunk of hunks) {
		while (oldLines != null && newLines != null && newLineIndex < hunk.additionStart && oldLineIndex < hunk.deletionStart) {
			oldInfo[oldLineIndex] = {
				type: "context-expanded",
				lineNumber: oldLineNumber,
				lineIndex
			};
			newInfo[newLineIndex] = {
				type: "context-expanded",
				lineNumber: newLineNumber,
				lineIndex
			};
			oldContent += oldLines[oldLineIndex - 1];
			newContent += newLines[newLineIndex - 1];
			oldLineIndex++;
			newLineIndex++;
			oldLineNumber++;
			newLineNumber++;
			lineIndex++;
		}
		oldLineNumber = hunk.deletionStart;
		newLineNumber = hunk.additionStart;
		for (const hunkContent of hunk.hunkContent) if (hunkContent.type === "context") for (const line of hunkContent.lines) {
			oldInfo[oldLineIndex] = {
				type: "context",
				lineNumber: oldLineNumber,
				lineIndex
			};
			newInfo[newLineIndex] = {
				type: "context",
				lineNumber: newLineNumber,
				lineIndex
			};
			oldContent += line;
			newContent += line;
			oldLineIndex++;
			newLineIndex++;
			newLineNumber++;
			oldLineNumber++;
			lineIndex++;
		}
		else {
			const len = Math.max(hunkContent.additions.length, hunkContent.deletions.length);
			let i = 0;
			while (i < len) {
				const oldLine = hunkContent.deletions[i];
				const newLine = hunkContent.additions[i];
				computeLineDiffDecorations({
					newLine,
					oldLine,
					oldLineIndex,
					newLineIndex,
					oldDecorations,
					newDecorations,
					lineDiffType
				});
				if (oldLine != null) {
					oldInfo[oldLineIndex] = {
						type: "change-deletion",
						lineNumber: oldLineNumber,
						lineIndex
					};
					oldContent += oldLine;
					oldLineIndex++;
					oldLineNumber++;
				}
				if (newLine != null) {
					newInfo[newLineIndex] = {
						type: "change-addition",
						lineNumber: newLineNumber,
						lineIndex
					};
					newContent += newLine;
					newLineIndex++;
					newLineNumber++;
				}
				lineIndex++;
				i++;
			}
		}
		if (oldLines == null || newLines == null || hunk !== hunks[hunks.length - 1]) continue;
		while (oldLineIndex <= oldLines.length || newLineIndex <= oldLines.length) {
			const oldLine = oldLines[oldLineIndex - 1];
			const newLine = newLines[newLineIndex - 1];
			if (oldLine == null && newLine == null) break;
			if (oldLine != null) {
				oldInfo[oldLineIndex] = {
					type: "context-expanded",
					lineNumber: oldLineNumber,
					lineIndex
				};
				oldContent += oldLine;
				oldLineIndex++;
				oldLineNumber++;
			}
			if (newLine != null) {
				newInfo[newLineIndex] = {
					type: "context-expanded",
					lineNumber: newLineNumber,
					lineIndex
				};
				newContent += newLine;
				newLineIndex++;
				newLineNumber++;
			}
			lineIndex++;
		}
	}
	return {
		oldContent: cleanLastNewline(oldContent),
		newContent: cleanLastNewline(newContent),
		oldInfo,
		newInfo,
		oldDecorations,
		newDecorations,
		lineIndex
	};
}
function renderTwoFiles({ oldFile, newFile, oldInfo, newInfo, highlighter, oldDecorations, newDecorations, languageOverride, options: { theme: themeOrThemes = DEFAULT_THEMES,...options } }) {
	const oldLang = languageOverride ?? getFiletypeFromFileName(oldFile.name);
	const newLang = languageOverride ?? getFiletypeFromFileName(newFile.name);
	const { state, transformers } = createTransformerWithState();
	const hastConfig = (() => {
		return typeof themeOrThemes === "string" ? {
			...options,
			lang: "text",
			theme: themeOrThemes,
			transformers,
			decorations: void 0,
			defaultColor: false,
			cssVariablePrefix: formatCSSVariablePrefix()
		} : {
			...options,
			lang: "text",
			themes: themeOrThemes,
			transformers,
			decorations: void 0,
			defaultColor: false,
			cssVariablePrefix: formatCSSVariablePrefix()
		};
	})();
	return {
		oldLines: (() => {
			if (oldFile.contents === "") return [];
			hastConfig.lang = oldLang;
			state.lineInfo = oldInfo;
			hastConfig.decorations = oldDecorations;
			return getLineNodes(highlighter.codeToHast(oldFile.contents, hastConfig));
		})(),
		newLines: (() => {
			if (newFile.contents === "") return [];
			hastConfig.lang = newLang;
			hastConfig.decorations = newDecorations;
			state.lineInfo = newInfo;
			return getLineNodes(highlighter.codeToHast(newFile.contents, hastConfig));
		})()
	};
}

//#endregion
export { renderDiffWithHighlighter };
//# sourceMappingURL=renderDiffWithHighlighter.js.map