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, getSuggestionRangeData, } from "../reviews/feedbackScrollTarget";
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 suggestions = useMemo(() => (editContext?.suggestedEdits || []).filter((suggestion) => suggestion.status !== "applied" && suggestion.status !== "rejected"), [editContext?.suggestedEdits]);
    useEffect(() => {
        if (!editContext) {
            setRangeDataBySuggestionId({});
            return;
        }
        let cancelled = false;
        const resolveRanges = async () => {
            const nextRangeData = {};
            await Promise.all(suggestions.map(async (suggestion) => {
                if (!suggestion.fieldId) {
                    nextRangeData[suggestion.id] = {
                        oldRanges: [],
                        newRanges: [],
                        oldText: "",
                        newText: "",
                    };
                    return;
                }
                const field = await editContext.itemsRepository.getField({
                    item: {
                        id: suggestion.itemId,
                        language: suggestion.mainItemLanguage,
                        version: suggestion.mainItemVersion,
                    },
                    fieldId: suggestion.fieldId,
                });
                nextRangeData[suggestion.id] = getSuggestionRangeData(suggestion.oldValue ?? field?.rawValue ?? "", suggestion.newValue ?? "");
            }));
            if (!cancelled) {
                setRangeDataBySuggestionId(nextRangeData);
            }
        };
        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 = rangeData.newRanges.length > 0
            ? rangeData.newRanges
            : rangeData.oldRanges;
        return ranges.map(([start, end]) => ({
            key: getSuggestedEditTextRangeKey(suggestion),
            fieldId: suggestion.fieldId,
            item: {
                id: suggestion.itemId,
                language: suggestion.mainItemLanguage,
                version: suggestion.mainItemVersion,
            },
            start,
            end,
        }));
    }), [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;
            if (!rangeDataBySuggestionId[suggestion.id])
                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, 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]);
    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 && (_jsx(FeedbackHighlightBadgeTray, { variant: variant, style: {
                            top: variant === "minimap" ? -12 : -38,
                            left: variant === "minimap"
                                ? group.isTextRange
                                    ? -4
                                    : 4
                                : group.isTextRange
                                    ? -9
                                    : 8,
                        }, children: 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", ariaSubject: "suggestion", onClick: () => {
                                        editContext.setSelectedComment(undefined);
                                        editContext.setSelectedSuggestedEdit(suggestion);
                                        editContext.setScrollIntoView(getSuggestedEditScrollIntoViewTarget(suggestion));
                                        editContext.select([suggestion.itemId]);
                                        modifiedFields?.setFocusedField({
                                            item: {
                                                id: suggestion.itemId,
                                                language: suggestion.mainItemLanguage,
                                                version: suggestion.mainItemVersion,
                                            },
                                            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