/**
 * Simple context menu component.
 * Shows a menu at a given position with clickable items.
 */

let activeMenu: HTMLElement | null = null;
let dismissListener: ((e: Event) => void) | null = null;
let escapeListener: ((e: KeyboardEvent) => void) | null = null;

export interface MenuItem {
    label: string;
    action: () => void;
    disabled?: boolean;
    separator?: boolean;
    /** Render bold, as the menu's default/primary action (e.g. "Move here"
     *  in the right-drag menu, vs the secondary "Copy here"). */
    emphasized?: boolean;
    /** Native browser tooltip shown on hover (title attribute). Use it
     *  to explain non-obvious side effects of an action — e.g., "skips
     *  Trash, no undo" so the user can tell two near-identical entries
     *  apart without trial-and-error. */
    tooltip?: string;
}

/** Close any open context menu and remove dismiss listeners */
export function closeContextMenu(): void {
    if (activeMenu) {
        activeMenu.remove();
        activeMenu = null;
    }
    if (dismissListener) {
        document.removeEventListener("pointerdown", dismissListener, true);
        dismissListener = null;
    }
    if (escapeListener) {
        document.removeEventListener("keydown", escapeListener, true);
        escapeListener = null;
    }
}

/** Show a context menu at the given position */
export function showContextMenu(x: number, y: number, items: MenuItem[]): void {
    closeContextMenu();

    const menu = document.createElement("div");
    menu.className = "ctx-menu";

    for (const item of items) {
        if (item.separator) {
            const sep = document.createElement("div");
            sep.className = "ctx-sep";
            menu.appendChild(sep);
            continue;
        }
        const el = document.createElement("div");
        el.className = "ctx-item" + (item.disabled ? " ctx-disabled" : "") + (item.emphasized ? " ctx-emphasized" : "");
        if (item.emphasized) el.style.fontWeight = "600";
        el.textContent = item.label;
        if (item.tooltip) el.title = item.tooltip;
        if (!item.disabled) {
            el.addEventListener("click", () => {
                closeContextMenu();
                item.action();
            });
        }
        menu.appendChild(el);
    }

    menu.style.left = `${x}px`;
    menu.style.top = `${y}px`;
    document.body.appendChild(menu);

    // Keep the menu fully on-screen — clamp to ALL FOUR edges. The old
    // flip-only logic (x − width) went NEGATIVE when the press was near an
    // edge on a narrow screen, so Android long-press menus rendered clipped
    // mid-word off the left side (Bob 2026-07-13). visualViewport tracks
    // the on-screen keyboard; taller-than-screen menus scroll internally.
    const vw = window.visualViewport?.width ?? window.innerWidth;
    const vh = window.visualViewport?.height ?? window.innerHeight;
    let rect = menu.getBoundingClientRect();
    if (rect.height > vh - 8) {
        menu.style.maxHeight = `${vh - 8}px`;
        menu.style.overflowY = "auto";
        rect = menu.getBoundingClientRect();
    }
    let left = x, top = y;
    if (left + rect.width > vw) left = x - rect.width;   // prefer flipping…
    if (top + rect.height > vh) top = y - rect.height;
    left = Math.max(4, Math.min(left, vw - rect.width - 4));  // …then clamp
    top = Math.max(4, Math.min(top, vh - rect.height - 4));
    menu.style.left = `${left}px`;
    menu.style.top = `${top}px`;

    activeMenu = menu;

    // Dismiss on click/tap outside the menu. Uses pointerdown in capture phase
    // so it fires before any child handler and catches both left- and right-clicks.
    // Deferred by one frame so the opening pointerdown doesn't immediately close it.
    requestAnimationFrame(() => {
        dismissListener = (e: Event) => {
            if (activeMenu && !activeMenu.contains(e.target as Node)) {
                closeContextMenu();
            }
        };
        document.addEventListener("pointerdown", dismissListener, true);

        escapeListener = (e: KeyboardEvent) => {
            if (e.key === "Escape") {
                e.preventDefault();
                e.stopPropagation();
                closeContextMenu();
            }
        };
        document.addEventListener("keydown", escapeListener, true);
    });
}

// Scroll anywhere closes the menu (capture phase so nested scrollers trigger it)
document.addEventListener("scroll", closeContextMenu, true);
// A new right-click that opens a different menu goes through showContextMenu→closeContextMenu
document.addEventListener("contextmenu", () => { /* handled by showContextMenu */ });
// Iframe pointerdown forwarded from preview/compose iframes — needed because
// iframe events don't bubble to the parent document, so the dismissListener
// (which hooks document.pointerdown) doesn't see clicks INSIDE iframes. The
// message-viewer's inline iframe script posts {type:"iframePointerDown"} on
// every non-right-click pointerdown.
window.addEventListener("message", (e: MessageEvent) => {
    if (e.data && (e.data as any).type === "iframePointerDown") closeContextMenu();
});
