import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
import { useEffect, useRef, useState } from "react";
import { useEditContext } from "../client/editContext";
import { findComponentRect } from "../utils";
import { useThrottledCallback } from "use-debounce";
import { cn } from "../../lib/utils";
import { PlaceholderDropZone } from "./PlaceholderDropZone";
import { showComponentContextMenu } from "../ContextMenu";
import { getComponentById } from "../componentTreeHelper";
import { Tooltip, TooltipContent, TooltipTrigger, } from "../../components/ui/tooltip";
import { GripVertical, ArrowUpFromDot, Sparkles } from "lucide-react";
import { setMultiDragImage, setComponentDragImage } from "../ui/DragPreview";
export function FrameMenu({ component, compareView, pageViewContext, }) {
    const editContext = useEditContext();
    const resizeObserverRef = useRef(null);
    const mutationObserverRef = useRef(null);
    const headerRef = useRef(null);
    const pendingDragStartTimeoutRef = useRef(null);
    const [commands, setCommands] = useState([]);
    const [componentRect, setComponentRect] = useState();
    const [isHeaderWiderThanComponent, setIsHeaderWiderThanComponent] = useState(false);
    const updatePosition = () => {
        if (!component || !editContext || !pageViewContext)
            return;
        const iframe = pageViewContext.editorIframe;
        if (!iframe)
            return;
        const resetSelection = () => {
            setComponentRect(undefined);
        };
        const componentRect = findComponentRect(iframe, component)?.rect;
        if (!componentRect) {
            resetSelection();
            return;
        }
        setComponentRect(componentRect);
    };
    const updatePositionThrottled = useThrottledCallback(updatePosition, 10, {
        leading: true,
        trailing: true,
    });
    // Cleanup effect to prevent memory leaks and state updates after unmount
    useEffect(() => {
        return () => {
            updatePositionThrottled.cancel?.();
            if (pendingDragStartTimeoutRef.current) {
                clearTimeout(pendingDragStartTimeoutRef.current);
                pendingDragStartTimeoutRef.current = null;
            }
        };
    }, []);
    // Effect to handle changes in the observed element
    useEffect(() => {
        if (component) {
            const iframe = pageViewContext.editorIframe;
            if (!iframe)
                return;
            mutationObserverRef.current = new MutationObserver(() => {
                updatePositionThrottled();
            });
            if (!iframe.contentDocument)
                return;
            mutationObserverRef.current.observe(iframe.contentDocument, {
                childList: true, // observe direct children changes
                subtree: true, // observe all descendants changes
                characterData: true, // observe text changes
                //attributes: true, // observe attribute changes (like style or class)
            });
            // Create a new ResizeObserver instance
            resizeObserverRef.current = new ResizeObserver(() => {
                updatePositionThrottled();
            });
            const scrollContainer = iframe.contentWindow?.document.scrollingElement ||
                iframe.contentWindow?.document.body;
            scrollContainer?.addEventListener("scroll", updatePositionThrottled);
            iframe.contentWindow?.addEventListener("scroll", updatePositionThrottled);
            const componentRect = findComponentRect(iframe, component);
            if (!componentRect) {
                updatePositionThrottled();
                return;
            }
            // Immediately set the rect so frame renders right away
            updatePositionThrottled();
            componentRect.elements.forEach((componentElement) => {
                resizeObserverRef.current.observe(componentElement);
            });
            // Store references for cleanup
            const scrollContainerRef = scrollContainer;
            const iframeWindowRef = iframe.contentWindow;
            return () => {
                if (mutationObserverRef.current) {
                    mutationObserverRef.current.disconnect();
                    mutationObserverRef.current = null;
                }
                if (resizeObserverRef.current) {
                    resizeObserverRef.current.disconnect();
                    resizeObserverRef.current = null;
                }
                // Clean up scroll event listeners
                scrollContainerRef?.removeEventListener("scroll", updatePositionThrottled);
                iframeWindowRef?.removeEventListener("scroll", updatePositionThrottled);
            };
        }
    }, [component]);
    // Effect to measure header width and compare with componentRect
    useEffect(() => {
        if (!headerRef.current || !componentRect)
            return;
        const headerResizeObserver = new ResizeObserver((entries) => {
            for (const entry of entries) {
                const headerWidth = entry.contentRect.width;
                setIsHeaderWiderThanComponent(headerWidth > componentRect.width);
            }
        });
        headerResizeObserver.observe(headerRef.current);
        return () => {
            headerResizeObserver.disconnect();
        };
    }, [componentRect]);
    useEffect(() => {
        const timeoutId = setTimeout(() => {
            updatePositionThrottled();
        }, 100);
        return () => clearTimeout(timeoutId);
    }, [
        pageViewContext?.scroll,
        pageViewContext?.viewport,
        pageViewContext?.device,
        pageViewContext?.fullscreen,
        pageViewContext?.zoom,
    ]);
    if (!component || !editContext)
        return null;
    // const commands = editContext.getComponentCommands([component]);
    useEffect(() => {
        // Load commands asynchronously - don't block rendering
        async function loadCommands() {
            const commands = await editContext.getComponentCommands([component]);
            setCommands(commands);
        }
        // Use requestIdleCallback or setTimeout to defer command loading, allowing frame to render first
        if (typeof requestIdleCallback !== "undefined") {
            requestIdleCallback(() => {
                loadCommands();
            }, { timeout: 0 });
        }
        else {
            // Fallback for browsers without requestIdleCallback
            setTimeout(() => {
                loadCommands();
            }, 0);
        }
    }, [component]);
    // Determine if draggable - layout components should not be draggable when showLayoutComponents is disabled
    const isLayoutReadonlyCheck = component.layoutId && !editContext.showLayoutComponents;
    const isDraggable = component.canBeMoved && !isLayoutReadonlyCheck;
    const commandButtons = commands
        .filter((x) => !editContext.isCommandDisabled({ command: x }) &&
        x.visibilityScopes.indexOf("editFrame") >= 0)
        .map((x) => ({
        id: x.id + "_button",
        icon: x.icon,
        label: x.label,
        onClick: (ev) => {
            ev.stopPropagation();
            ev.preventDefault();
            editContext.executeCommand({
                command: x,
                event: ev,
            });
        },
    }));
    const customButtons = []; // TODO
    const buttons = [...customButtons, ...commandButtons];
    function handleDragStart(event) {
        if (!component?.datasourceItem || !editContext)
            return;
        // Set custom drag image with background for both single and multi-select
        const selectedCount = editContext?.selection?.length || 0;
        if (selectedCount > 1) {
            setMultiDragImage(event, selectedCount, "components");
        }
        else {
            // Calculate color for drag image
            const isShared = component.isShared;
            const isLayout = component.layoutId;
            const isLayoutReadonly = isLayout && !editContext.showLayoutComponents;
            const isReadonly = editContext.mode === "preview" || compareView || isLayoutReadonly;
            let dragColor = "default";
            if (isReadonly && !isLayoutReadonly)
                dragColor = "readonly";
            else if (editContext?.mode === "suggestions")
                dragColor = "suggestions";
            else if (isLayout)
                dragColor = "layout";
            else if (isShared)
                dragColor = "shared";
            else if (component.canBeMoved)
                dragColor = "default";
            else
                dragColor = "nonMovable";
            // Use custom drag image for single component to ensure background is visible
            setComponentDragImage(event, component.name, dragColor);
        }
        // Allow both copy and move from component drag origins
        try {
            event.dataTransfer.effectAllowed = "copyMove";
        }
        catch { }
        event.dataTransfer.setData("componentId", component.id);
        pendingDragStartTimeoutRef.current = setTimeout(() => {
            if (!component?.datasourceItem)
                return;
            const language = pageViewContext.page.item.language;
            const version = pageViewContext.page.item.version;
            const selectedIds = (editContext?.selection?.length || 0) > 1
                ? editContext.selection
                : [component.id];
            const componentsToDrag = selectedIds.map((id) => ({
                id,
                language,
                version,
            }));
            editContext.dragStart({
                type: "component",
                typeId: component.typeId,
                templateId: component.datasourceItem?.templateId,
                name: component.name,
                components: componentsToDrag,
            });
        }, 50);
        event.stopPropagation();
    }
    function handleDragEnd() {
        if (pendingDragStartTimeoutRef.current) {
            clearTimeout(pendingDragStartTimeoutRef.current);
            pendingDragStartTimeoutRef.current = null;
        }
        editContext.dragEnd();
    }
    const isShared = component.isShared;
    const isLayout = component.layoutId;
    // Layout components should be read-only when showLayoutComponents is disabled
    const isLayoutReadonly = isLayout && !editContext.showLayoutComponents;
    const isReadonly = editContext.mode === "preview" || compareView || isLayoutReadonly;
    function getColor() {
        if (isReadonly && !isLayoutReadonly)
            return "readonly";
        if (editContext?.mode === "suggestions")
            return "suggestions";
        if (isLayout)
            return "layout";
        if (isShared)
            return "shared";
        if (component.canBeMoved)
            return "default";
        return "nonMovable";
    }
    const color = getColor();
    const colorVariants = {
        shared: "border-orange-400",
        readonly: "border-gray-400",
        layout: "border-purple-400",
        default: "border-component-blue",
        nonMovable: "border-red-400",
        suggestions: "border-teal-400",
    };
    const bgColorVariants = {
        shared: "bg-orange-400",
        readonly: "bg-gray-400",
        layout: "bg-purple-400",
        default: "bg-component-blue",
        nonMovable: "bg-red-400",
        suggestions: "bg-teal-400",
    };
    const dropzoneColorVariants = {
        shared: "bg-orange-400/40 hover:bg-orange-400",
        readonly: "bg-gray-400/40 hover:bg-gray-400",
        layout: "bg-purple-400/40 hover:bg-purple-400",
        default: "bg-component-blue/40 hover:bg-component-blue",
        nonMovable: "bg-red-400/40 hover:bg-red-400",
        suggestions: "bg-teal-400/40 hover:bg-teal-400",
    };
    // Calculate initial estimation for the header width
    const estimatedHeaderWidth = buttons.length * 30 + component.name.length * 8; // Approximate width calculation
    const initialIsHeaderWiderThanComponent = estimatedHeaderWidth > (componentRect?.width || 0);
    useEffect(() => {
        setIsHeaderWiderThanComponent(initialIsHeaderWiderThanComponent);
    }, [initialIsHeaderWiderThanComponent]);
    const isMultiSelected = editContext.selection.length > 1;
    if (!componentRect)
        return null;
    // Check if the component's placeholder can accept more components
    const placeholder = component.parentPlaceholder;
    const canAppend = !isMultiSelected &&
        placeholder?.editable &&
        placeholder?.insertOptions?.some((option) => !option.isHidden && !option.isInvalid);
    // Calculate position for the append dropzone (bottom center of component)
    const appendPosition = canAppend
        ? {
            x: componentRect.x + componentRect.width / 2,
            y: componentRect.y + componentRect.height,
        }
        : null;
    // Calculate the index after the current component
    const appendIndex = placeholder
        ? placeholder.components.findIndex((c) => c.id === component.id) + 1
        : 0;
    // Determine a real element to anchor the insertion animation (bottom of component)
    const appendAnchorElement = pageViewContext.editorIframe
        ? findComponentRect(pageViewContext.editorIframe, component)?.elements[findComponentRect(pageViewContext.editorIframe, component).elements
            .length - 1]
        : null;
    if (componentRect.width === 0 && componentRect.height === 0) {
        return null;
    }
    const handleHeaderContextMenu = async (e) => {
        if (!editContext)
            return;
        if (e.ctrlKey)
            return;
        e.preventDefault();
        e.stopPropagation();
        const clientX = e.clientX;
        const clientY = e.clientY;
        const { shiftKey, altKey, ctrlKey } = e;
        const selectedComponents = (editContext.selection?.length || 0) > 0
            ? editContext.selection
                .map((id) => pageViewContext.page
                ? getComponentById(id, pageViewContext.page)
                : undefined)
                .filter((x) => x)
            : [component];
        const showMenu = await showComponentContextMenu(selectedComponents, editContext);
        if (showMenu) {
            const syntheticEvent = new MouseEvent("contextmenu", {
                bubbles: true,
                cancelable: true,
                view: window,
                clientX,
                clientY,
                shiftKey,
                altKey,
                ctrlKey,
            });
            showMenu(syntheticEvent);
        }
    };
    return (_jsxs(_Fragment, { children: [_jsx("div", { className: cn("pointer-events-none absolute inset-0 rounded-bl-xs border-2", colorVariants[color], "tour-frame-menu animate-in fade-in zoom-in-95 opacity-70 duration-100", "transition-opacity duration-100 ease-out hover:opacity-100", !isMultiSelected && isHeaderWiderThanComponent && "border-t-0", !isMultiSelected && !isHeaderWiderThanComponent && "rounded-tl-xs", isMultiSelected && "rounded-t-xs"), style: {
                    // Make the surrounding frame 1px larger on all sides
                    left: componentRect.x - 2,
                    top: componentRect.y - 2,
                    width: (componentRect.width ?? 0) + 4,
                    height: componentRect.height + 4,
                    zIndex: 8,
                    transition: "left 0.15s ease-out, top 0.15s ease-out, width 0.15s ease-out, height 0.15s ease-out",
                }, "data-testid": "frame-menu", children: _jsxs("div", { ref: headerRef, className: cn("editframe-menu pointer-events-auto absolute z-1000 flex items-center pr-4 text-base text-white", bgColorVariants[color], "animate-in slide-in-from-top-2 fade-in duration-100", "transition-all duration-100 ease-out", isMultiSelected || componentRect.y - 36 < 0
                        ? "rounded-bl"
                        : "rounded-t", isHeaderWiderThanComponent && "rounded-bl"), style: {
                        right: "-1.5px",
                        top: isMultiSelected || componentRect.y - 36 < 0 ? "0" : undefined,
                        bottom: isMultiSelected
                            ? undefined
                            : componentRect.y - 36 >= 0
                                ? componentRect.height + 1 + "px"
                                : undefined,
                        zIndex: 800,
                        height: "36px",
                        transition: "bottom 0.15s ease-out, top 0.15s ease-out",
                    }, onContextMenu: handleHeaderContextMenu, children: [_jsxs("div", { draggable: isDraggable, onDragStart: handleDragStart, onDragEnd: handleDragEnd, className: cn("group flex items-center gap-1 p-2 text-sm text-nowrap", "transition-all duration-150 ease-out", isDraggable ? "cursor-move" : "cursor-default", !isDraggable && "pl-6"), children: [isDraggable && (_jsx(GripVertical, { strokeWidth: 1, className: "h-4 w-4 transition-transform duration-150 ease-out" })), component.name, component.hasPersonalization && (_jsxs(Tooltip, { delayDuration: 500, children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx("span", { className: "inline-flex h-4 w-4 items-center justify-center rounded-full bg-white/20", "data-testid": "component-personalization-badge", children: _jsx(Sparkles, { height: 12, width: 12 }) }) }), _jsx(TooltipContent, { children: "Personalized" })] })), component.parentPlaceholder?.parentComponent && (_jsxs(Tooltip, { delayDuration: 500, children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx("span", { className: "cursor-pointer opacity-0 transition-opacity group-hover:opacity-100", onClick: () => {
                                                    editContext.select([
                                                        component.parentPlaceholder?.parentComponent.id || "",
                                                    ]);
                                                }, children: _jsx(ArrowUpFromDot, { height: 14, width: 14 }) }) }), _jsx(TooltipContent, { children: "Select parent" })] }))] }), editContext.mode === "edit" && !isMultiSelected && (_jsx("div", { className: "flex items-center gap-2.5 text-sm", children: buttons.map((b, i) => (_jsxs(Tooltip, { delayDuration: 500, children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx("div", { className: "cursor-pointer transition-all duration-150 ease-out hover:scale-110 hover:text-gray-200", onClick: (ev) => {
                                                ev.stopPropagation();
                                                b.onClick(ev);
                                            }, "data-testid": `frame-menu-${b.id}`, children: typeof b.icon === "string" ? (_jsx("i", { className: b.icon + " cursor-pointer text-sm" })) : (b.icon) }) }), _jsx(TooltipContent, { children: b.label })] }, i))) }))] }) }), canAppend &&
                !isReadonly &&
                editContext.mode === "edit" &&
                placeholder &&
                appendPosition &&
                appendAnchorElement && (_jsx(PlaceholderDropZone, { className: `${dropzoneColorVariants[color]} rounded-md`, insertMenuTitle: "Append", placeholder: placeholder, description: `Append to ${placeholder.name}`, index: appendIndex, parentName: component.name, spotPosition: appendPosition, spotPositionElement: appendAnchorElement, spotPositionAnchor: "bottom", size: "small", typeIdOnCtrlClick: component.typeId }))] }));
}
//# sourceMappingURL=FrameMenu.js.map