import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
import React, { useEffect, useRef, useState } from "react";
import { flushSync } from "react-dom";
import { useEditContext, useFieldsEditContext } from "../client/editContext";
import { deleteSuggestedEdit, createOrUpdateSuggestedEdit, } from "../services/suggestedEditsService";
import { cleanupPendingSuggestedEditOperations, waitForPendingSuggestedEditSave, } from "../client/operations";
import { Button } from "../../components/ui/button";
import { formatDate } from "../utils";
import { SimpleIconButton } from "../ui/SimpleIconButton";
import { Popover, PopoverContent, PopoverTrigger, } from "../../components/ui/popover";
import { DiffView } from "./DiffView";
// Import lucide icons (adjust names as needed)
import { Trash2, GalleryVertical, Check, Brush } from "lucide-react";
// Import patch functions from the diff library.
import { createPatch, applyPatch } from "diff";
import { cn } from "../../lib/utils";
export function SuggestedEditComponent({ edit }) {
    const editContext = useEditContext();
    const fieldsContext = useFieldsEditContext();
    const ref = useRef(null);
    const [item, setItem] = useState(null);
    const [patchPossible, setPatchPossible] = useState(true);
    const [deletePopoverOpen, setDeletePopoverOpen] = useState(false);
    const [patchWarning, setPatchWarning] = useState("");
    const [applied, setApplied] = useState(false);
    const [isApplying, setIsApplying] = useState(false);
    const [ignoreFormatting, setIgnoreFormatting] = useState(true);
    const [clipUnchanged, setClipUnchanged] = useState(true);
    const canApply = edit.status !== "applied" && !editContext?.readonly && !isApplying;
    // Load the full item from the repository.
    useEffect(() => {
        if (editContext?.itemsRepository &&
            edit.itemId &&
            edit.mainItemLanguage &&
            edit.mainItemVersion !== undefined) {
            editContext.itemsRepository
                .getItem({
                id: edit.itemId,
                language: edit.mainItemLanguage,
                version: edit.mainItemVersion,
            })
                .then((loadedItem) => {
                setItem(loadedItem);
                // When the item loads, check if the patch is applicable.
                checkAndComputePatch(loadedItem);
            })
                .catch((err) => {
                console.error("Error loading item:", err);
            });
        }
    }, [
        edit.itemId,
        edit.mainItemLanguage,
        edit.mainItemVersion,
        editContext?.itemsRepository,
    ]);
    // Determine if this suggested edit is selected based on the focused field.
    const isSelected = fieldsContext?.focusedField &&
        fieldsContext.focusedField.fieldId === edit.fieldId &&
        fieldsContext.focusedField.item.id === edit.itemId;
    useEffect(() => {
        if (isSelected && ref.current) {
            ref.current.scrollIntoView({ behavior: "smooth", block: "nearest" });
        }
    }, [isSelected]);
    // When a suggested edit is clicked, update the focused field and select the item.
    function handleSelectSuggestedEdit() {
        if (edit.fieldId) {
            fieldsContext?.setFocusedField({
                fieldId: edit.fieldId,
                item: {
                    id: edit.itemId,
                    language: edit.mainItemLanguage,
                    version: edit.mainItemVersion,
                },
            }, false);
            // Also select the item.
            editContext?.select?.([edit.itemId]);
        }
    }
    // Render contextual info by retrieving item and field names from the loaded item.
    const renderContextInfo = () => {
        const pageName = editContext?.currentItemDescriptor?.name;
        const itemName = item ? item.name : undefined;
        const fieldName = item && item.fields
            ? item.fields.find((f) => f.id === edit.fieldId)?.name || undefined
            : undefined;
        const segments = [pageName, itemName, fieldName].filter((x) => !!x && x.length > 0);
        if (segments.length === 0)
            return null;
        return (_jsx("div", { className: "mt-3 flex items-center border-t pt-3 text-xs", children: segments.map((seg, idx) => (_jsxs(React.Fragment, { children: [idx > 0 && _jsx("div", { className: "text-2xs mx-2 text-gray-500", children: ">" }), _jsx("div", { className: "text-2xs text-gray-500", children: seg })] }, idx))) }));
    };
    // Allow deletion only if the current user is the author.
    const canDelete = edit.author === editContext?.user?.name;
    // Render the header with author info, creation date, and control buttons.
    const renderHeader = () => {
        return (_jsxs("div", { className: "mb-3 flex items-start justify-between", children: [_jsxs("div", { children: [_jsx("div", { className: "text-xs font-bold text-gray-900", title: edit.author, children: edit.authorDisplayName || edit.author }), _jsx("div", { className: "text-xs text-gray-500", children: edit.created ? formatDate(new Date(edit.created)) : "" })] }), _jsxs("div", { className: "flex items-center gap-1", children: [canApply && !applied && patchPossible && (_jsx(SimpleIconButton, { className: "text-gray-500", icon: _jsx(Check, { size: 14 }), label: "Apply", onClick: handleApplyPatch })), applied && (_jsx("span", { title: "Applied by " +
                                edit.updatedBy +
                                " (" +
                                formatDate(new Date(edit.updated)) +
                                ")", children: _jsx(Check, { className: "h-4 w-4 px-1 font-bold text-green-500" }) })), canDelete && (_jsxs(Popover, { open: deletePopoverOpen, onOpenChange: setDeletePopoverOpen, children: [_jsx(PopoverTrigger, { asChild: true, children: _jsx("button", { className: "cursor-pointer rounded-full p-1 text-gray-500 hover:bg-gray-100", title: "Delete", children: _jsx(Trash2, { size: 14 }) }) }), _jsx(PopoverContent, { className: "w-auto p-2", align: "end", children: _jsx(Button, { variant: "outline", onClick: async () => {
                                            try {
                                                await deleteSuggestedEdit(edit);
                                                // Clean up pending operations to prevent race conditions
                                                cleanupPendingSuggestedEditOperations(edit, editContext?.user);
                                                // Immediately update local state to reflect deletion
                                                if (editContext?.setSuggestedEdits &&
                                                    editContext?.suggestedEdits) {
                                                    const updatedEdits = editContext.suggestedEdits.filter((e) => e.id !== edit.id);
                                                    editContext.setSuggestedEdits(updatedEdits);
                                                }
                                            }
                                            catch (error) {
                                                console.error("Failed to delete suggested edit:", error);
                                            }
                                            finally {
                                                setDeletePopoverOpen(false);
                                            }
                                        }, children: "Delete" }) })] }))] })] }));
    };
    // Render toggle buttons using SimpleIconButtons.
    const renderDiffToggleButtons = () => {
        return (_jsxs("div", { className: "mb-2 flex gap-2", children: [_jsx(SimpleIconButton, { icon: _jsx(Brush, { size: 14, className: "p-0.5" }), label: "Ignore Formatting", onClick: () => setIgnoreFormatting((prev) => !prev), className: cn("text-gray-500", ignoreFormatting ? "bg-gray-200" : "") }), _jsx(SimpleIconButton, { icon: _jsx(GalleryVertical, { size: 14, className: "p-0.5" }), label: "Clip", onClick: () => setClipUnchanged((prev) => !prev), className: cn("text-gray-500", clipUnchanged ? "bg-gray-200" : "") })] }));
    };
    // Helper: Check if the patch is applicable to the current field value.
    async function checkAndComputePatch(loadedItem) {
        const field = loadedItem?.fields?.find((f) => f.id === edit.fieldId);
        if (!field)
            return;
        const currentValue = field.rawValue || "";
        const patch = createPatch("field", edit.oldValue, edit.newValue);
        const patchedCandidate = applyPatch(currentValue, patch);
        if (patchedCandidate === false || typeof patchedCandidate !== "string") {
            setPatchPossible(false);
            setPatchWarning("Patch cannot be applied cleanly.");
        }
        else {
            setPatchPossible(true);
            setPatchWarning("");
        }
    }
    const runInEditMode = (action, actionDescription) => {
        if (!editContext)
            return;
        if (editContext.mode === "edit") {
            void action();
            return;
        }
        editContext.confirm({
            header: "Switch to Edit Mode?",
            message: `Applying suggestions requires edit mode. Switch to edit mode to ${actionDescription}?`,
            acceptLabel: "Switch and apply",
            rejectLabel: "Cancel",
            accept: async () => {
                flushSync(() => {
                    editContext.setMode("edit");
                });
                await action();
            },
        });
    };
    // Handler for applying the patch using editContext.operations.editField.
    async function applySuggestedPatch() {
        if (!patchPossible || isApplying)
            return;
        if (!editContext)
            return;
        setIsApplying(true);
        try {
            await waitForPendingSuggestedEditSave(edit);
            // Recalculate the patch immediately before applying
            const field = item?.fields?.find((f) => f.id === edit.fieldId);
            if (!field)
                return;
            const currentValue = field.rawValue || "";
            const patch = createPatch("field", edit.oldValue, edit.newValue);
            const patchedCandidate = applyPatch(currentValue, patch);
            if (patchedCandidate === false || typeof patchedCandidate !== "string") {
                setPatchWarning("Patch cannot be applied cleanly to current field value.");
                return;
            }
            await editContext.operations.editField({
                field: {
                    fieldId: edit.fieldId,
                    item: {
                        id: edit.itemId,
                        language: edit.mainItemLanguage,
                        version: edit.mainItemVersion,
                    },
                },
                value: patchedCandidate,
                rawValue: patchedCandidate,
                forceMode: "edit",
                refresh: "immediate",
            });
            // Update the suggestion status to "Applied" and persist the update.
            const updatedEdit = { ...edit, status: "applied" };
            await createOrUpdateSuggestedEdit(updatedEdit);
            setApplied(true);
            setPatchWarning("");
        }
        finally {
            setIsApplying(false);
        }
    }
    // Handler for replacing the field content completely using editContext.operations.editField.
    async function replaceFieldCompletely() {
        if (!editContext || isApplying)
            return;
        setIsApplying(true);
        try {
            await waitForPendingSuggestedEditSave(edit);
            await editContext.operations.editField({
                field: {
                    fieldId: edit.fieldId,
                    item: {
                        id: edit.itemId,
                        language: edit.mainItemLanguage,
                        version: edit.mainItemVersion,
                    },
                },
                value: edit.newValue,
                rawValue: edit.newValue,
                forceMode: "edit",
                refresh: "immediate",
            });
            const updatedEdit = { ...edit, status: "applied" };
            await createOrUpdateSuggestedEdit(updatedEdit);
            setApplied(true);
            setPatchWarning("");
        }
        finally {
            setIsApplying(false);
        }
    }
    function handleApplyPatch() {
        if (!patchPossible || !editContext)
            return;
        runInEditMode(applySuggestedPatch, "apply this suggestion");
    }
    function handleReplaceCompletely() {
        if (!editContext)
            return;
        runInEditMode(replaceFieldCompletely, "replace the field content");
    }
    return (_jsxs("div", { ref: ref, "data-testid": "suggested-edit", className: `mb-3 cursor-pointer rounded-lg border-2 bg-white p-3 shadow-sm hover:bg-gray-50 ${isSelected ? "border-blue-500" : "border-transparent"}`, onClick: handleSelectSuggestedEdit, children: [renderHeader(), renderDiffToggleButtons(), _jsxs("div", { className: "text-sm whitespace-pre-wrap text-gray-700", children: [_jsx(DiffView, { oldText: edit.oldValue, newText: edit.newValue, ignoreFormatting: ignoreFormatting, clipUnchanged: clipUnchanged, clipThreshold: 50, clipContext: 10 }), canApply && patchWarning && (_jsxs("div", { className: "mt-1 text-xs text-red-500", children: [patchWarning, _jsx("a", { className: "ml-2 cursor-pointer underline", onClick: handleReplaceCompletely, children: "Click here to replace the field content." })] }))] }), renderContextInfo()] }, edit.id));
}
//# sourceMappingURL=SuggestedEdit.js.map