import { Fragment as _Fragment, jsx as _jsx } from "react/jsx-runtime";
import { useEffect, useMemo, useRef, useState } from "react";
import { useEditContext } from "../client/editContext";
import { fieldKey, resolveVersionDiffTargets, } from "../version-diff/versionDiffTargets";
import { versionDiffHighlightClasses } from "../version-diff/versionDiffHighlightStyles";
import { useSlotContext } from "../views/editorSlotContext";
import { bridgeRectToOverlayRect, measureBridgeFieldRect, measureBridgeTextRangeRects, rectListEquals, } from "./overlay/geometry";
import { useOverlayMeasurement } from "./overlay/useOverlayMeasurement";
import { normalizeMarkerId } from "../utils";
const VERSION_DIFF_BRIDGE_SOURCE = "version-diff";
function measuredDiffHighlightsEqual(previous, next) {
    if (previous.length !== next.length)
        return false;
    for (let i = 0; i < previous.length; i++) {
        const previousItem = previous[i];
        const nextItem = next[i];
        if (previousItem.key !== nextItem.key ||
            previousItem.className !== nextItem.className ||
            !rectListEquals(previousItem.rects, nextItem.rects)) {
            return false;
        }
    }
    return true;
}
export function VersionDiffHighlightings({ pageViewContext, variant = "default", }) {
    const editContext = useEditContext();
    const slotContext = useSlotContext();
    const itemsRepository = editContext?.itemsRepository;
    const [targets, setTargets] = useState([]);
    const [componentTargets, setComponentTargets] = useState([]);
    const previousVersionCache = useRef(new Map());
    const compareToKey = [
        slotContext?.compareTo?.id,
        slotContext?.compareTo?.language,
        slotContext?.compareTo?.version,
    ].join("|");
    useEffect(() => {
        if (!itemsRepository || !pageViewContext.page) {
            setTargets([]);
            setComponentTargets([]);
            return;
        }
        let cancelled = false;
        const resolveTargets = async () => {
            const nextTargets = await resolveVersionDiffTargets({
                bridgeStructure: pageViewContext.bridgeStructure,
                pageItem: pageViewContext.page.item.descriptor,
                getItem: itemsRepository.getItem,
                baseline: slotContext?.compareMode && slotContext.compareTo
                    ? { type: "compare", compareTo: slotContext.compareTo }
                    : { type: "previous" },
                previousVersionCache: previousVersionCache.current,
            });
            if (cancelled)
                return;
            setTargets(nextTargets.fieldTargets);
            setComponentTargets(nextTargets.componentTargets);
        };
        resolveTargets();
        return () => {
            cancelled = true;
        };
    }, [
        itemsRepository,
        pageViewContext.page?.editRevision,
        pageViewContext.page?.item.id,
        pageViewContext.page?.item.language,
        pageViewContext.page?.item.version,
        pageViewContext.bridgeStructure,
        itemsRepository?.revision,
        slotContext?.compareMode,
        compareToKey,
    ]);
    const rangesByTargetKey = useMemo(() => new Map(targets.map((target) => [
        fieldKey(target.descriptor),
        target.hasDeletionOnlyChange
            ? []
            : target.ranges.filter(([start, end]) => start < end),
    ])), [targets]);
    const lastBridgeTextRangeSignatureRef = useRef("");
    useEffect(() => {
        if (!pageViewContext.bridgeReady) {
            lastBridgeTextRangeSignatureRef.current = "";
            return;
        }
        const textRanges = targets.flatMap((target) => {
            if (target.hasDeletionOnlyChange)
                return [];
            const targetKey = fieldKey(target.descriptor);
            const ranges = rangesByTargetKey.get(targetKey) || [];
            return ranges.map(([start, end], index) => ({
                key: getVersionDiffTextRangeKey(targetKey, index),
                fieldId: target.descriptor.fieldId,
                item: target.descriptor.item,
                start,
                end,
            }));
        });
        // Empty maps to "" so an initial empty set is a no-op, while going from
        // ranges back to empty still emits a clearing update.
        const signature = textRanges.length > 0 ? JSON.stringify(textRanges) : "";
        if (signature === lastBridgeTextRangeSignatureRef.current)
            return;
        lastBridgeTextRangeSignatureRef.current = signature;
        // Contribute to the shared geometry union so our ranges merge with
        // comments/suggestions/locks instead of clobbering them, and so highlights
        // clear when the diff goes away.
        pageViewContext.requestBridgeGeometry?.({
            source: VERSION_DIFF_BRIDGE_SOURCE,
            textRanges,
        });
    }, [
        pageViewContext,
        pageViewContext.bridgeReady,
        rangesByTargetKey,
        targets,
    ]);
    const highlights = useOverlayMeasurement((metrics) => {
        const bridgeGeometry = pageViewContext.bridgeGeometry;
        const bridgeStructure = pageViewContext.bridgeStructure;
        if (!bridgeGeometry)
            return [];
        const measured = [];
        for (const target of componentTargets) {
            const overlayRect = measureBridgeComponentDiffRect(bridgeGeometry, target, metrics);
            if (!overlayRect)
                continue;
            measured.push({
                key: `component-${target.id}`,
                className: variant === "minimap"
                    ? versionDiffHighlightClasses.componentMini
                    : versionDiffHighlightClasses.component,
                rects: [overlayRect],
            });
        }
        for (const target of targets) {
            const targetKey = fieldKey(target.descriptor);
            const rects = [];
            if (target.hasDeletionOnlyChange) {
                const rect = measureBridgeFieldRect(bridgeGeometry, bridgeStructure, target.descriptor, metrics);
                if (rect)
                    rects.push(rect);
            }
            else {
                const ranges = rangesByTargetKey.get(targetKey) || [];
                ranges.forEach((_, index) => {
                    const rangeRects = measureBridgeTextRangeRects(bridgeGeometry, getVersionDiffTextRangeKey(targetKey, index), metrics);
                    if (rangeRects.length > 0) {
                        rects.push(...rangeRects);
                        return;
                    }
                    const fallbackRect = measureBridgeFieldRect(bridgeGeometry, bridgeStructure, target.descriptor, metrics);
                    if (fallbackRect)
                        rects.push(fallbackRect);
                });
            }
            if (rects.length === 0)
                continue;
            measured.push({
                key: targetKey,
                className: target.hasDeletionOnlyChange
                    ? variant === "minimap"
                        ? versionDiffHighlightClasses.deletionOnlyMini
                        : versionDiffHighlightClasses.deletionOnly
                    : variant === "minimap"
                        ? versionDiffHighlightClasses.textMini
                        : versionDiffHighlightClasses.text,
                rects,
            });
        }
        return measured;
    }, [
        pageViewContext.bridgeGeometry,
        pageViewContext.bridgeStructure,
        targets,
        componentTargets,
        rangesByTargetKey,
        variant,
    ], {
        initialValue: [],
        equals: measuredDiffHighlightsEqual,
        interests: ["viewport", "layout", "text", "adornment-data"],
    });
    if (highlights.length === 0)
        return null;
    return (_jsx(_Fragment, { children: highlights.flatMap((highlight) => highlight.rects.map((rect, index) => (_jsx("div", { className: highlight.className, "data-testid": "version-diff-highlight", style: {
                top: rect.y,
                left: rect.x,
                width: Math.max(rect.width, 2),
                height: Math.max(rect.height, 2),
            } }, `${highlight.key}-${index}`)))) }));
}
function getVersionDiffTextRangeKey(targetKey, rangeIndex) {
    return `version-diff:${targetKey}:${rangeIndex}`;
}
function measureBridgeComponentDiffRect(geometry, target, metrics) {
    if (!geometry)
        return null;
    const candidateIds = [
        target.visualId,
        target.id,
        target.dataItemId,
        target.dataLayoutId,
        target.dataTypeId,
        target.providerRenderingItemId,
    ]
        .filter((id) => !!id)
        .map(normalizeMarkerId);
    const geometryTarget = geometry.targets.find((candidate) => {
        if (candidate.kind !== "component")
            return false;
        const candidateKeys = [candidate.componentId, candidate.key, candidate.elementKey]
            .filter((id) => !!id)
            .map(normalizeMarkerId);
        return candidateKeys.some((key) => candidateIds.includes(key));
    });
    if (!geometryTarget?.rect)
        return null;
    return bridgeRectToOverlayRect(geometry, geometryTarget.rect, metrics);
}
//# sourceMappingURL=VersionDiffHighlightings.js.map