import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
import { useEffect, useMemo, useRef, useState } from "react";
import { useEditContext, useFieldsEditContext } from "../client/editContext";
import { SuggestionDisplayPopover } from "../reviews/SuggestionDisplayPopover";
import { measureBridgeTextRangeRects, rectListEquals, } from "./overlay/geometry";
import { useOverlayMeasurement } from "./overlay/useOverlayMeasurement";
import { dedupeFeedbackHighlightItems, FeedbackHighlightBadge, FeedbackHighlightBadgeTray, } from "./FeedbackHighlightBadge";
import { getSuggestedEditScrollIntoViewTarget, getSuggestedEditTextRangeKey, getSuggestionDisplayRanges, getSuggestionRangeData, } from "../reviews/feedbackScrollTarget";
import { getSuggestedEditItemLanguage, getSuggestedEditItemVersion, getSuggestionDisplayValue, getSuggestionFieldIdentityKey, getSuggestionSelectionKey, } from "../reviews/suggestionDisplayValue";
import { SuggestionVariantSwitcher } from "./SuggestionVariantSwitcher";
const SUGGESTION_BRIDGE_SOURCE = "suggestions";
function suggestionMeasurementsEqual(previous, next) {
    const previousKeys = Object.keys(previous);
    const nextKeys = Object.keys(next);
    if (previousKeys.length !== nextKeys.length)
        return false;
    for (const key of previousKeys) {
        const previousValue = previous[key];
        const nextValue = next[key];
        if (!previousValue || !nextValue)
            return false;
        if (previousValue.isTextRange !== nextValue.isTextRange)
            return false;
        if (!rectListEquals(previousValue.rects, nextValue.rects))
            return false;
    }
    return true;
}
export function SuggestionHighlightings({ pageViewContext, variant = "default", }) {
    const editContext = useEditContext();
    const modifiedFields = useFieldsEditContext();
    const lastMeasurementsRef = useRef({});
    const bridgeTextRangeRequestRef = useRef({
        signature: "",
        requestedSignature: "",
        hasSeenAnyTarget: false,
    });
    const [rangeDataBySuggestionId, setRangeDataBySuggestionId] = useState({});
    const [fieldValueByIdentity, setFieldValueByIdentity] = useState({});
    const [fieldIdentityBySuggestionId, setFieldIdentityBySuggestionId] = useState({});
    const previewSuggestedText = editContext?.mode === "suggestions";
    const suggestions = useMemo(() => (editContext?.suggestedEdits || []).filter((suggestion) => suggestion.status !== "applied" && suggestion.status !== "rejected"), [editContext?.suggestedEdits]);
    useEffect(() => {
        if (!editContext) {
            setRangeDataBySuggestionId({});
            setFieldValueByIdentity({});
            setFieldIdentityBySuggestionId({});
            return;
        }
        let cancelled = false;
        const resolveRanges = async () => {
            const nextRangeData = {};
            const nextFieldValues = {};
            const nextFieldIdentities = {};
            await Promise.all(suggestions.map(async (suggestion) => {
                if (!suggestion.fieldId) {
                    nextRangeData[suggestion.id] = {
                        oldRanges: [],
                        newRanges: [],
                        oldAnchorOffsets: [],
                        newAnchorOffsets: [],
                        oldText: "",
                        newText: "",
                    };
                    return;
                }
                const language = getSuggestedEditItemLanguage(suggestion);
                const version = getSuggestedEditItemVersion(suggestion) ?? 0;
                const field = await editContext.itemsRepository.getField({
                    item: {
                        id: suggestion.itemId,
                        language,
                        version,
                    },
                    fieldId: suggestion.fieldId,
                });
                const resolvedItem = field?.descriptor?.item ?? {
                    id: suggestion.itemId,
                    language,
                    version,
                };
                const identityKey = getSuggestionFieldIdentityKey({
                    itemId: resolvedItem.id,
                    language: resolvedItem.language,
                    version: resolvedItem.version,
                    fieldId: suggestion.fieldId,
                });
                nextFieldIdentities[suggestion.id] = identityKey;
                nextFieldValues[identityKey] = {
                    rawValue: field?.rawValue ?? "",
                    itemId: resolvedItem.id,
                    language: resolvedItem.language,
                    version: resolvedItem.version,
                    fieldId: suggestion.fieldId,
                };
                nextRangeData[suggestion.id] = getSuggestionRangeData(suggestion.oldValue ?? field?.rawValue ?? "", suggestion.newValue ?? "");
            }));
            if (!cancelled) {
                setRangeDataBySuggestionId(nextRangeData);
                setFieldValueByIdentity(nextFieldValues);
                setFieldIdentityBySuggestionId(nextFieldIdentities);
            }
        };
        resolveRanges();
        return () => {
            cancelled = true;
        };
    }, [editContext, suggestions]);
    const bridgeTextRanges = useMemo(() => suggestions.flatMap((suggestion) => {
        if (!suggestion.fieldId)
            return [];
        const rangeData = rangeDataBySuggestionId[suggestion.id];
        if (!rangeData)
            return [];
        const ranges = getSuggestionDisplayRanges(rangeData, previewSuggestedText ? "new" : "old");
        const resolvedField = fieldValueByIdentity[fieldIdentityBySuggestionId[suggestion.id]];
        return ranges.map(([start, end]) => ({
            key: getSuggestedEditTextRangeKey(suggestion),
            fieldId: suggestion.fieldId,
            item: {
                id: resolvedField?.itemId ?? suggestion.itemId,
                language: resolvedField?.language ??
                    getSuggestedEditItemLanguage(suggestion),
                version: resolvedField?.version ??
                    getSuggestedEditItemVersion(suggestion) ??
                    0,
            },
            start,
            end,
        }));
    }), [
        fieldIdentityBySuggestionId,
        fieldValueByIdentity,
        previewSuggestedText,
        rangeDataBySuggestionId,
        suggestions,
    ]);
    const bridgeTextRangeSignature = useMemo(() => bridgeTextRanges
        .map((range) => [
        range.key,
        range.fieldId,
        range.item.id,
        range.item.language,
        range.item.version,
        range.start,
        range.end,
    ].join(":"))
        .join("|"), [bridgeTextRanges]);
    useEffect(() => {
        const requestState = bridgeTextRangeRequestRef.current;
        if (!pageViewContext.bridgeReady || bridgeTextRanges.length === 0) {
            if (pageViewContext.bridgeReady && requestState.signature !== "") {
                // Drop our contribution from the shared union so the highlights clear
                // instead of lingering as phantom geometry targets.
                pageViewContext.requestBridgeGeometry?.({
                    source: SUGGESTION_BRIDGE_SOURCE,
                    textRanges: [],
                });
            }
            requestState.signature = "";
            requestState.requestedSignature = "";
            requestState.hasSeenAnyTarget = false;
            return;
        }
        const signatureChanged = requestState.signature !== bridgeTextRangeSignature;
        if (signatureChanged) {
            requestState.signature = bridgeTextRangeSignature;
            requestState.requestedSignature = "";
            requestState.hasSeenAnyTarget = false;
        }
        const bridgeGeometry = pageViewContext.bridgeGeometry;
        const hasAnyTarget = hasAnyExpectedBridgeTextRange(bridgeGeometry, bridgeTextRanges);
        const hasAllTargets = hasAllExpectedBridgeTextRanges(bridgeGeometry, bridgeTextRanges);
        if (hasAnyTarget) {
            requestState.hasSeenAnyTarget = true;
        }
        if (hasAllTargets && !signatureChanged) {
            requestState.requestedSignature = "";
            return;
        }
        const shouldRequest = signatureChanged ||
            requestState.requestedSignature !== bridgeTextRangeSignature ||
            (requestState.hasSeenAnyTarget && !hasAnyTarget);
        if (!shouldRequest)
            return;
        requestState.requestedSignature = bridgeTextRangeSignature;
        pageViewContext.requestBridgeGeometry?.({
            source: SUGGESTION_BRIDGE_SOURCE,
            textRanges: bridgeTextRanges,
        });
    }, [
        pageViewContext,
        pageViewContext.bridgeReady,
        pageViewContext.bridgeGeometry,
        bridgeTextRanges,
        bridgeTextRangeSignature,
    ]);
    const measurements = useOverlayMeasurement((metrics, reasons) => {
        const bridgeGeometry = pageViewContext.bridgeGeometry;
        if (!bridgeGeometry)
            return {};
        const canReusePreviousMeasurement = !reasons.has("viewport") &&
            !reasons.has("layout") &&
            !reasons.has("text");
        const nextMeasurements = {};
        for (const suggestion of suggestions) {
            if (!suggestion.fieldId)
                continue;
            const rangeData = rangeDataBySuggestionId[suggestion.id];
            if (!rangeData)
                continue;
            const ranges = getSuggestionDisplayRanges(rangeData, previewSuggestedText ? "new" : "old");
            if (ranges.length === 0)
                continue;
            const rects = measureBridgeTextRangeRects(bridgeGeometry, getSuggestedEditTextRangeKey(suggestion), metrics);
            if (rects.length === 0) {
                const previousMeasurement = lastMeasurementsRef.current[suggestion.id];
                if (canReusePreviousMeasurement &&
                    previousMeasurement?.isTextRange &&
                    previousMeasurement.rects.length) {
                    nextMeasurements[suggestion.id] = previousMeasurement;
                }
                continue;
            }
            nextMeasurements[suggestion.id] = {
                rects,
                isTextRange: true,
            };
        }
        return nextMeasurements;
    }, [
        pageViewContext.bridgeGeometry,
        previewSuggestedText,
        suggestions,
        rangeDataBySuggestionId,
    ], {
        initialValue: {},
        equals: suggestionMeasurementsEqual,
        interests: ["viewport", "layout", "text", "adornment-data"],
    });
    useEffect(() => {
        lastMeasurementsRef.current = measurements;
    }, [measurements]);
    const overlayGroups = useMemo(() => {
        const groups = new Map();
        for (const suggestion of suggestions) {
            const measurement = measurements[suggestion.id];
            if (!measurement?.rects.length)
                continue;
            const key = getSuggestionMeasurementKey(measurement);
            const existing = groups.get(key);
            if (existing) {
                existing.suggestions.push(suggestion);
                continue;
            }
            groups.set(key, {
                key,
                suggestions: [suggestion],
                rects: measurement.rects,
                isTextRange: measurement.isTextRange,
            });
        }
        return Array.from(groups.values());
    }, [suggestions, measurements]);
    const variantDisplayByIdentity = useMemo(() => {
        const displays = new Map();
        for (const suggestion of suggestions) {
            if (!suggestion.fieldId)
                continue;
            const identityKey = fieldIdentityBySuggestionId[suggestion.id];
            if (!identityKey)
                continue;
            if (displays.has(identityKey))
                continue;
            const fieldValue = fieldValueByIdentity[identityKey];
            if (!fieldValue)
                continue;
            const selectionKey = getSuggestionSelectionKey(fieldValue);
            displays.set(identityKey, getSuggestionDisplayValue({
                repositoryValue: fieldValue.rawValue,
                suggestedEdits: suggestions,
                field: {
                    ...fieldValue,
                    pageItemId: pageViewContext.pageItemDescriptor?.id,
                    pageItemLanguage: pageViewContext.pageItemDescriptor?.language,
                    pageItemVersion: pageViewContext.pageItemDescriptor?.version,
                },
                mode: "suggestions",
                selectedVariantId: editContext?.suggestionVariantSelection[selectionKey],
            }));
        }
        return displays;
    }, [
        editContext?.suggestionVariantSelection,
        fieldIdentityBySuggestionId,
        fieldValueByIdentity,
        pageViewContext.pageItemDescriptor,
        suggestions,
    ]);
    if (!editContext)
        return null;
    const selectedCommentId = editContext.selectedComment?.id;
    const selectedSuggestedEditId = editContext.selectedSuggestedEdit?.id;
    const hasSelectedFeedback = !!selectedCommentId || !!selectedSuggestedEditId;
    return (_jsx(_Fragment, { children: overlayGroups.flatMap((group) => {
            const badgeSuggestions = dedupeFeedbackHighlightItems(group.suggestions);
            return group.rects.map((rect, index) => (_jsxs("div", { style: {
                    position: "absolute",
                    top: rect.y,
                    left: rect.x,
                    width: rect.width,
                    height: rect.height,
                    zIndex: 20,
                    pointerEvents: "none",
                }, children: [index === 0 && (_jsxs(FeedbackHighlightBadgeTray, { variant: variant, style: {
                            top: variant === "minimap" ? -12 : -38,
                            left: variant === "minimap"
                                ? group.isTextRange
                                    ? -4
                                    : 4
                                : group.isTextRange
                                    ? -9
                                    : 8,
                        }, children: [variant === "default" &&
                                Array.from(new Set(group.suggestions.map((suggestion) => fieldIdentityBySuggestionId[suggestion.id] ??
                                    getSuggestionFieldIdentityKey({
                                        itemId: suggestion.itemId,
                                        language: getSuggestedEditItemLanguage(suggestion),
                                        version: getSuggestedEditItemVersion(suggestion) ?? 0,
                                        fieldId: suggestion.fieldId ?? "",
                                    })))).map((identityKey) => {
                                    const display = variantDisplayByIdentity.get(identityKey);
                                    const fieldValue = fieldValueByIdentity[identityKey];
                                    if (!display || !fieldValue)
                                        return null;
                                    const selectionKey = getSuggestionSelectionKey(fieldValue);
                                    return (_jsx(SuggestionVariantSwitcher, { variants: display.variants, selectedVariantId: display.selectedVariantId, onSelect: (variantId) => editContext.setSuggestionVariantSelection((current) => ({
                                            ...current,
                                            [selectionKey]: variantId,
                                        })) }, identityKey));
                                }), badgeSuggestions.map((suggestion, badgeIndex) => {
                                const isSelected = suggestion.id === selectedSuggestedEditId;
                                const isDimmed = hasSelectedFeedback && !isSelected;
                                return (_jsx(SuggestionDisplayPopover, { suggestion: suggestion, onSuggestionUpdated: () => { }, children: _jsx(FeedbackHighlightBadge, { item: suggestion, isSelected: isSelected, isDimmed: isDimmed, variant: variant, minimapColorClassName: "bg-component-suggestions", zIndex: isSelected
                                            ? badgeSuggestions.length + 101
                                            : badgeSuggestions.length - badgeIndex + 100, testId: "suggestion-highlight-icon", "data-suggestion-id": variant === "default" ? suggestion.id : undefined, ariaSubject: "suggestion", onClick: () => {
                                            editContext.setSelectedComment(undefined);
                                            editContext.setSelectedSuggestedEdit(suggestion);
                                            editContext.setScrollIntoView(getSuggestedEditScrollIntoViewTarget(suggestion));
                                            editContext.select([suggestion.itemId]);
                                            modifiedFields?.setFocusedField({
                                                item: {
                                                    id: suggestion.itemId,
                                                    language: getSuggestedEditItemLanguage(suggestion),
                                                    version: getSuggestedEditItemVersion(suggestion) ?? 0,
                                                },
                                                fieldId: suggestion.fieldId,
                                            }, true);
                                        } }) }, suggestion.id));
                            })] })), (hasSelectedFeedback
                        ? group.suggestions.map((suggestion) => [suggestion])
                        : [group.suggestions]).map((suggestions, highlightIndex) => {
                        const isSelected = suggestions.length === 1 &&
                            suggestions[0]?.id === selectedSuggestedEditId;
                        const isDimmed = hasSelectedFeedback && !isSelected;
                        const highlightKey = suggestions
                            .map((suggestion) => suggestion.id)
                            .join("-");
                        return (_jsx("div", { "data-testid": "suggested-edit-highlight", "data-feedback-selected": isSelected ? "true" : undefined, "data-feedback-dimmed": isDimmed ? "true" : undefined, className: group.isTextRange
                                ? "absolute inset-0 rounded-sm"
                                : "absolute inset-0 rounded-[3px]", style: {
                                ...getSuggestionHighlightStyle(isSelected, isDimmed),
                                zIndex: isSelected ? 2 : 0,
                            } }, highlightKey || highlightIndex));
                    })] }, `${group.key}-${index}`)));
        }) }));
}
function hasAnyExpectedBridgeTextRange(geometry, textRanges) {
    if (!geometry)
        return false;
    const expectedKeys = new Set(textRanges.map((range) => range.key));
    return geometry.targets.some((target) => target.kind === "textRange" && expectedKeys.has(target.key));
}
function hasAllExpectedBridgeTextRanges(geometry, textRanges) {
    if (!geometry)
        return false;
    const availableKeys = new Set(geometry.targets
        .filter((target) => target.kind === "textRange")
        .map((target) => target.key));
    return textRanges.every((range) => availableKeys.has(range.key));
}
function getSuggestionMeasurementKey(measurement) {
    const rectKey = measurement.rects
        .map((rect) => `${roundMeasurement(rect.x)}:${roundMeasurement(rect.y)}:${roundMeasurement(rect.width)}:${roundMeasurement(rect.height)}`)
        .join("|");
    return `${measurement.isTextRange ? "text" : "box"}:${rectKey}`;
}
function roundMeasurement(value) {
    return Math.round(value * 2) / 2;
}
function getSuggestionHighlightStyle(isSelected, isDimmed) {
    const fillStrength = isDimmed ? 6 : isSelected ? 28 : 15;
    return {
        background: `color-mix(in srgb, var(--color-feedback-green) ${fillStrength}%, transparent)`,
        opacity: isDimmed ? 0.42 : 1,
        boxShadow: isSelected
            ? "0 1px 5px color-mix(in srgb, var(--color-feedback-green) 22%, transparent)"
            : undefined,
    };
}
//# sourceMappingURL=SuggestionHighlightings.js.map