import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
import { useEditContext } from "../../client-components";
import { useRef, useState, useEffect, useMemo } from "react";
import { createPortal } from "react-dom";
import { FilterInput, highlightMatch } from "../../components/FilterInput";
import { ScrollingContentTree } from "../ScrollingContentTree";
import { SimpleTabs } from "../ui/SimpleTabs";
import ItemSearch from "../ui/ItemSearch";
import { templatesRootItemId } from "../../config/config";
import { ArrowRight, Plus, TriangleAlert } from "lucide-react";
import { localStorageService } from "../services/localStorageService";
function normalizeTemplateId(value) {
    return (value ?? "").replace(/[{}]/g, "").toLowerCase();
}
function normalizePath(value) {
    return (value ?? "").trim().toLowerCase();
}
// Separate component for the Insert Menu panel to use portal
function InsertMenuPanel({ triggerRef, isPanelOpen, tabs, activeTab, setActiveTab, isAnyTabLoading, }) {
    const [position, setPosition] = useState(null);
    useEffect(() => {
        if (!isPanelOpen || !triggerRef.current) {
            setPosition(null);
            return;
        }
        // Get the position of the trigger element
        const triggerRect = triggerRef.current.getBoundingClientRect();
        if (!triggerRect)
            return;
        const PANEL_WIDTH = 450;
        const PANEL_HEIGHT = 380;
        const PADDING = 8;
        const viewportWidth = window.innerWidth;
        const viewportHeight = window.innerHeight;
        let left = triggerRect.right;
        let top = triggerRect.top - 50;
        // Check if panel would overflow right edge
        if (left + PANEL_WIDTH > viewportWidth - PADDING) {
            left = triggerRect.left - PANEL_WIDTH;
            if (left < PADDING) {
                left = PADDING;
            }
        }
        // Check if panel would overflow bottom edge
        if (top + PANEL_HEIGHT > viewportHeight - PADDING) {
            top = viewportHeight - PANEL_HEIGHT - PADDING;
        }
        // Check if panel would overflow top edge
        if (top < PADDING) {
            top = PADDING;
        }
        setPosition({ top, left });
    }, [isPanelOpen, triggerRef]);
    if (!isPanelOpen || !position)
        return null;
    const panel = (_jsx("div", { "data-testid": "insert-menu-panel", className: "z-50 rounded-md bg-white shadow-lg", onKeyDownCapture: (ev) => {
            ev.stopPropagation();
        }, onKeyUpCapture: (ev) => {
            ev.stopPropagation();
        }, 
        // Important: do NOT stop propagation in capture phase here.
        // React capture stopPropagation prevents descendants (e.g. tree expand toggles using onMouseDown)
        // from receiving the event. We only need to stop bubbling so outer context menus don't close.
        onPointerDown: (ev) => {
            ev.stopPropagation();
        }, onMouseDown: (ev) => {
            ev.stopPropagation();
        }, onPointerUp: (ev) => {
            ev.stopPropagation();
        }, onMouseUp: (ev) => {
            ev.stopPropagation();
        }, onClick: (ev) => {
            ev.stopPropagation();
        }, style: {
            position: "fixed",
            top: position.top,
            left: position.left,
            minWidth: "450px",
            height: "380px",
            border: "1px solid #ddd",
        }, children: _jsxs("div", { className: "flex h-full flex-col px-4 pb-4 text-xs", children: [_jsx("div", { className: "flex-shrink-0", children: _jsx(SimpleTabs, { className: "border-gray-3 mb-2 border-b", tabClassName: "pt-4", tabs: tabs, setActiveTab: setActiveTab, activeTab: activeTab, isLoading: isAnyTabLoading, hideContent: true }) }), _jsx("div", { className: "min-h-0 flex-1 overflow-hidden", children: tabs[activeTab]?.content })] }) }));
    return createPortal(panel, document.body);
}
export const InsertMenuTemplate = ({ insertOptions, item, commandCallback, mode = "overlay", }) => {
    const editContext = useEditContext();
    const [filter, setFilter] = useState("");
    const filterRef = useRef(null);
    const [activeTab, setActiveTab] = useState(0);
    const [isTabHydrated, setIsTabHydrated] = useState(false);
    // Load saved tab preference after hydration to avoid SSR mismatch
    useEffect(() => {
        const stored = localStorageService.getString("editor.defaultInsertTab");
        if (stored) {
            setActiveTab(parseInt(stored));
        }
        setIsTabHydrated(true);
    }, []);
    // Save tab preference when it changes (only after initial hydration)
    useEffect(() => {
        if (isTabHydrated) {
            localStorageService.setString("editor.defaultInsertTab", activeTab.toString());
        }
    }, [activeTab, isTabHydrated]);
    const [recentTemplates, setRecentTemplates] = useState([]);
    const [recentItems, setRecentItems] = useState([]);
    const [hoveredItem, setHoveredItem] = useState();
    // Loading states
    const [isLoadingRecent, setIsLoadingRecent] = useState(false);
    const hideTimeoutRef = useRef(undefined);
    const triggerRef = useRef(null);
    const [isPanelOpen, setIsPanelOpen] = useState(false);
    const allowedTemplateIds = useMemo(() => new Set((insertOptions || [])
        .map((option) => normalizeTemplateId((option.id ?? option.typeId ?? "")))
        .filter(Boolean)), [insertOptions]);
    useEffect(() => {
        const loadRecentItems = async () => {
            if (recentTemplates.length === 0)
                return;
            setIsLoadingRecent(true);
            try {
                const items = await editContext?.itemsRepository.getItems(recentTemplates.map((id) => ({
                    id,
                    version: 0,
                    language: "en",
                })));
                if (items) {
                    // Remove duplicates based on item.id to prevent React key conflicts
                    const uniqueItems = items.filter((item, index, self) => self.findIndex((i) => i.id === item.id) === index);
                    setRecentItems(uniqueItems);
                }
            }
            finally {
                setIsLoadingRecent(false);
            }
        };
        loadRecentItems();
    }, [recentTemplates]);
    useEffect(() => {
        const stored = localStorageService.getOrSetItem("editor.recentTemplates", []);
        // Ensure the parsed data is a string array and remove duplicates
        const templateIds = Array.isArray(stored)
            ? stored.filter((id) => typeof id === "string")
            : [];
        const uniqueTemplates = [...new Set(templateIds)];
        setRecentTemplates(uniqueTemplates);
        // Update localStorage if duplicates were found or invalid data was cleaned
        if (uniqueTemplates.length !== templateIds.length ||
            templateIds.length !== stored.length) {
            localStorageService.setItem("editor.recentTemplates", uniqueTemplates);
        }
    }, [insertOptions]);
    // No wizard-specific loading here; wizards come via generic insert options
    if (!editContext)
        return null;
    const getCommand = (x) => {
        return async (triggerEvent) => {
            // Proactively close any open overlays (e.g., context menu) before proceeding
            try {
                editContext?.setCurrentOverlay?.(undefined);
                // Also simulate an outside click so Radix ContextMenu closes without using Escape
                document.dispatchEvent(new MouseEvent("mousedown", { bubbles: true }));
                document.dispatchEvent(new MouseEvent("mouseup", { bubbles: true }));
                document.dispatchEvent(new MouseEvent("click", { bubbles: true }));
            }
            catch { }
            // Yield to event loop to ensure menu has closed before opening dialog
            await new Promise((r) => setTimeout(r, 0));
            const recentIds = localStorageService.getOrSetItem("editor.recentTemplates", []);
            const optionId = x.id ?? x.typeId;
            const updatedIds = [
                optionId,
                ...recentIds.filter((id) => id !== optionId),
            ]
                .filter((id) => id != null)
                .slice(0, 10);
            localStorageService.setItem("editor.recentTemplates", updatedIds);
            setRecentTemplates(updatedIds);
            const cfg = editContext.configuration;
            const runCommandTemplate = async (commandId, details) => {
                const cmd = cfg?.commands?.[commandId];
                if (!cmd) {
                    editContext.showErrorToast({
                        summary: "Unsupported command template",
                        details: `The command template '${x.name ?? optionId ?? "Unknown"}' is configured to run ` +
                            `'${commandId}', but no such command is registered in the editor.`,
                    });
                    return;
                }
                await editContext.executeCommand({
                    command: cmd,
                    data: {
                        ...(details ?? {}),
                        item: item.descriptor,
                        templateId: optionId,
                    },
                    event: triggerEvent,
                });
            };
            // If option carries an action, execute the corresponding command
            if (x?.action?.type) {
                const action = x.action;
                await runCommandTemplate(action.type, action.data ?? {});
                return;
            }
            const mappedCommand = cfg?.commandTemplates?.byTypeId?.[normalizeTemplateId(optionId)];
            const matchesMappedCommand = !!mappedCommand?.commandId &&
                (!mappedCommand.optionName || mappedCommand.optionName === x.name) &&
                (!mappedCommand.parentPathPrefix ||
                    normalizePath(item.path).startsWith(normalizePath(mappedCommand.parentPathPrefix)));
            if (matchesMappedCommand) {
                await runCommandTemplate(mappedCommand.commandId);
                return;
            }
            if (x.isCommandTemplate) {
                editContext.showErrorToast({
                    summary: "Unsupported command template",
                    details: `The command template '${x.name ?? optionId ?? "Unknown"}' is not supported by this editor.`,
                });
                return;
            }
            const normalizedOptionId = normalizeTemplateId(optionId);
            if (!allowedTemplateIds.has(normalizedOptionId)) {
                const proceed = await new Promise((resolve) => {
                    editContext.confirm({
                        showCancel: true,
                        message: "This template is not listed in the insert options of the selected parent. Insert anyway?",
                        header: "Template not allowed under selected parent",
                        icon: _jsx(TriangleAlert, { size: 28 }),
                        accept: () => resolve(true),
                        reject: () => resolve(false),
                    });
                });
                if (!proceed)
                    return;
            }
            const result = await editContext?.executeCommand({
                command: editContext.configuration.commands.insertItem,
                data: { items: [item], templateId: optionId },
                event: triggerEvent,
            });
            if (commandCallback)
                commandCallback(editContext?.configuration.commands.insertItem, result);
        };
    };
    const options = insertOptions.length === 0
        ? undefined
        : insertOptions
            .filter((x, index, self) => self.findIndex((item) => (item.id ?? item.typeId) ===
            (x.id ?? x.typeId)) === index) // Remove duplicates using id or typeId
            .map((x) => ({
            label: x.name,
            id: x.id ?? x.typeId,
            icon: (_jsx("img", { src: x.icon, style: { height: "16px" }, width: "16", height: "16", onDragStart: (e) => e.preventDefault() })),
            command: getCommand(x),
        }));
    const tabs = [
        {
            label: "Options",
            testId: "insert-menu-tab-options",
            content: (_jsx("div", { className: "flex h-full flex-col gap-2", children: options ? (_jsxs(_Fragment, { children: [_jsx(FilterInput, { ref: filterRef, className: "w-full text-xs", placeholder: "Filter", value: filter, onChange: setFilter }), _jsx("div", { className: "relative flex-1", children: _jsx("div", { className: "absolute inset-0 overflow-auto", children: options
                                    .filter((x) => x.label.toLowerCase().indexOf(filter.toLowerCase()) >
                                    -1)
                                    .map((option) => (_jsxs("div", { className: "flex cursor-pointer items-center gap-2 p-1 text-xs hover:bg-gray-100", onClick: (ev) => {
                                        ev.stopPropagation();
                                        ev.preventDefault();
                                        option.command(ev);
                                    }, children: [option.icon, _jsx("span", { className: "flex-1", children: highlightMatch(option.label, filter) })] }, option.id))) }) })] })) : (_jsx("div", { className: "p-2 text-xs text-gray-500", children: "No options available." })) })),
            id: "options",
        },
        {
            label: "Recent",
            testId: "insert-menu-tab-recent",
            content: (_jsx("div", { className: "flex h-full flex-col", children: recentItems.length === 0 ? (_jsx("div", { className: "p-2 text-xs text-gray-500", children: "No recent templates" })) : (recentItems.map((template) => (_jsxs("div", { className: "flex cursor-pointer items-center gap-2 p-1 text-xs hover:bg-gray-100", onClick: (ev) => {
                        ev.stopPropagation();
                        ev.preventDefault();
                        getCommand(template)(ev);
                    }, children: [_jsx("img", { src: template.icon, style: { height: "16px" }, width: "16", height: "16", onDragStart: (e) => e.preventDefault() }), _jsx("span", { className: "flex-1", children: template.name })] }, template.id)))) })),
            id: "recent",
        },
        {
            label: "From Template",
            testId: "insert-menu-tab-from-template",
            content: (_jsxs("div", { className: "flex h-full w-full flex-col gap-2", children: [_jsx(ItemSearch, { setHoveredItem: setHoveredItem, rootItemIds: [templatesRootItemId], itemSelected: (item) => {
                            getCommand(item)(null);
                        } }), _jsx("div", { className: "relative min-h-0 flex-1", children: _jsx(ScrollingContentTree, { selectedItemId: hoveredItem?.id ?? item?.id, rootItemId: templatesRootItemId, onSelectionChange: (node) => {
                                let item = node;
                                if (Array.isArray(node) && node[0]) {
                                    item = node[0];
                                }
                                if (!item ||
                                    (item.templateId !== "ab86861a-6030-46c5-b394-e8f99e8b87db" &&
                                        item.templateId !== "35e75c72-4985-4e09-88c3-0eac6cd1e64f"))
                                    return;
                                getCommand(item)(null);
                            } }) })] })),
            id: "fromTemplate",
        },
    ];
    // No separate "Wizards" tab; wizard options arrive via generic insert options
    const isAnyTabLoading = isLoadingRecent;
    if (mode === "inline") {
        return (_jsx("div", { className: "flex h-[380px] min-w-[450px] flex-col p-2", children: _jsxs("div", { className: "flex min-h-0 flex-1 flex-col text-xs", children: [_jsx("div", { className: "flex-shrink-0", children: _jsx(SimpleTabs, { className: "border-gray-3 mb-2 border-b", tabs: tabs, setActiveTab: setActiveTab, activeTab: activeTab, isLoading: isAnyTabLoading, hideContent: true }) }), _jsx("div", { className: "min-h-0 flex-1 overflow-hidden", children: tabs[activeTab]?.content })] }) }));
    }
    return (_jsxs("div", { ref: triggerRef, "data-testid": "insert-menu-trigger", className: "flex items-center gap-2 text-xs", style: { display: "flex", alignItems: "center", cursor: "pointer" }, onMouseEnter: () => {
            if (hideTimeoutRef.current) {
                clearTimeout(hideTimeoutRef.current);
            }
            setIsPanelOpen(true);
            // Do not focus immediately to avoid interfering with outer menu focus handling
            setTimeout(() => filterRef.current && filterRef.current.focus(), 0);
        }, onMouseLeave: () => {
            hideTimeoutRef.current = setTimeout(() => {
                setIsPanelOpen(false);
            }, 200);
        }, onClick: (ev) => {
            ev.stopPropagation();
            ev.preventDefault();
        }, 
        // Bubble-phase propagation blocking keeps Radix menus open without breaking child onMouseDown handlers
        // (e.g. the tree expand chevron uses onMouseDown).
        onPointerDown: (ev) => {
            // Keep parent context menu open when interacting with the insert panel
            ev.stopPropagation();
        }, onMouseDown: (ev) => {
            ev.stopPropagation();
        }, onPointerUp: (ev) => {
            ev.stopPropagation();
        }, onMouseUp: (ev) => {
            ev.stopPropagation();
        }, children: [_jsx(Plus, { strokeWidth: 1, size: 16, className: "text-gray-2" }), _jsx("span", { children: "Insert" }), _jsx(ArrowRight, { strokeWidth: 1, size: 16, className: "text-gray-2 ml-auto" }), _jsx(InsertMenuPanel, { triggerRef: triggerRef, isPanelOpen: isPanelOpen, tabs: tabs, activeTab: activeTab, setActiveTab: setActiveTab, isAnyTabLoading: isAnyTabLoading })] }));
};
//# sourceMappingURL=InsertMenu.js.map