import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
import { useEffect, useMemo, useRef, useState } from "react";
import { useEditContext } from "../client/editContext";
import { Tooltip, TooltipContent, TooltipTrigger, } from "../../components/ui/tooltip";
import { Avatar } from "../../components/ui/avatar";
import { useResolvedUser } from "../services/userResolution";
import { measureBridgeFieldRect, measureBridgeTextRangeRects, rectEquals, } from "./overlay/geometry";
import { useIframeOverlay } from "./overlay/IframeOverlayProvider";
import { useOverlayMeasurement } from "./overlay/useOverlayMeasurement";
const LOCKED_FIELD_BRIDGE_SOURCE = "locked-fields";
function useResolvedSessionUser(session) {
    return useResolvedUser(session.user.name, {
        fullName: session.user.fullName,
        avatarUrl: session.user.avatarUrl,
    });
}
export function LockedFieldIndicator({ pageViewContext, }) {
    const editContext = useEditContext();
    const sessionsWithFieldLock = useMemo(() => editContext?.activeSessions.filter((session) => {
        if (!session.fieldLock)
            return false;
        if (session.sessionId !== editContext.sessionId)
            return true;
        const caret = editContext.remoteCarets[session.sessionId];
        return remoteCaretMatchesFieldLock(caret, session.fieldLock);
    }) ?? [], [
        editContext?.activeSessions,
        editContext?.remoteCarets,
        editContext?.sessionId,
    ]);
    const effectivePageViewContext = pageViewContext ??
        editContext?.getActiveSlotContext()?.primaryPageViewContext;
    const lastBridgeTextRangeSignatureRef = useRef("");
    useEffect(() => {
        if (!editContext || !effectivePageViewContext?.bridgeReady) {
            lastBridgeTextRangeSignatureRef.current = "";
            return;
        }
        const textRanges = sessionsWithFieldLock.flatMap((session) => {
            const fieldLock = session.fieldLock;
            const caret = editContext.remoteCarets[session.sessionId];
            if (!fieldLock ||
                caret?.offset == null ||
                !remoteCaretMatchesFieldLock(caret, fieldLock)) {
                return [];
            }
            return [
                {
                    key: getRemoteCaretTextRangeKey(session.sessionId, caret.offset),
                    fieldId: fieldLock.fieldId,
                    item: fieldLock.item,
                    start: Math.max(0, caret.offset),
                    end: Math.max(0, caret.offset),
                },
            ];
        });
        // 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 caret ranges merge with
        // comments/suggestions instead of clobbering them. Replacing the whole
        // source each time also drops stale offset-keyed ranges as the caret moves.
        effectivePageViewContext.requestBridgeGeometry?.({
            source: LOCKED_FIELD_BRIDGE_SOURCE,
            textRanges,
        });
    }, [
        editContext,
        editContext?.remoteCarets,
        effectivePageViewContext,
        effectivePageViewContext?.bridgeReady,
        sessionsWithFieldLock,
    ]);
    if (!editContext)
        return;
    return (_jsx(_Fragment, { children: sessionsWithFieldLock.map((session) => {
            if (!session.fieldLock)
                return;
            const caret = editContext.remoteCarets[session.sessionId];
            const caretMatchesLock = remoteCaretMatchesFieldLock(caret, session.fieldLock);
            const isLocalSession = session.sessionId === editContext.sessionId;
            if (!effectivePageViewContext?.bridgeGeometry)
                return;
            return (_jsxs("div", { children: [!caretMatchesLock && (_jsx(BridgeFieldLock, { pageViewContext: effectivePageViewContext, session: session })), caretMatchesLock && caret?.offset != null && (_jsx(BridgeFieldCaret, { pageViewContext: effectivePageViewContext, session: session, offset: caret.offset, isLocalSession: isLocalSession }))] }, session.sessionId));
        }) }));
}
function remoteCaretMatchesFieldLock(caret, fieldLock) {
    return (caret?.offset != null &&
        caret.fieldId === fieldLock.fieldId &&
        caret.item?.id === fieldLock.item.id &&
        caret.item?.language === fieldLock.item.language &&
        caret.item?.version === fieldLock.item.version);
}
function getRemoteCaretTextRangeKey(sessionId, offset) {
    return `remote-caret:${sessionId}:${offset ?? "none"}`;
}
function BridgeFieldCaret({ pageViewContext, session, offset, isLocalSession, }) {
    const lastMeasuredRectRef = useRef(null);
    useEffect(() => {
        lastMeasuredRectRef.current = null;
    }, [
        session.sessionId,
        session.fieldLock?.fieldId,
        session.fieldLock?.item.id,
        session.fieldLock?.item.language,
        session.fieldLock?.item.version,
    ]);
    const rect = useOverlayMeasurement((metrics) => {
        const caretRect = measureBridgeTextRangeRects(pageViewContext.bridgeGeometry, getRemoteCaretTextRangeKey(session.sessionId, offset), metrics)[0];
        if (caretRect) {
            lastMeasuredRectRef.current = caretRect;
            return caretRect;
        }
        return lastMeasuredRectRef.current;
    }, [
        pageViewContext.bridgeGeometry,
        pageViewContext.bridgeStructure,
        offset,
        session.sessionId,
        session.fieldLock?.fieldId,
        session.fieldLock?.item.id,
        session.fieldLock?.item.language,
        session.fieldLock?.item.version,
    ], {
        initialValue: null,
        equals: rectEquals,
        interests: ["viewport", "layout", "text", "adornment-data"],
    });
    if (!rect) {
        return isLocalSession ? null : (_jsx(BridgeFieldLock, { pageViewContext: pageViewContext, session: session }));
    }
    return (_jsx(RemoteCaretAdornment, { rect: rect, session: session, hideLabel: isLocalSession }));
}
function BridgeFieldLock({ pageViewContext, session, }) {
    const rect = useOverlayMeasurement((metrics) => {
        if (!session.fieldLock)
            return null;
        return measureBridgeFieldRect(pageViewContext.bridgeGeometry, pageViewContext.bridgeStructure, session.fieldLock, metrics);
    }, [
        pageViewContext.bridgeGeometry,
        pageViewContext.bridgeStructure,
        session.sessionId,
        session.fieldLock?.fieldId,
        session.fieldLock?.item.id,
        session.fieldLock?.item.language,
        session.fieldLock?.item.version,
    ], {
        initialValue: null,
        equals: rectEquals,
        interests: ["viewport", "layout", "text", "adornment-data"],
    });
    return rect ? _jsx(FieldLockBadge, { rect: rect, session: session }) : null;
}
function FieldLockBadge({ rect, session, }) {
    const { displayName, avatarUrl } = useResolvedSessionUser(session);
    const [tooltipOpen, setTooltipOpen] = useState(false);
    const overlay = useIframeOverlay();
    useEffect(() => {
        setTooltipOpen(false);
    }, [
        session.sessionId,
        session.fieldLock?.fieldId,
        session.fieldLock?.item.id,
    ]);
    useEffect(() => {
        if (!tooltipOpen)
            return;
        const closeTooltip = () => setTooltipOpen(false);
        window.addEventListener("scroll", closeTooltip, true);
        window.addEventListener("resize", closeTooltip);
        const unsubscribeScroll = overlay.subscribeScroll(closeTooltip);
        return () => {
            window.removeEventListener("scroll", closeTooltip, true);
            window.removeEventListener("resize", closeTooltip);
            unsubscribeScroll();
        };
    }, [overlay, tooltipOpen]);
    return (_jsxs(Tooltip, { open: tooltipOpen, onOpenChange: setTooltipOpen, delayDuration: 250, disableHoverableContent: true, children: [_jsx(TooltipTrigger, { asChild: true, children: _jsxs("div", { className: "badge-pad-sm absolute flex max-w-40 items-center gap-1 rounded text-xs text-white opacity-90 hover:opacity-100", onPointerEnter: () => setTooltipOpen(true), onPointerLeave: () => setTooltipOpen(false), onPointerDown: () => setTooltipOpen(false), onBlur: () => setTooltipOpen(false), style: {
                        left: rect.x + "px",
                        top: rect.y + "px",
                        background: session.color,
                    }, children: [avatarUrl && (_jsx(Avatar, { src: avatarUrl, name: displayName, className: "size-4 border border-white/40" })), _jsx("span", { className: "truncate", children: displayName })] }) }), _jsx(TooltipContent, { side: "top", className: "pointer-events-none max-w-xs", children: _jsxs("div", { className: "space-y-1 text-xs", children: [_jsxs("div", { className: "font-medium", children: ["Field locked by ", displayName] }), _jsx("div", { children: "This field is currently being edited by another user, so editing is blocked until their field lock is released." }), _jsxs("div", { className: "text-neutral-grey-50", children: ["Username: ", session.user.name] })] }) })] }));
}
function RemoteCaretAdornment({ rect, session, hideLabel, }) {
    const { displayName, avatarUrl } = useResolvedSessionUser(session);
    return (_jsxs("div", { className: "pointer-events-none absolute z-[1]", style: {
            left: rect.x,
            top: rect.y,
            height: Math.max(rect.height, 14),
        }, children: [_jsx(RemoteCaretBlinkStyle, {}), _jsx("div", { className: "h-full w-0.5 rounded-full", style: {
                    background: session.color,
                    animation: "parheliaRemoteCaretBlink 1s steps(1, end) infinite",
                } }), !hideLabel && (_jsxs("div", { className: "badge-pad-sm absolute top-0 left-1.5 flex max-w-40 -translate-y-full items-center gap-1 rounded text-xs text-white shadow", style: { background: session.color }, children: [avatarUrl && (_jsx(Avatar, { src: avatarUrl, name: displayName, className: "size-4 border border-white/40" })), _jsx("span", { className: "truncate", children: displayName })] }))] }));
}
function RemoteCaretBlinkStyle() {
    return (_jsx("style", { children: `@keyframes parheliaRemoteCaretBlink {
          0%, 49% { opacity: 1; }
          50%, 100% { opacity: 0; }
        }` }));
}
//# sourceMappingURL=LockedFieldIndicator.js.map