export type TextSegment = { text: string; highlight: boolean };

export type MatchOffset = { start: number; length: number };

/**
 * Converts a flat string and a list of character-level highlight offsets into
 * an array of text segments that can be rendered without {@html}.
 *
 * Offsets are sorted and merged (overlapping spans become the longest enclosing
 * span) before processing. Out-of-bounds offsets are clamped or dropped.
 *
 * @param text    The display text (no delimiter characters).
 * @param offsets Character offsets produced by the backend (Java char positions,
 *                compatible with JavaScript String indexing).
 */
export function applyOffsets(text: string, offsets: MatchOffset[]): TextSegment[] {
	if (!offsets.length) return [{ text, highlight: false }];

	// Sort by start position and merge overlapping / adjacent spans
	const sorted = [...offsets].sort((a, b) => a.start - b.start);
	const merged: { start: number; end: number }[] = [];
	for (const { start, length } of sorted) {
		const end = start + length;
		if (end <= 0 || start >= text.length) continue; // completely out of bounds
		const clampedStart = Math.max(0, start);
		const clampedEnd = Math.min(text.length, end);
		const last = merged[merged.length - 1];
		if (!last || clampedStart > last.end) {
			merged.push({ start: clampedStart, end: clampedEnd });
		} else {
			last.end = Math.max(last.end, clampedEnd);
		}
	}

	if (!merged.length) return [{ text, highlight: false }];

	const segments: TextSegment[] = [];
	let pos = 0;
	for (const { start, end } of merged) {
		if (pos < start) segments.push({ text: text.slice(pos, start), highlight: false });
		segments.push({ text: text.slice(start, end), highlight: true });
		pos = end;
	}
	if (pos < text.length) segments.push({ text: text.slice(pos), highlight: false });
	return segments;
}
