import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
import { useCallback, useEffect, useRef, useState } from "react";
import { createPortal } from "react-dom";
import { MessageCirclePlus, PenLine, Sparkles } from "lucide-react";
import { Tooltip, TooltipContent, TooltipTrigger, } from "../../components/ui/tooltip";
import { useEditContext } from "../client/editContext";
import { LinkEditorDialog, } from "../LinkEditorDialog";
import { generateInternalLinkUrl } from "../field-types/richtext/utils/plugins";
import { normalizeUrl } from "../utils/urlUtils";
import { getInlineAiTextEditShortcutLabel, INLINE_AI_TEXT_EDIT_LABEL, } from "../ai/inlineAiTextEditLabels";
import { calculateDialogPosition } from "../ai/useInlineAiPosition";
import { getBridgeRichTextProfileGroups, loadParsedRichTextProfile, } from "../field-types/richtext/bridgeRichTextProfile";
import { SLATE_MARKS } from "../field-types/richtext/types";
import { getRichTextLinkIcon, getRichTextListIcon, getRichTextMarkIcon, getRichTextStripFormattingIcon, } from "../field-types/richtext/richTextToolbarIcons";
import { calculateBridgeInlineFormatToolbarLayout, getInlineAiAnchorBelowToolbar, } from "./bridgeInlineFormatToolbarLayout";
import { showCommentPopoverForTrigger } from "../reviews/CommentPopover";
import { enterSuggestionsMode, exitSuggestionsMode, isSuggestionsModeAvailable, } from "../reviews/openCommentsFeedback";
/**
 * Custom event the AI text-edit button dispatches to open the inline AI dialog.
 * InlineAiTrigger listens for this and snapshots the current selection.
 */
const INLINE_AI_OPEN_EVENT = "inline-ai-open";
const BRIDGE_INLINE_LINK_OPEN_EVENT = "bridge-inline-link-open";
const OPEN_MODAL_DIALOG_OVERLAY_SELECTOR = [
    '[data-slot="dialog-overlay"][data-state="open"]',
    '[data-slot="alert-dialog-overlay"][data-state="open"]',
].join(",");
const LIST_BUTTONS = {
    "ordered-list": {
        id: "ordered-list",
        label: "Numbered list",
        icon: getRichTextListIcon("ordered-list"),
        command: "insertOrderedList",
    },
    "unordered-list": {
        id: "unordered-list",
        label: "Bulleted list",
        icon: getRichTextListIcon("unordered-list"),
        command: "insertUnorderedList",
    },
};
const STRIP_FORMATTING_BUTTON = {
    id: "strip-formatting",
    label: "Strip formatting",
    icon: getRichTextStripFormattingIcon(),
    command: "removeFormat",
};
const ICON_BUTTON_BASE_CLASS = "text-neutral-grey-100 hover:bg-white inline-flex h-8 w-8 items-center justify-center rounded-[4px] p-0 focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-blue-600";
const INACTIVE_ICON_BUTTON_CLASS = "bg-neutral-grey-5";
const ACTIVE_ICON_BUTTON_CLASS = "bg-white shadow-sm";
const TOOL_GROUP_CLASS = "bg-neutral-grey-5 inline-flex items-center gap-px rounded-md p-px";
const TOOLTIP_SIDE_OFFSET = 8;
function isMacShortcutPlatform() {
    if (typeof navigator === "undefined")
        return false;
    return /Mac|iPhone|iPad|iPod/i.test(navigator.userAgent);
}
function formatHotkeyLabel(hotkey) {
    if (!hotkey)
        return undefined;
    const isMac = isMacShortcutPlatform();
    return hotkey
        .split("+")
        .map((part) => {
        const normalized = part.toLowerCase();
        if (normalized === "mod")
            return isMac ? "⌘" : "Ctrl";
        if (normalized === "shift")
            return isMac ? "⇧" : "Shift";
        if (normalized === "alt")
            return isMac ? "⌥" : "Alt";
        return part.length === 1 ? part.toUpperCase() : part;
    })
        .join(isMac ? "" : "+");
}
function getToolbarButtonShortcut(button) {
    if (button.id in SLATE_MARKS) {
        const markConfig = SLATE_MARKS[button.id];
        return "hotkey" in markConfig
            ? formatHotkeyLabel(markConfig.hotkey)
            : undefined;
    }
    if (button.id === "ai-text-edit") {
        return getInlineAiTextEditShortcutLabel();
    }
    return undefined;
}
function ToolbarButtonTooltip({ label, shortcut, children, }) {
    return (_jsxs(Tooltip, { delayDuration: 250, children: [_jsx(TooltipTrigger, { asChild: true, children: children }), _jsx(TooltipContent, { side: "top", sideOffset: TOOLTIP_SIDE_OFFSET, className: "z-[1100] px-2 py-1.5 text-[11px] whitespace-nowrap", children: _jsxs("div", { className: "flex items-center gap-2", children: [_jsx("span", { className: "font-medium", children: label }), shortcut && (_jsx("kbd", { className: "bg-primary-foreground/10 rounded px-1.5 py-0.5 font-mono text-[10px] leading-none font-medium", children: shortcut }))] }) })] }));
}
function hasOpenModalDialogOverlay() {
    if (typeof document === "undefined") {
        return false;
    }
    return !!document.querySelector(OPEN_MODAL_DIALOG_OVERLAY_SELECTOR);
}
function useOpenModalDialogOverlay() {
    const [open, setOpen] = useState(hasOpenModalDialogOverlay);
    useEffect(() => {
        if (typeof document === "undefined" ||
            typeof MutationObserver === "undefined") {
            return;
        }
        const updateOpenState = () => {
            const nextOpen = hasOpenModalDialogOverlay();
            setOpen((previousOpen) => previousOpen === nextOpen ? previousOpen : nextOpen);
        };
        updateOpenState();
        const observer = new MutationObserver(updateOpenState);
        observer.observe(document.body, {
            childList: true,
            subtree: true,
            attributes: true,
            attributeFilter: ["data-state", "data-slot"],
        });
        return () => observer.disconnect();
    }, []);
    return open;
}
export function BridgeInlineFormatOverlay({ onCommand, pageViewContext, }) {
    const editContext = useEditContext();
    const selectedRange = editContext?.selectedRange;
    const modalDialogOpen = useOpenModalDialogOverlay();
    const overlayRef = useRef(null);
    const [overlay, setOverlay] = useState({
        visible: false,
        anchorX: 0,
        top: 0,
    });
    const [linkDialogOpen, setLinkDialogOpen] = useState(false);
    const [linkDialogPosition, setLinkDialogPosition] = useState();
    const [linkCommandRange, setLinkCommandRange] = useState();
    const [toolbarProfile, setToolbarProfile] = useState({
        key: "",
        ready: false,
        groups: [],
    });
    const isRichText = !!(selectedRange?.isRichText && selectedRange?.elementKey);
    const formatting = selectedRange?.formatting;
    const toolbarProfileKey = getToolbarProfileKey(selectedRange);
    useEffect(() => {
        if (!editContext || !pageViewContext || !selectedRange || !isRichText) {
            setToolbarProfile((previous) => previous.key === "" && !previous.ready && previous.groups.length === 0
                ? previous
                : { key: "", ready: false, groups: [] });
            return;
        }
        if (toolbarProfile.key === toolbarProfileKey && toolbarProfile.ready) {
            return;
        }
        let cancelled = false;
        setToolbarProfile((previous) => previous.key === toolbarProfileKey && !previous.ready
            ? previous
            : { key: toolbarProfileKey, ready: false, groups: [] });
        const loadProfile = async () => {
            try {
                const field = await resolveSelectedRangeField(selectedRange, pageViewContext, editContext);
                const profile = field ? await loadParsedRichTextProfile(field) : null;
                if (cancelled)
                    return;
                setToolbarProfile({
                    key: toolbarProfileKey,
                    ready: true,
                    groups: getBridgeRichTextProfileGroups(profile),
                });
            }
            catch (error) {
                console.error("Error loading bridge rich text toolbar profile:", error);
                if (cancelled)
                    return;
                setToolbarProfile({
                    key: toolbarProfileKey,
                    ready: true,
                    groups: [],
                });
            }
        };
        void loadProfile();
        return () => {
            cancelled = true;
        };
    }, [
        editContext,
        isRichText,
        pageViewContext,
        pageViewContext?.bridgeStructure,
        selectedRange,
        toolbarProfile.key,
        toolbarProfile.ready,
        toolbarProfileKey,
    ]);
    useEffect(() => {
        // The unified toolbar is shown for any editable text selection in the page
        // host (rich text or plain text). Formatting controls are gated on
        // isRichText below; the AI text-edit button is always shown.
        if (!selectedRange?.text?.trim() ||
            !selectedRange.elementKey ||
            !selectedRange.clientRect ||
            !onCommand ||
            editContext?.readonly ||
            modalDialogOpen ||
            editContext?.currentOverlay === "context-menu" ||
            (editContext?.mode !== "edit" && editContext?.mode !== "suggestions")) {
            setOverlay((previous) => previous.visible ? { ...previous, visible: false } : previous);
            return;
        }
        const overlayWidth = overlayRef.current?.offsetWidth;
        const overlayHeight = overlayRef.current?.offsetHeight;
        const layout = calculateBridgeInlineFormatToolbarLayout(selectedRange.clientRect, {
            toolbarWidth: overlayWidth,
            toolbarHeight: overlayHeight,
        });
        setOverlay({
            visible: true,
            anchorX: layout.left + layout.width / 2,
            top: layout.top,
        });
    }, [
        editContext?.mode,
        editContext?.currentOverlay,
        editContext?.readonly,
        modalDialogOpen,
        onCommand,
        selectedRange?.clientRect,
        selectedRange?.elementKey,
        selectedRange?.isRichText,
        selectedRange?.text,
        toolbarProfile.groups,
        toolbarProfile.ready,
    ]);
    useEffect(() => {
        if (!modalDialogOpen || !linkDialogOpen)
            return;
        setLinkDialogOpen(false);
        setLinkDialogPosition(undefined);
        setLinkCommandRange(undefined);
    }, [modalDialogOpen, linkDialogOpen]);
    const openLinkDialogAtAnchor = useCallback((anchor, commandRange) => {
        const selectedRect = commandRange?.clientRect ?? selectedRange?.clientRect;
        const resolvedAnchor = anchor ??
            (selectedRect
                ? {
                    x: selectedRect.left,
                    y: selectedRect.bottom,
                    align: "start",
                    origin: "caret",
                }
                : undefined);
        if (!resolvedAnchor)
            return;
        setLinkDialogPosition(calculateDialogPosition({ x: resolvedAnchor.x, y: resolvedAnchor.y }, 486, 592, resolvedAnchor.align ?? "start", resolvedAnchor.origin ?? "caret"));
        setLinkCommandRange(commandRange);
        setLinkDialogOpen(true);
    }, [selectedRange?.clientRect]);
    useEffect(() => {
        const handleBridgeInlineLinkOpen = (event) => {
            const commandRange = event.detail
                ?.selectedRange ?? selectedRange;
            if (!commandRange?.text?.trim() ||
                !commandRange.isRichText ||
                !commandRange.elementKey ||
                !onCommand ||
                editContext?.readonly ||
                (editContext?.mode !== "edit" && editContext?.mode !== "suggestions")) {
                return;
            }
            openLinkDialogAtAnchor(undefined, commandRange);
        };
        document.addEventListener(BRIDGE_INLINE_LINK_OPEN_EVENT, handleBridgeInlineLinkOpen);
        return () => {
            document.removeEventListener(BRIDGE_INLINE_LINK_OPEN_EVENT, handleBridgeInlineLinkOpen);
        };
    }, [
        editContext?.mode,
        editContext?.readonly,
        onCommand,
        openLinkDialogAtAnchor,
        selectedRange,
        selectedRange?.elementKey,
        selectedRange?.isRichText,
        selectedRange?.text,
    ]);
    const showInlineFormatToolbar = overlay.visible && !modalDialogOpen;
    const showLinkDialog = linkDialogOpen && !modalDialogOpen;
    if (!showInlineFormatToolbar && !showLinkDialog) {
        return null;
    }
    const sendCommand = (command, value) => {
        const commandRange = linkCommandRange ?? selectedRange;
        if (!commandRange?.elementKey || !commandRange.fieldId || !onCommand) {
            return;
        }
        onCommand({
            elementKey: commandRange.elementKey,
            fieldId: commandRange.fieldId,
            command,
            value,
            selectedText: commandRange.text,
            selectionStartOffset: commandRange.startOffset,
            selectionEndOffset: commandRange.endOffset,
        });
    };
    const handleFormatMouseDown = (button) => (event) => {
        event.preventDefault();
        event.stopPropagation();
        sendCommand(button.command);
    };
    const iconButtonClass = (active = false) => `${ICON_BUTTON_BASE_CLASS} ${active ? ACTIVE_ICON_BUTTON_CLASS : INACTIVE_ICON_BUTTON_CLASS}`;
    const isButtonActive = (button) => {
        switch (button.id) {
            case "bold":
                return !!formatting?.bold;
            case "italic":
                return !!formatting?.italic;
            case "underline":
                return !!formatting?.underline;
            case "strikethrough":
                return !!formatting?.strikethrough;
            case "subscript":
                return !!formatting?.subscript;
            case "superscript":
                return !!formatting?.superscript;
            case "ordered-list":
                return !!formatting?.orderedList;
            case "unordered-list":
                return !!formatting?.unorderedList;
            case "link":
                return !!formatting?.link;
            default:
                return false;
        }
    };
    const getButtonForProfileOption = (option) => {
        if (option.type === "mark") {
            const markConfig = SLATE_MARKS[option.id];
            const icon = getRichTextMarkIcon(option.id);
            if (!markConfig || !icon)
                return null;
            return {
                id: option.id,
                label: markConfig.label,
                icon,
                command: option.command,
            };
        }
        if (option.type === "list") {
            return LIST_BUTTONS[option.id];
        }
        return {
            id: "link",
            label: "Link",
            icon: getRichTextLinkIcon(),
            command: "createLink",
        };
    };
    const getLinkHref = (link) => {
        if (link.type === "internal") {
            return generateInternalLinkUrl(link.itemId, link.targetItemLongId, link.queryString);
        }
        const url = link.url || "#";
        return url.startsWith("mailto:") || url.startsWith("tel:")
            ? url
            : normalizeUrl(url);
    };
    const openLinkDialog = (event) => {
        event.preventDefault();
        event.stopPropagation();
        const toolbarRect = overlayRef.current?.getBoundingClientRect();
        const buttonRect = event.currentTarget.getBoundingClientRect();
        const anchor = toolbarRect
            ? getInlineAiAnchorBelowToolbar(toolbarRect, buttonRect)
            : {
                x: buttonRect.left,
                y: buttonRect.bottom,
                align: "start",
                origin: "toolbar",
            };
        openLinkDialogAtAnchor(anchor);
    };
    const openInlineAi = (event) => {
        event.preventDefault();
        event.stopPropagation();
        const toolbarRect = overlayRef.current?.getBoundingClientRect();
        const buttonRect = event.currentTarget.getBoundingClientRect();
        document.dispatchEvent(new CustomEvent(INLINE_AI_OPEN_EVENT, {
            bubbles: true,
            cancelable: true,
            detail: {
                anchor: toolbarRect
                    ? {
                        ...getInlineAiAnchorBelowToolbar(toolbarRect),
                        x: buttonRect.left,
                    }
                    : {
                        x: buttonRect.left,
                        y: buttonRect.bottom,
                        align: "start",
                        origin: "toolbar",
                    },
            },
        }));
    };
    const addComment = (event) => {
        event.preventDefault();
        event.stopPropagation();
        if (!editContext)
            return;
        showCommentPopoverForTrigger(event.currentTarget, editContext, {
            range: selectedRange,
        });
    };
    const suggestionsAvailable = isSuggestionsModeAvailable(editContext);
    const suggestionsActive = editContext?.mode === "suggestions";
    const toggleSuggestions = (event) => {
        event.preventDefault();
        event.stopPropagation();
        if (!editContext)
            return;
        if (suggestionsActive) {
            exitSuggestionsMode(editContext, { reason: "inline-toolbar" });
        }
        else {
            enterSuggestionsMode(editContext, { reason: "inline-toolbar" });
        }
    };
    const content = (_jsxs(_Fragment, { children: [showInlineFormatToolbar && (_jsxs("div", { ref: overlayRef, "data-bridge-inline-format-toolbar": "true", className: "border-border-default fixed z-[90] flex items-center gap-1 rounded-md border bg-white p-2 shadow-lg", style: {
                    left: overlay.anchorX,
                    top: overlay.top,
                    transform: "translateX(-50%)",
                }, onMouseDown: (event) => {
                    // Preserve the host selection when interacting with the toolbar chrome.
                    event.preventDefault();
                    event.stopPropagation();
                }, children: [isRichText && (_jsxs(_Fragment, { children: [toolbarProfile.ready &&
                                toolbarProfile.groups.map((group) => (_jsx("div", { className: TOOL_GROUP_CLASS, children: group.options.map((option) => {
                                        const button = getButtonForProfileOption(option);
                                        if (!button)
                                            return null;
                                        if (option.type === "link") {
                                            return (_jsx(ToolbarButtonTooltip, { label: button.label, shortcut: getToolbarButtonShortcut(button), children: _jsx("button", { type: "button", className: iconButtonClass(isButtonActive(button)), "aria-label": button.label, "aria-haspopup": "dialog", onMouseDown: openLinkDialog, children: button.icon }) }, `${group.id}-${button.id}`));
                                        }
                                        return (_jsx(ToolbarButtonTooltip, { label: button.label, shortcut: getToolbarButtonShortcut(button), children: _jsx("button", { type: "button", className: iconButtonClass(isButtonActive(button)), "aria-label": button.label, "aria-pressed": isButtonActive(button), onMouseDown: handleFormatMouseDown(button), children: button.icon }) }, `${group.id}-${button.id}`));
                                    }) }, group.id))), _jsx("div", { className: TOOL_GROUP_CLASS, children: _jsx(ToolbarButtonTooltip, { label: STRIP_FORMATTING_BUTTON.label, shortcut: getToolbarButtonShortcut(STRIP_FORMATTING_BUTTON), children: _jsx("button", { type: "button", className: iconButtonClass(), "aria-label": STRIP_FORMATTING_BUTTON.label, onMouseDown: handleFormatMouseDown(STRIP_FORMATTING_BUTTON), children: STRIP_FORMATTING_BUTTON.icon }) }) }), _jsx("span", { "aria-hidden": "true", className: "bg-neutral-grey-5 mx-0.5 h-5 w-px" })] })), _jsxs("div", { className: TOOL_GROUP_CLASS, children: [_jsx(ToolbarButtonTooltip, { label: "Add Comment", children: _jsx("button", { type: "button", className: iconButtonClass(), "aria-label": "Add Comment", "data-bridge-inline-format-comment-button": "true", onMouseDown: addComment, children: _jsx(MessageCirclePlus, { "aria-hidden": "true", className: "h-4 w-4", strokeWidth: 1.75 }) }) }), suggestionsAvailable && (_jsx(ToolbarButtonTooltip, { label: "Suggestions mode", children: _jsx("button", { type: "button", className: iconButtonClass(suggestionsActive), "aria-label": "Suggestions mode", "aria-pressed": suggestionsActive, "data-bridge-inline-format-suggest-button": "true", onMouseDown: toggleSuggestions, children: _jsx(PenLine, { "aria-hidden": "true", className: "h-4 w-4", strokeWidth: 1.75 }) }) }))] }), _jsx(ToolbarButtonTooltip, { label: INLINE_AI_TEXT_EDIT_LABEL, shortcut: getToolbarButtonShortcut({
                            id: "ai-text-edit",
                            label: INLINE_AI_TEXT_EDIT_LABEL,
                            icon: null,
                            command: INLINE_AI_OPEN_EVENT,
                        }), children: _jsxs("button", { type: "button", "data-bridge-inline-format-ai-button": "true", className: "text-neutral-grey-100 hover:bg-neutral-grey-5 inline-flex h-8 items-center gap-1 rounded-[4px] bg-transparent px-2 text-xs leading-none font-medium focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-blue-600", "aria-label": INLINE_AI_TEXT_EDIT_LABEL, onMouseDown: openInlineAi, children: [_jsx(Sparkles, { "aria-hidden": "true", className: "h-3 w-3" }), _jsx("span", { children: INLINE_AI_TEXT_EDIT_LABEL })] }) })] })), showLinkDialog && (_jsx(LinkEditorDialog, { linkValue: {
                    type: "internal",
                    url: "",
                    target: "_blank",
                }, position: linkDialogPosition, onOk: (link) => {
                    sendCommand("createLink", getLinkHref(link));
                    setLinkDialogOpen(false);
                    setLinkDialogPosition(undefined);
                    setLinkCommandRange(undefined);
                }, onCancel: () => {
                    setLinkDialogOpen(false);
                    setLinkDialogPosition(undefined);
                    setLinkCommandRange(undefined);
                } }))] }));
    return createPortal(content, document.body);
}
function getToolbarProfileKey(selectedRange) {
    if (!selectedRange?.isRichText || !selectedRange.elementKey) {
        return "";
    }
    return [
        selectedRange.itemId,
        selectedRange.language,
        selectedRange.version,
        selectedRange.fieldId,
        selectedRange.elementKey,
    ].join(":");
}
async function resolveSelectedRangeField(selectedRange, pageViewContext, editContext) {
    const descriptor = resolveSelectedRangeFieldDescriptor(selectedRange, pageViewContext);
    if (!descriptor)
        return undefined;
    const item = await editContext.itemsRepository.getItem(descriptor.item);
    return item?.fields.find((field) => fieldKeysMatch(field.id, descriptor.fieldId) ||
        fieldKeysMatch(field.name, descriptor.fieldId) ||
        fieldKeysMatch(field.displayName, descriptor.fieldId));
}
function resolveSelectedRangeFieldDescriptor(selectedRange, pageViewContext) {
    if (!selectedRange.fieldId)
        return undefined;
    const structureFields = pageViewContext.bridgeStructure?.fields ?? [];
    const structureField = structureFields.find((field) => selectedRange.elementKey &&
        field.elementKey === selectedRange.elementKey) ??
        structureFields.find((field) => fieldKeysMatch(field.fieldId, selectedRange.fieldId) &&
            fieldKeysMatch(field.item?.id, selectedRange.itemId));
    const fallbackItem = pageViewContext.page?.item ?? pageViewContext.pageItemDescriptor;
    const sourceItem = structureField?.item ?? selectedRange ?? fallbackItem;
    const item = resolveItemDescriptor(sourceItem, fallbackItem);
    const fieldId = structureField?.fieldId ?? selectedRange.fieldId;
    if (!item || !fieldId)
        return undefined;
    return {
        item,
        fieldId,
    };
}
function resolveItemDescriptor(source, fallback) {
    const itemId = source?.id ?? source?.itemId ?? fallback?.id;
    const language = source?.language ?? fallback?.language;
    const version = source?.version ?? fallback?.version;
    const database = source?.database ?? fallback?.database;
    if (!itemId || !language || typeof version !== "number") {
        return undefined;
    }
    return {
        id: itemId,
        language,
        version,
        database,
    };
}
function normalizeFieldKey(value) {
    return (value || "").replace(/[{}-]/g, "").toLowerCase();
}
function fieldKeysMatch(left, right) {
    const normalizedLeft = normalizeFieldKey(left);
    const normalizedRight = normalizeFieldKey(right);
    return (!!normalizedLeft && !!normalizedRight && normalizedLeft === normalizedRight);
}
//# sourceMappingURL=BridgeInlineFormatOverlay.js.map