import * as DiffLib from "diff";
import { COMMENT_TRANSIENT_ELEMENT_KEY, } from "./commentTransientSelection";
import { getSuggestedEditItemLanguage, getSuggestedEditItemVersion, } from "./suggestionDisplayValue";
export function getCommentTextRangeKey(comment) {
    return `comment:${comment.id}`;
}
export function getSuggestedEditTextRangeKey(suggestion) {
    return `suggestion:${suggestion.id}`;
}
export function getCommentTextRange(comment) {
    if (typeof comment.rangeStart !== "number" ||
        typeof comment.rangeEnd !== "number") {
        return null;
    }
    const start = Math.max(0, Math.min(comment.rangeStart, comment.rangeEnd));
    const end = Math.max(start, comment.rangeStart, comment.rangeEnd);
    return end > start ? [start, end] : null;
}
export function getCommentFieldDescriptor(comment) {
    if (!comment.fieldId)
        return undefined;
    return {
        fieldId: comment.fieldId,
        item: {
            id: comment.itemId,
            language: comment.language,
            version: comment.version,
        },
    };
}
export function getCommentTransientElementKey(comment) {
    return comment[COMMENT_TRANSIENT_ELEMENT_KEY];
}
export function getCommentScrollIntoViewTarget(comment) {
    const textRange = getCommentTextRange(comment);
    const fallbackField = getCommentFieldDescriptor(comment);
    const textRangeKey = getCommentTextRangeKey(comment);
    if (fallbackField && textRange) {
        return {
            kind: "textRange",
            key: textRangeKey,
            textRanges: [
                {
                    key: textRangeKey,
                    elementKey: getCommentTransientElementKey(comment),
                    fieldId: fallbackField.fieldId,
                    item: fallbackField.item,
                    start: textRange[0],
                    end: textRange[1],
                },
            ],
            fallbackField,
            fallbackComponentId: comment.itemId,
        };
    }
    if (fallbackField) {
        return {
            kind: "field",
            field: fallbackField,
            fallbackComponentId: comment.itemId,
        };
    }
    return comment.itemId;
}
export function getSuggestedEditFieldDescriptor(suggestion) {
    return {
        fieldId: suggestion.fieldId,
        item: getSuggestedEditItemDescriptor(suggestion),
    };
}
export function getSuggestedEditItemDescriptor(suggestion) {
    return {
        id: suggestion.itemId,
        language: getSuggestedEditItemLanguage(suggestion),
        version: getSuggestedEditItemVersion(suggestion),
    };
}
export function getSuggestionRangeData(oldValue, newValue) {
    const oldText = toPlainSuggestionText(oldValue ?? "");
    const newText = toPlainSuggestionText(newValue ?? "");
    if (oldText === newText) {
        return {
            oldRanges: [],
            newRanges: [],
            oldAnchorOffsets: [],
            newAnchorOffsets: [],
            oldText,
            newText,
        };
    }
    const diffResult = DiffLib.diffChars(oldText, newText);
    const oldChangeRanges = [];
    const newChangeRanges = [];
    const oldAnchorOffsets = [];
    const newAnchorOffsets = [];
    let positionInOld = 0;
    let positionInNew = 0;
    for (const part of diffResult) {
        if (part.removed) {
            oldChangeRanges.push([positionInOld, positionInOld + part.value.length]);
            newAnchorOffsets.push(positionInNew);
            positionInOld += part.value.length;
        }
        else if (part.added) {
            newChangeRanges.push([positionInNew, positionInNew + part.value.length]);
            oldAnchorOffsets.push(positionInOld);
            positionInNew += part.value.length;
        }
        else {
            positionInOld += part.value.length;
            positionInNew += part.value.length;
        }
    }
    return {
        oldRanges: mergeRanges(oldChangeRanges),
        newRanges: mergeRanges(newChangeRanges),
        oldAnchorOffsets,
        newAnchorOffsets,
        oldText,
        newText,
    };
}
export function getSuggestionDisplayRanges(rangeData, value) {
    const changeRanges = value === "old" ? rangeData.oldRanges : rangeData.newRanges;
    const anchorOffsets = value === "old" ? rangeData.oldAnchorOffsets : rangeData.newAnchorOffsets;
    const uncoveredAnchorOffsets = Array.from(new Set(anchorOffsets)).filter((offset) => !changeRanges.some(([start, end]) => offset >= start && offset <= end));
    return [
        ...changeRanges,
        ...uncoveredAnchorOffsets.map((offset) => [offset, offset]),
    ].sort(([leftStart], [rightStart]) => leftStart - rightStart);
}
export function getSuggestedEditScrollIntoViewTarget(suggestion) {
    const fallbackField = getSuggestedEditFieldDescriptor(suggestion);
    const textRangeKey = getSuggestedEditTextRangeKey(suggestion);
    const rangeData = getSuggestionRangeData(suggestion.oldValue, suggestion.newValue);
    const ranges = rangeData.newRanges.length > 0 ? rangeData.newRanges : rangeData.oldRanges;
    if (ranges.length > 0) {
        return {
            kind: "textRange",
            key: textRangeKey,
            textRanges: ranges.map(([start, end]) => ({
                key: textRangeKey,
                fieldId: suggestion.fieldId,
                item: fallbackField.item,
                start,
                end,
            })),
            fallbackField,
            fallbackComponentId: suggestion.itemId,
        };
    }
    return {
        kind: "field",
        field: fallbackField,
        fallbackComponentId: suggestion.itemId,
    };
}
function toPlainSuggestionText(value) {
    if (!value || !/[<&]/.test(value))
        return value || "";
    if (typeof document === "undefined") {
        return value.replace(/<[^>]*>/g, "");
    }
    const template = document.createElement("template");
    template.innerHTML = value;
    return template.content.textContent || "";
}
function mergeRanges(changeRanges) {
    const mergedRanges = [];
    if (changeRanges.length === 0)
        return mergedRanges;
    changeRanges.sort((a, b) => a[0] - b[0]);
    let currentRange = [...changeRanges[0]];
    for (let i = 1; i < changeRanges.length; i++) {
        const nextRange = changeRanges[i];
        if (!nextRange)
            continue;
        if (nextRange[0] <= currentRange[1] + 3) {
            currentRange[1] = Math.max(currentRange[1], nextRange[1]);
        }
        else {
            mergedRanges.push(currentRange);
            currentRange = [...nextRange];
        }
    }
    mergedRanges.push(currentRange);
    return mergedRanges;
}
//# sourceMappingURL=feedbackScrollTarget.js.map