import { useEffect, useRef, useState } from "react";
import { useEditContext, useFieldsEditContext, useEditContextRef, } from "../client/editContext";
import { getFieldDescriptorFromElement, hasFieldLock, getSessionWithFieldLock, } from "../utils";
import { applyPatch, diffWords } from "diff";
import { createPatch } from "diff";
// import { useDebouncedCallback } from "use-debounce";
// import { executePrompt } from "../services/aiService";
import { useInlineAiCompletion } from "./useInlineAICompletion";
import { getComponentById } from "../componentTreeHelper";
export function InlineEditor({ pageViewContext, compareView, }) {
    const context = useEditContext();
    const contextRef = useEditContextRef();
    const modifiedFieldsContext = useFieldsEditContext();
    const [cursorSpanId] = useState(() => `cursor-indicator-${Math.random().toString(36).substring(2, 9)}`);
    const isUpdatingRef = useRef(false);
    if (!context)
        return;
    const mutationObserverRef = useRef(null);
    const selectionChangeListenerRef = useRef(null);
    const lastQueuedInlineValueRef = useRef(null);
    const refreshCompletion = useInlineAiCompletion({
        pageViewContext,
        cursorSpanId,
        isUpdatingRef,
    });
    const isTextFieldType = (fieldType) => {
        const fieldTypeLower = fieldType.toLowerCase();
        return (fieldTypeLower === "single-line text" ||
            fieldTypeLower === "multi-line text" ||
            fieldTypeLower === "rich text");
    };
    // Stable identity check: compare by field key (not by DOM node reference).
    // This avoids stomping the active field if the iframe DOM is replaced/morphed.
    const getFieldKeyFromElement = (el) => {
        const fieldId = el.getAttribute("data-fieldid") || "";
        const itemId = el.getAttribute("data-itemid") || "";
        const language = el.getAttribute("data-language") || "";
        const versionRaw = el.getAttribute("data-version");
        const version = versionRaw ? parseInt(versionRaw, 10) : NaN;
        return { fieldId, itemId, language, version };
    };
    const isSameFieldKey = (el, key) => {
        if (!key)
            return false;
        const k = getFieldKeyFromElement(el);
        return (k.fieldId === key.fieldId &&
            k.itemId === key.itemId &&
            k.language === key.language &&
            k.version === key.version);
    };
    // Only skip DOM updates for fields being edited inline in the iframe.
    // Do NOT skip updates for sidebar-focused fields - we want those to update the preview.
    const activeFieldKey = modifiedFieldsContext?.inlineEditingFieldElement
        ? getFieldKeyFromElement(modifiedFieldsContext.inlineEditingFieldElement)
        : context.selectedRange
            ? {
                fieldId: context.selectedRange.fieldId,
                itemId: context.selectedRange.itemId,
                language: context.selectedRange.language,
                version: context.selectedRange.version,
            }
            : undefined;
    // Helper: set innerHTML when content changes
    function setInnerHTMLWithStackTrace(element, nextHTML, meta) {
        const prevHTML = element.innerHTML ?? "";
        if (prevHTML === nextHTML)
            return false;
        element.innerHTML = nextHTML;
        // try {
        //   const resolvedFieldId =
        //     meta.fieldId ?? element.getAttribute("data-fieldid");
        //   const resolvedItemId = meta.itemId ?? element.getAttribute("data-itemid");
        //   const resolvedLanguage =
        //     meta.language ?? element.getAttribute("data-language");
        //   const resolvedVersion =
        //     meta.version ??
        //     (element.getAttribute("data-version")
        //       ? parseInt(element.getAttribute("data-version") as string, 10)
        //       : null);
        //   // Log concise info and stack trace
        //   // Limit previews to keep console tidy
        //   const preview = (s: string) =>
        //     s.length > 200 ? s.slice(0, 200) + "…" : s;
        //   console.groupCollapsed(
        //     `[InlineEditor] DOM field update (${meta.source}) ${nextHTML} `,
        //   );
        //   const modifiedFieldValue = modifiedFieldsContext?.modifiedFields.find(
        //     (x) =>
        //       x.fieldId === resolvedFieldId &&
        //       x.item.id === resolvedItemId &&
        //       x.item.language === resolvedLanguage &&
        //       x.item.version === resolvedVersion,
        //   )?.value;
        //   console.log({
        //     fieldId: resolvedFieldId,
        //     itemId: resolvedItemId,
        //     language: resolvedLanguage,
        //     version: resolvedVersion,
        //     prevLength: prevHTML.length,
        //     nextLength: nextHTML.length,
        //     prevPreview: preview(prevHTML),
        //     nextPreview: preview(nextHTML),
        //     modifiedFieldsContext: modifiedFieldsContext?.modifiedFields,
        //     repository: contextRef.current?.itemsRepository,
        //     modifiedFieldValue,
        //   });
        //   console.trace();
        //   console.groupEnd();
        // } catch {
        //   // no-op logging failure
        // }
        return true;
    }
    function queueInlineFieldValue({ value, fieldId, fieldName, itemId, language, version, }) {
        if (lastQueuedInlineValueRef.current === value)
            return;
        lastQueuedInlineValueRef.current = value;
        contextRef.current?.operations.editField({
            field: {
                fieldId,
                fieldName: fieldName ?? undefined,
                item: {
                    id: itemId,
                    language,
                    version,
                },
            },
            refresh: "none",
            value,
        });
    }
    useEffect(() => {
        if (!context || compareView || context.mode === "preview")
            return;
        const element = modifiedFieldsContext?.inlineEditingFieldElement;
        const editableElements = pageViewContext.editorIframe?.contentWindow?.document.querySelectorAll('[contenteditable="true"]');
        editableElements?.forEach((element) => {
            if (element !== modifiedFieldsContext?.inlineEditingFieldElement)
                element.setAttribute("contenteditable", "false");
        });
        if (element) {
            lastQueuedInlineValueRef.current = null;
            // Clean up any existing cursor indicators
            const iframeDocument = pageViewContext.editorIframe?.contentWindow?.document;
            const existingCursors = iframeDocument?.querySelectorAll('[data-cursor-indicator="true"]');
            existingCursors?.forEach((el) => el.parentNode?.removeChild(el));
            const fieldId = element.getAttribute("data-fieldid");
            const fieldName = element.getAttribute("data-fieldname");
            const itemId = element.getAttribute("data-itemid");
            const language = element.getAttribute("data-language");
            const versionText = element.getAttribute("data-version");
            const version = versionText ? parseInt(versionText) : undefined;
            const isRichText = element.getAttribute("data-is-richtext") === "true";
            if (!fieldId || !itemId || !language || !version)
                return;
            // Guard: if layout components are hidden, do not allow editing fields of layout components
            if (contextRef.current?.showLayoutComponents === false &&
                contextRef.current?.page) {
                const owner = getComponentById(itemId, contextRef.current.page);
                if (owner?.layoutId)
                    return;
            }
            const fieldDescriptor = getFieldDescriptorFromElement(element);
            // Check if another session has this field locked
            const fieldLockedBySession = getSessionWithFieldLock(fieldDescriptor, context);
            const isLockedByAnotherUser = fieldLockedBySession &&
                fieldLockedBySession.sessionId !== context.sessionId;
            // If another user has the lock, don't allow editing
            if (isLockedByAnotherUser)
                return;
            const hasFielLock = context.mode === "suggestions" ||
                hasFieldLock(fieldDescriptor, context);
            if (!hasFielLock)
                return;
            element.setAttribute("contenteditable", "true");
            const iframeWindow = pageViewContext.editorIframe?.contentWindow;
            const sanitizeCurrentValue = () => {
                let currentValue = isRichText ? element.innerHTML : element.innerText;
                if (isRichText) {
                    const clone = element.cloneNode(true);
                    const cursorElem = clone.querySelector(`#${cursorSpanId}`);
                    if (cursorElem)
                        cursorElem.parentNode?.removeChild(cursorElem);
                    const ownerDoc = clone.ownerDocument || document;
                    const walker = ownerDoc.createTreeWalker(clone, NodeFilter.SHOW_TEXT);
                    const toClean = [];
                    while (walker.nextNode()) {
                        const tn = walker.currentNode;
                        if (tn.nodeValue && tn.nodeValue.includes("\u200B"))
                            toClean.push(tn);
                    }
                    toClean.forEach((tn) => (tn.nodeValue = tn.nodeValue?.replaceAll("\u200B", "") || ""));
                    currentValue = clone.innerHTML;
                }
                else {
                    currentValue = currentValue.replaceAll("\u200B", "");
                }
                return currentValue;
            };
            const inputHandler = () => {
                const currentValue = sanitizeCurrentValue();
                queueInlineFieldValue({
                    value: currentValue,
                    fieldId,
                    fieldName,
                    itemId,
                    language,
                    version,
                });
            };
            element.addEventListener("input", inputHandler);
            // Setup a more robust mutation observer
            mutationObserverRef.current = new MutationObserver((mutations) => {
                // If we're already processing an update, don't trigger another one
                if (isUpdatingRef.current)
                    return;
                // Check if mutations only affect our cursor span
                const hasRealChanges = mutations.some((mutation) => {
                    // Skip our cursor span
                    if (mutation.target.nodeType === Node.ELEMENT_NODE &&
                        mutation.target.id === cursorSpanId) {
                        console.log("skipping cursor span");
                        return false;
                    }
                    // Skip mutations where target is our span
                    if (mutation.target.nodeType === Node.ELEMENT_NODE &&
                        mutation.target.getAttribute &&
                        mutation.target.getAttribute("data-cursor-indicator") === "true") {
                        console.log("skipping cursor span 2");
                        return false;
                    }
                    // Skip if all added/removed nodes are just our cursor span
                    if (mutation.type === "childList") {
                        const nonCursorChanges = Array.from(mutation.addedNodes)
                            .concat(Array.from(mutation.removedNodes))
                            .filter((node) => {
                            return (node.nodeType !== Node.ELEMENT_NODE ||
                                !node.id ||
                                node.id !== cursorSpanId);
                        });
                        return nonCursorChanges.length > 0;
                    }
                    return true;
                });
                if (hasRealChanges) {
                    // Capture and sanitize the value at mutation time
                    const capturedValue = sanitizeCurrentValue();
                    queueInlineFieldValue({
                        value: capturedValue,
                        fieldId,
                        fieldName,
                        itemId,
                        language,
                        version,
                    });
                }
                // Always update cursor position after any mutation,
                // but delay to let the mutation processing complete
                // if (!isUpdatingRef.current) {
                //   setTimeout(updateCursorSpan, 0);
                //   //   updateCompletion();
                // }
            });
            setTimeout(() => {
                // Only focus if no other important element has focus (like the AI dialog)
                const activeEl = document.activeElement;
                const isInDialog = activeEl?.closest(".agent-inline-dialog") ||
                    activeEl?.closest('[role="dialog"]');
                if (!isInDialog) {
                    element.focus();
                }
            }, 50);
            // Initial cursor placement
            // setTimeout(updateCursorSpan, 10);
            // Add listener for selection changes
            if (iframeWindow) {
                const selectionChangeHandler = () => {
                    // Only update if we're not already updating
                    // if (!isUpdatingRef.current) {
                    //   setTimeout(updateCursorSpan, 0);
                    // }
                };
                iframeWindow.document.addEventListener("selectionchange", selectionChangeHandler);
                selectionChangeListenerRef.current = () => {
                    iframeWindow.document.removeEventListener("selectionchange", selectionChangeHandler);
                };
                mutationObserverRef.current.observe(element, {
                    childList: true, // observe direct children changes
                    subtree: true, // observe all descendants changes
                    characterData: true, // observe text changes
                });
                // Cleanup
                return () => {
                    element.removeEventListener("input", inputHandler);
                    if (mutationObserverRef.current) {
                        mutationObserverRef.current.disconnect();
                        mutationObserverRef.current = null;
                    }
                    if (selectionChangeListenerRef.current) {
                        selectionChangeListenerRef.current();
                        selectionChangeListenerRef.current = null;
                    }
                    // iframeWindow.document.removeEventListener("keydown", keyHandler);
                    // Clean up any cursor indicators on unmount
                    const cursorElement = iframeDocument?.getElementById(cursorSpanId);
                    if (cursorElement) {
                        cursorElement.parentNode?.removeChild(cursorElement);
                    }
                };
            }
        }
        async function updateFocusedFieldContent() {
            const element = modifiedFieldsContext?.inlineEditingFieldElement;
            if (!element)
                return;
            const savedPosition = saveCaretPosition(element);
            let contentWasUpdated = false;
            const fieldId = element.getAttribute("data-fieldid");
            const itemId = element.getAttribute("data-itemid");
            const language = element.getAttribute("data-language");
            const versionStr = element.getAttribute("data-version");
            if (!fieldId || !itemId || !language || !versionStr)
                return;
            const version = parseInt(versionStr, 10);
            const descriptor = { id: itemId, language, version };
            // Retrieve the current field value from the repository.
            const loadedItem = await contextRef.current?.itemsRepository.getItem(descriptor);
            if (!loadedItem)
                return;
            // Get the baseline value from the repository.
            const repositoryField = loadedItem.fields.find((f) => f.id === fieldId);
            let baseValue = repositoryField ? repositoryField.rawValue || "" : "";
            // Override with the modified field value if it exists.
            const modField = modifiedFieldsContext?.modifiedFields.find((mod) => mod.fieldId === fieldId &&
                mod.item.id === itemId &&
                mod.item.language === language &&
                mod.item.version === version);
            if (modField) {
                baseValue = modField.value || "";
            }
            // If suggestions mode is active, merge all suggestions for this field.
            if (contextRef.current?.mode === "suggestions") {
                const pageVer = pageViewContext.pageItemDescriptor?.version;
                const fieldSuggestions = (contextRef.current?.suggestedEdits ?? []).filter((s) => s.fieldId === fieldId &&
                    s.itemId === itemId &&
                    s.mainItemLanguage === language &&
                    (pageVer != null
                        ? s.mainItemVersion === pageVer || s.mainItemVersion === 0
                        : s.mainItemVersion === version));
                // Sort suggestions in chronological order (oldest first).
                fieldSuggestions.sort((a, b) => new Date(a.created).getTime() - new Date(b.created).getTime());
                let mergedValue = baseValue;
                for (const suggestion of fieldSuggestions) {
                    const patch = createPatch("field", suggestion.oldValue, suggestion.newValue);
                    const patchedCandidate = applyPatch(mergedValue, patch);
                    if (patchedCandidate !== false &&
                        typeof patchedCandidate === "string") {
                        mergedValue = patchedCandidate;
                    }
                    else {
                        mergedValue = suggestion.newValue;
                    }
                }
                // Update focused field element with the merged plain text value.
                setInnerHTMLWithStackTrace(element, mergedValue, {
                    source: "updateFocusedFieldContent:mergedValue",
                    fieldId,
                    itemId,
                    language,
                    version,
                });
                contentWasUpdated = true;
            }
            else {
                if (element.innerHTML !== baseValue) {
                    // If suggestions mode is off, update with the base value.
                    setInnerHTMLWithStackTrace(element, baseValue, {
                        source: "updateFocusedFieldContent:baseValue",
                        fieldId,
                        itemId,
                        language,
                        version,
                    });
                    contentWasUpdated = true;
                }
            }
            if (contentWasUpdated) {
                setTimeout(() => {
                    if (savedPosition)
                        restoreCaretPosition(element, savedPosition);
                }, 0);
            }
        }
        // Avoid rewriting the focused editable DOM while the user is typing in normal edit mode.
        // This prevents lost/reordered characters on slow machines.
        if (contextRef.current?.mode === "suggestions" ||
            contextRef.current?.showSuggestedEdits) {
            setTimeout(() => {
                updateFocusedFieldContent();
            }, 50);
        }
        return () => {
            if (mutationObserverRef.current) {
                mutationObserverRef.current.disconnect();
                mutationObserverRef.current = null;
            }
            if (selectionChangeListenerRef.current) {
                selectionChangeListenerRef.current();
                selectionChangeListenerRef.current = null;
            }
            // Clean up any cursor indicators on unmount
            const iframeDocument = pageViewContext.editorIframe?.contentWindow?.document;
            const cursorElement = iframeDocument?.getElementById(cursorSpanId);
            if (cursorElement) {
                cursorElement.parentNode?.removeChild(cursorElement);
            }
        };
    }, [modifiedFieldsContext?.inlineEditingFieldElement]);
    function saveCaretPosition(editableElement) {
        const selection = pageViewContext.editorIframe?.contentWindow?.getSelection();
        if (!selection ||
            selection.rangeCount === 0 ||
            !editableElement.contains(selection.anchorNode)) {
            return null;
        }
        // Get the current range
        const range = selection.getRangeAt(0);
        const getGlobalOffset = (node, offset) => {
            let absoluteOffset = 0;
            function walk(current) {
                if (current === node) {
                    absoluteOffset += offset;
                    return true;
                }
                if (current.nodeType === Node.TEXT_NODE) {
                    absoluteOffset += current.length;
                }
                else {
                    for (let i = 0; i < current.childNodes.length; i++) {
                        if (walk(current.childNodes[i]))
                            return true;
                    }
                }
                return false;
            }
            walk(editableElement);
            return absoluteOffset;
        };
        return {
            startOffset: getGlobalOffset(range.startContainer, range.startOffset),
            endOffset: getGlobalOffset(range.endContainer, range.endOffset),
        };
    }
    function restoreCaretPosition(element, position) {
        const offsets = typeof position === "number"
            ? { startOffset: position, endOffset: position }
            : position;
        if (!offsets)
            return;
        const selection = pageViewContext.editorIframe?.contentWindow?.getSelection();
        if (!selection)
            return;
        selection.removeAllRanges();
        function findNodeAndOffset(targetPos) {
            let currentPos = 0;
            function walk(node) {
                if (node.nodeType === Node.TEXT_NODE) {
                    const length = node.length;
                    if (currentPos + length >= targetPos) {
                        return { node, offset: targetPos - currentPos };
                    }
                    currentPos += length;
                }
                else {
                    for (let i = 0; i < node.childNodes.length; i++) {
                        const res = walk(node.childNodes[i]);
                        if (res)
                            return res;
                    }
                }
                return null;
            }
            return walk(element);
        }
        const start = findNodeAndOffset(offsets.startOffset);
        const end = findNodeAndOffset(offsets.endOffset);
        if (start && end) {
            const range = new Range();
            range.setStart(start.node, start.offset);
            range.setEnd(end.node, end.offset);
            selection.addRange(range);
        }
        else if (element.firstChild) {
            // Fallback to end of content
            const range = new Range();
            range.selectNodeContents(element);
            range.collapse(false);
            selection.addRange(range);
        }
    }
    useEffect(() => {
        const updateFieldsWithSuggestions = async () => {
            const iframeWindow = pageViewContext.editorIframe?.contentWindow;
            if (!iframeWindow)
                return;
            const doc = iframeWindow.document;
            // First, process modified fields directly (from updateFields functionality)
            // IMPORTANT: Only update fields that belong to this page's language/version context.
            // There can be multiple InlineEditor instances (main + comparison), and each should only
            // update its own iframe's fields to avoid cross-contamination.
            const currentPageLang = pageViewContext.pageItemDescriptor?.language;
            const currentPageVer = pageViewContext.pageItemDescriptor?.version;
            for (const modField of modifiedFieldsContext?.modifiedFields ?? []) {
                // Skip fields that don't belong to this page's context
                if (currentPageLang && modField.item.language !== currentPageLang)
                    continue;
                if (currentPageVer && modField.item.version !== currentPageVer)
                    continue;
                // In SXA markup, the same field can appear multiple times for a component.
                // Update all matching nodes and only skip the exact node currently edited inline.
                const allMatchingElements = doc.querySelectorAll(`[data-fieldid="${modField.fieldId}"][data-itemid="${modField.item.id}"]`);
                const inlineEditingElement = modifiedFieldsContext?.inlineEditingFieldElement ?? null;
                const iframeActiveEl = doc.activeElement;
                const isInlineEditActive = inlineEditingElement && (iframeActiveEl === inlineEditingElement ||
                    inlineEditingElement.contains(iframeActiveEl));
                const targetElements = Array.from(allMatchingElements).filter((candidate) => !isInlineEditActive || candidate !== inlineEditingElement);
                if (targetElements.length > 0) {
                    const realField = await context.itemsRepository.getField({
                        fieldId: modField.fieldId,
                        item: modField.item,
                    });
                    const fieldType = realField?.type;
                    if (fieldType && isTextFieldType(fieldType)) {
                        const next = modField.value ? modField.value : "";
                        let wroteAtLeastOne = false;
                        for (const targetElement of targetElements) {
                            const currentHTML = targetElement.innerHTML;
                            if (currentHTML === next)
                                continue;
                            wroteAtLeastOne = true;
                            // Suppress MutationObserver to prevent feedback loop where server-driven
                            // updates get re-sent to the backend as new operations
                            isUpdatingRef.current = true;
                            setInnerHTMLWithStackTrace(targetElement, next, {
                                source: "updateFieldsWithSuggestions:modifiedField",
                                fieldId: modField.fieldId,
                                itemId: modField.item.id,
                                language: modField.item.language,
                                version: modField.item.version,
                            });
                        }
                        if (wroteAtLeastOne) {
                            // Reset after macrotask to ensure MutationObserver callbacks (which are microtasks) are suppressed
                            setTimeout(() => {
                                isUpdatingRef.current = false;
                            }, 0);
                        }
                    }
                }
            }
            if (context.mode !== "suggestions" && !context.showSuggestedEdits) {
                return;
            }
            // Now proceed with the original updateFieldsWithSuggestions logic
            const allFieldElements = doc.querySelectorAll("[data-fieldid][data-itemid][data-language][data-version]");
            const fieldItems = await context.itemsRepository.getItems(Array.from(allFieldElements)
                .map((element) => {
                const fieldId = element.getAttribute("data-fieldid");
                return fieldId
                    ? {
                        id: fieldId,
                        language: "en",
                        version: 0,
                    }
                    : null;
            })
                .filter((item) => item !== null));
            const validFields = fieldItems.filter((item) => {
                const typeField = item.fields.find((f) => f.name === "Type");
                if (!typeField)
                    return false;
                const typeValue = typeField.value;
                return typeValue && isTextFieldType(typeValue);
            });
            const validFieldElements = Array.from(allFieldElements).filter((element) => validFields.some((item) => element.getAttribute("data-fieldid") === item.id));
            validFields.forEach(async (item, index) => {
                const fieldElement = validFieldElements[index];
                if (!fieldElement)
                    return;
                // Do not update if this field is currently being edited inline.
                // In suggestions mode we show suggested text in all fields, but must NOT overwrite
                // the focused field while the user is actively typing - that resets the cursor.
                // We detect active typing by checking if the field is both the inlineEditingFieldElement
                // AND the iframe's activeElement (when the user clicks mode-switch buttons, focus moves
                // to the parent frame so the iframe field is no longer activeElement).
                if (isSameFieldKey(fieldElement, activeFieldKey)) {
                    const iframeDoc = pageViewContext.editorIframe?.contentWindow?.document;
                    const isFieldActiveInIframe = iframeDoc?.activeElement === fieldElement;
                    if (isFieldActiveInIframe && modifiedFieldsContext?.inlineEditingFieldElement) {
                        return;
                    }
                    if (context.mode !== "suggestions") {
                        return;
                    }
                }
                const fieldId = fieldElement.getAttribute("data-fieldid");
                const itemId = fieldElement.getAttribute("data-itemid");
                const language = fieldElement.getAttribute("data-language");
                const versionStr = fieldElement.getAttribute("data-version");
                if (!fieldId || !itemId || !language || !versionStr)
                    return;
                const version = parseInt(versionStr, 10);
                // Skip if this field currently has local modifications to avoid stomping fresh edits.
                // In suggestions mode, don't skip - we need to re-apply the merged suggestion after
                // unfocus. The cursor-jump protection above (isFieldActiveInIframe) prevents overwrites
                // while the user is actively typing.
                const isModifiedNow = modifiedFieldsContext?.modifiedFields?.some((m) => m.fieldId === fieldId &&
                    m.item.id === itemId &&
                    m.item.language === language &&
                    m.item.version === version);
                if (isModifiedNow && context.mode !== "suggestions") {
                    return;
                }
                // Build an item descriptor.
                const descriptor = { id: itemId, language, version };
                // Fetch the current item from the repository.
                const loadedItem = await context.itemsRepository.getItem(descriptor);
                if (!loadedItem)
                    return;
                // Get the baseline from repository.
                const repositoryField = loadedItem.fields.find((f) => f.id === fieldId);
                let originalValue = repositoryField
                    ? repositoryField.rawValue || ""
                    : "";
                // If the field is modified locally, use that value.
                const modField = modifiedFieldsContext?.modifiedFields.find((mod) => mod.fieldId === fieldId &&
                    mod.item.id === itemId &&
                    mod.item.language === language &&
                    mod.item.version === version);
                if (modField) {
                    originalValue = modField.value || "";
                }
                // If showSuggestedEdits is false, update with the base value.
                if (!context.showSuggestedEdits && context.mode !== "suggestions") {
                    if (fieldElement.innerHTML !== originalValue) {
                        setInnerHTMLWithStackTrace(fieldElement, originalValue, {
                            source: "updateFieldsWithSuggestions:baselineOnly",
                            fieldId,
                            itemId,
                            language,
                            version,
                        });
                    }
                    return;
                }
                // Otherwise, gather all suggestions for this field.
                const pageId = pageViewContext.pageItemDescriptor?.id;
                const pageVer = pageViewContext.pageItemDescriptor?.version;
                const fieldSuggestions = context.suggestedEdits.filter((s) => s.status === "pending" &&
                    s.fieldId === fieldId &&
                    s.itemId === itemId &&
                    s.mainItemLanguage === language &&
                    s.mainItemId === pageId &&
                    (pageVer != null
                        ? s.mainItemVersion === pageVer || s.mainItemVersion === 0
                        : s.mainItemVersion === version));
                if (!fieldSuggestions.length)
                    return;
                // Sort suggestions in chronological order (oldest first).
                fieldSuggestions.sort((a, b) => new Date(a.created).getTime() - new Date(b.created).getTime());
                // Apply suggestions sequentially to generate the merged value.
                let mergedValue = originalValue;
                let couldApplyPatch = true;
                for (const suggestion of fieldSuggestions) {
                    // Compute a patch from the suggestion's baseline to its intended new value.
                    const patch = createPatch("field", suggestion.oldValue, suggestion.newValue);
                    const patchedCandidate = applyPatch(mergedValue, patch);
                    if (patchedCandidate !== false &&
                        typeof patchedCandidate === "string") {
                        mergedValue = patchedCandidate;
                    }
                    else {
                        couldApplyPatch = false;
                    }
                }
                if (!couldApplyPatch)
                    mergedValue = fieldSuggestions[0]?.newValue || originalValue;
                // If showSuggestedEditsDiff is false, show only the merged text
                if (!context.showSuggestedEditsDiff) {
                    if (fieldElement.innerHTML !== mergedValue) {
                        setInnerHTMLWithStackTrace(fieldElement, mergedValue, {
                            source: "updateFieldsWithSuggestions:mergedNoDiff",
                            fieldId,
                            itemId,
                            language,
                            version,
                        });
                    }
                    return;
                }
                // Compute a word-based diff between originalValue and mergedValue.
                const diffParts = diffWords(originalValue, mergedValue);
                // Build HTML markup from the diff:
                let diffHTML = "";
                diffParts.forEach((part) => {
                    let style = "";
                    if (part.added) {
                        style = "color: green;";
                    }
                    else if (part.removed) {
                        style = "color: red; text-decoration: line-through;";
                    }
                    else {
                        style = "color: gray;";
                    }
                    // Escape any HTML in part.value if needed.
                    diffHTML += `<span style="${style}">${part.value}</span>`;
                });
                // Update the element's innerHTML with the diff markup.
                if (fieldElement.innerHTML !== diffHTML) {
                    setInnerHTMLWithStackTrace(fieldElement, diffHTML, {
                        source: "updateFieldsWithSuggestions:diffMarkup",
                        fieldId,
                        itemId,
                        language,
                        version,
                    });
                }
            });
        };
        updateFieldsWithSuggestions();
    }, [
        context.mode,
        modifiedFieldsContext?.modifiedFields,
        context?.itemsRepository.revision,
        modifiedFieldsContext?.inlineEditingFieldElement,
        context?.showSuggestedEdits,
        context?.suggestedEdits,
        pageViewContext.pageItemDescriptor,
        pageViewContext.page,
        context.showSuggestedEditsDiff,
        context.showSuggestedEdits,
    ]);
    useEffect(() => {
        if (!context || compareView)
            return;
        const iframeDoc = pageViewContext.editorIframe?.contentWindow?.document;
        if (!iframeDoc)
            return;
        // Get current page language and version to filter elements - use currentItemDescriptor as fallback during transitions
        const resetEffectPageLanguage = context.page?.item?.language ?? context.currentItemDescriptor?.language;
        const resetEffectPageVersion = context.page?.item?.version ?? context.currentItemDescriptor?.version;
        // Query all field elements
        const allFieldEls = Array.from(iframeDoc.querySelectorAll("[data-fieldid][data-itemid][data-language][data-version]"));
        allFieldEls.forEach(async (el) => {
            // don't stomp on the one that's live‑editing (only in non-preview modes)
            if (context.mode !== "preview" && isSameFieldKey(el, activeFieldKey))
                return;
            const fieldId = el.getAttribute("data-fieldid");
            const itemId = el.getAttribute("data-itemid");
            const language = el.getAttribute("data-language");
            const version = parseInt(el.getAttribute("data-version"), 10);
            // CRITICAL FIX: Skip elements from other languages or versions during transitions
            // to prevent cross-language/version contamination during rapid switching
            if (resetEffectPageLanguage && language !== resetEffectPageLanguage) {
                return;
            }
            if (resetEffectPageVersion && version !== resetEffectPageVersion) {
                return;
            }
            // In suggestions mode (but not preview), only reset fields that don't have any suggested edits
            if (context.mode === "suggestions" ||
                (context.mode !== "preview" && context.showSuggestedEdits)) {
                const hasActiveSuggestions = context.suggestedEdits.some((edit) => edit.status === "pending" &&
                    edit.fieldId === fieldId &&
                    edit.itemId === itemId &&
                    edit.mainItemLanguage === language &&
                    (resetEffectPageVersion != null
                        ? edit.mainItemVersion === resetEffectPageVersion ||
                            edit.mainItemVersion === 0
                        : edit.mainItemVersion === version));
                // If field has active suggestions, don't reset it (let the other hook handle it)
                if (hasActiveSuggestions)
                    return;
            }
            const realField = await context.itemsRepository.getItem({
                id: fieldId,
                language: "en",
                version: 0,
            });
            const fieldType = realField?.fields.find((f) => f.name === "Type")?.value;
            if (!fieldType || !isTextFieldType(fieldType)) {
                return;
            }
            // lookup baseline
            const loaded = await context.itemsRepository.getItem({
                id: itemId,
                language,
                version,
            });
            const repoF = loaded?.fields.find((f) => f.id === fieldId);
            let value = repoF?.rawValue ?? "";
            // override with any local edit (only in non-preview modes)
            // CRITICAL FIX: Only apply modified fields that match the current page language and version
            // to prevent cross-language/version contamination during rapid switching
            if (context.mode !== "preview" &&
                resetEffectPageLanguage &&
                language === resetEffectPageLanguage &&
                resetEffectPageVersion &&
                version === resetEffectPageVersion) {
                const mod = modifiedFieldsContext?.modifiedFields.find((m) => m.fieldId === fieldId &&
                    m.item.id === itemId &&
                    m.item.language === language &&
                    m.item.version === version);
                if (mod)
                    value = mod.value;
            }
            // write it in
            if (el.innerHTML !== value) {
                setInnerHTMLWithStackTrace(el, value, {
                    source: "resetAllFieldsToBaselineOrLocal",
                    fieldId,
                    itemId,
                    language,
                    version,
                });
            }
        });
    }, [context.mode, context.showSuggestedEdits, context.suggestedEdits]);
    return null;
}
//# sourceMappingURL=InlineEditor.js.map