//#region src/shared/text/code-regions.ts
function findCodeRegions(text) {
	const regions = [];
	for (const match of text.matchAll(/(^|\n)(```|~~~)[^\n]*\n[\s\S]*?(?:\n\2|$)/g)) {
		const start = (match.index ?? 0) + match[1].length;
		regions.push({
			start,
			end: start + match[0].length - match[1].length
		});
	}
	for (const match of text.matchAll(/`+[^`]+`+/g)) {
		const start = match.index ?? 0;
		const end = start + match[0].length;
		if (!regions.some((r) => start >= r.start && end <= r.end)) regions.push({
			start,
			end
		});
	}
	regions.sort((a, b) => a.start - b.start);
	return regions;
}
function isInsideCode(pos, regions) {
	return regions.some((r) => pos >= r.start && pos < r.end);
}
//#endregion
//#region src/shared/text/final-tags.ts
const FINAL_TAG_CANDIDATE_RE = /<[^<>]*>/g;
function isWhitespace(char) {
	return /\s/.test(char);
}
function parseAttributeList(text) {
	let index = 0;
	while (index < text.length) {
		while (index < text.length && isWhitespace(text[index] ?? "")) index += 1;
		if (index >= text.length) return true;
		const nameStart = index;
		while (index < text.length) {
			const char = text[index] ?? "";
			if (isWhitespace(char) || char === "=") break;
			if (char === "/" || char === "\"" || char === "'" || char === "<" || char === ">") return false;
			index += 1;
		}
		if (index === nameStart) return false;
		while (index < text.length && isWhitespace(text[index] ?? "")) index += 1;
		if (text[index] !== "=") continue;
		index += 1;
		while (index < text.length && isWhitespace(text[index] ?? "")) index += 1;
		if (index >= text.length) return false;
		const quote = text[index];
		if (quote === "\"" || quote === "'") {
			index += 1;
			const end = text.indexOf(quote, index);
			if (end === -1) return false;
			index = end + 1;
			continue;
		}
		const valueStart = index;
		while (index < text.length && !isWhitespace(text[index] ?? "")) {
			const char = text[index] ?? "";
			if (char === "\"" || char === "'" || char === "<" || char === ">") return false;
			index += 1;
		}
		if (index === valueStart) return false;
	}
	return true;
}
function parseFinalTag(text) {
	if (!text.startsWith("<") || !text.endsWith(">")) return null;
	let body = text.slice(1, -1).trimStart();
	let isClose = false;
	if (body.startsWith("/")) {
		isClose = true;
		body = body.slice(1).trimStart();
	}
	if (!body.toLowerCase().startsWith("final")) return null;
	const boundary = body[5] ?? "";
	if (boundary && !isWhitespace(boundary) && boundary !== "/") return null;
	let rest = body.slice(5);
	if (isClose) return rest.trim().length === 0 ? {
		isClose: true,
		isSelfClosing: false
	} : null;
	const trimmedRest = rest.trimEnd();
	const isSelfClosing = trimmedRest.endsWith("/");
	rest = isSelfClosing ? trimmedRest.slice(0, -1) : rest;
	if (!parseAttributeList(rest)) return null;
	return {
		isClose: false,
		isSelfClosing
	};
}
function findFinalTagMatches(text) {
	const matches = [];
	for (const match of text.matchAll(FINAL_TAG_CANDIDATE_RE)) {
		const tagText = match[0];
		const parsed = parseFinalTag(tagText);
		if (!parsed) continue;
		matches.push({
			index: match.index ?? 0,
			text: tagText,
			...parsed
		});
	}
	return matches;
}
function stripFinalTags(text) {
	let output = "";
	let lastIndex = 0;
	for (const match of findFinalTagMatches(text)) {
		output += text.slice(lastIndex, match.index);
		lastIndex = match.index + match.text.length;
	}
	output += text.slice(lastIndex);
	return output;
}
//#endregion
//#region src/shared/text/reasoning-tags.ts
const QUICK_TAG_RE = /<\s*\/?\s*(?:(?:antml:)?(?:think(?:ing)?|thought)|antthinking|final)\b/i;
const THINKING_TAG_RE = /<\s*(\/?)\s*(?:(?:antml:)?(?:think(?:ing)?|thought)|antthinking)\b[^<>]*>/gi;
function applyTrim(value, mode) {
	if (mode === "none") return value;
	if (mode === "start") return value.trimStart();
	return value.trim();
}
function hasOrphanReasoningCloseBoundary(params) {
	return params.before.trim().length > 0 && params.after.trim().length > 0;
}
function stripReasoningTagsFromText(text, options) {
	if (!text) return text;
	if (!QUICK_TAG_RE.test(text)) return text;
	const mode = options?.mode ?? "strict";
	const trimMode = options?.trim ?? "both";
	let cleaned = text;
	const matches = findFinalTagMatches(cleaned);
	THINKING_TAG_RE.lastIndex = 0;
	const hasThinkingTag = THINKING_TAG_RE.test(cleaned);
	THINKING_TAG_RE.lastIndex = 0;
	if (matches.length === 0 && !hasThinkingTag) return text;
	if (matches.length > 0) {
		const finalMatches = [];
		const preCodeRegions = findCodeRegions(cleaned);
		for (const match of matches) {
			const start = match.index;
			finalMatches.push({
				start,
				length: match.text.length,
				inCode: isInsideCode(start, preCodeRegions)
			});
		}
		for (let i = finalMatches.length - 1; i >= 0; i--) {
			const m = finalMatches[i];
			if (!m.inCode) cleaned = cleaned.slice(0, m.start) + cleaned.slice(m.start + m.length);
		}
	}
	const codeRegions = findCodeRegions(cleaned);
	THINKING_TAG_RE.lastIndex = 0;
	let result = "";
	let lastIndex = 0;
	let thinkingDepth = 0;
	let firstUnclosedContentIndex;
	for (const match of cleaned.matchAll(THINKING_TAG_RE)) {
		const idx = match.index ?? 0;
		const isClose = match[1] === "/";
		if (isInsideCode(idx, codeRegions)) continue;
		if (thinkingDepth === 0) {
			if (isClose) {
				const afterIndex = idx + match[0].length;
				const before = cleaned.slice(lastIndex, idx);
				if (hasOrphanReasoningCloseBoundary({
					before,
					after: cleaned.slice(afterIndex)
				})) result = "";
				else result += before;
				lastIndex = afterIndex;
				continue;
			}
			result += cleaned.slice(lastIndex, idx);
			thinkingDepth = 1;
			firstUnclosedContentIndex = idx + match[0].length;
		} else if (isClose) {
			thinkingDepth -= 1;
			if (thinkingDepth === 0) firstUnclosedContentIndex = void 0;
		} else thinkingDepth += 1;
		lastIndex = idx + match[0].length;
	}
	if (thinkingDepth === 0 || mode === "preserve") result += cleaned.slice(lastIndex);
	const trimmedResult = applyTrim(result, trimMode);
	if (mode === "strict" && thinkingDepth > 0 && !trimmedResult && firstUnclosedContentIndex !== void 0 && cleaned.trim()) return applyTrim(cleaned.slice(firstUnclosedContentIndex), trimMode);
	return trimmedResult;
}
//#endregion
export { findCodeRegions as a, stripFinalTags as i, stripReasoningTagsFromText as n, isInsideCode as o, findFinalTagMatches as r, hasOrphanReasoningCloseBoundary as t };
