import { createContext, useContext, createSignal, createEffect, onCleanup, Show, JSXElement } from "solid-js";
import { FloatingRoot, FloatingTrigger, FloatingContent, useFloatingContext, useFloatingHover, Placement } from "./Floating.tsx";
import { Icon } from "./Icons.tsx";

// Tailwind utility class groups for the menu UI — replaces the old
// `.ui-menu` / `.item` / `.divider` / `.section` @scope CSS.  These
// are plain utility strings so they compose with any caller-supplied
// classes via simple concatenation.
// Padded container + inset, rounded items (the highlight is a rounded rectangle
// that doesn't reach the menu edges) — matching the popover insert menus.
const MENU_CLS = "bg-white rounded-default shadow-lg border border-neutral-200 p-1.5 min-w-48 max-h-96 overflow-y-auto";
const ITEM_CLS = "flex items-center gap-2 w-full py-1.5 px-2 rounded-default text-sm text-left text-neutral-700 bg-transparent border-0 cursor-pointer hover:bg-neutral-100 hover:text-neutral-900 focus:bg-neutral-100 focus:outline-hidden disabled:opacity-50 disabled:cursor-not-allowed";
// Divider runs edge-to-edge (negated container padding) for a clean separator.
const DIVIDER_CLS = "my-1 -mx-1.5 border-0 border-t border-neutral-200";
const SECTION_CLS = "pt-1.5 pb-0.5 px-2 text-[10px] font-semibold text-neutral-400 uppercase tracking-wide text-left";
const SUBMENU_TRIGGER_CLS = "flex items-center justify-between gap-2 w-full py-1.5 px-2 rounded-default text-sm text-left text-neutral-700 cursor-pointer hover:bg-neutral-100 hover:text-neutral-900";

interface MenuContextValue {
    openOnHover: boolean;
    hoverCloseDelay: number;
    closeMenu: () => void;
    cancelParentClose?: () => void;
}

const MenuContext = createContext<MenuContextValue | null>(null);

function useMenuContext(): MenuContextValue {
    const context = useContext(MenuContext);
    if (!context) {
        throw new Error("Menu components must be used within a Menu");
    }
    return context;
}

interface MenuProps {
    open?: boolean;
    onOpenChange?: (open: boolean) => void;
    placement?: Placement;
    offset?: number;
    openOnHover?: boolean;
    hoverCloseDelay?: number;
    children?: JSXElement;
}

export function Menu(props: MenuProps) {
    return (
        <FloatingRoot open={props.open} onOpenChange={(v: boolean) => props.onOpenChange?.(v)} placement={props.placement ?? "bottom-start"} offset={props.offset ?? 4}>
            <MenuContextProvider openOnHover={props.openOnHover ?? false} hoverCloseDelay={props.hoverCloseDelay ?? 150}>
                {props.children}
            </MenuContextProvider>
        </FloatingRoot>
    );
}

interface MenuContextProviderProps {
    openOnHover: boolean;
    hoverCloseDelay: number;
    children?: JSXElement;
}

function MenuContextProvider(props: MenuContextProviderProps) {
    const { setIsOpen, cancelHoverClose } = useFloatingContext();

    const closeMenu = () => setIsOpen(false);

    return (
        <MenuContext.Provider value={{
            get openOnHover() { return props.openOnHover; },
            get hoverCloseDelay() { return props.hoverCloseDelay; },
            closeMenu,
            cancelParentClose: cancelHoverClose,
        }}>
            {props.children}
        </MenuContext.Provider>
    );
}

interface MenuTriggerProps {
    asChild?: boolean;
    class?: string;
    children?: JSXElement | ((state: { isOpen: boolean }) => JSXElement);
}

export function MenuTrigger(props: MenuTriggerProps) {
    const menuCtx = useMenuContext();
    const { isOpen } = useFloatingContext();

    const resolvedChildren = () => typeof props.children === "function"
        ? (props.children as (state: { isOpen: boolean }) => JSXElement)({ isOpen: isOpen() })
        : props.children;

    return (
        <FloatingTrigger class={props.class || ""} openOnHover={menuCtx.openOnHover} hoverCloseDelay={menuCtx.hoverCloseDelay}>
            {resolvedChildren()}
        </FloatingTrigger>
    );
}

interface MenuContentProps {
    class?: string;
    children?: JSXElement;
}

export function MenuContent(props: MenuContentProps) {
    const menuCtx = useMenuContext();
    const hoverProps = useFloatingHover(menuCtx.openOnHover, menuCtx.hoverCloseDelay);

    return (
        <FloatingContent class={MENU_CLS + " " + (props.class || "")} onMouseEnter={hoverProps.onMouseEnter} onMouseLeave={hoverProps.onMouseLeave}>
            {props.children}
        </FloatingContent>
    );
}

interface MenuItemProps {
    icon?: string;
    disabled?: boolean;
    onclick?: ((_e: MouseEvent) => Promise<void>) | ((_e: MouseEvent) => void);
    closeOnClick?: boolean;
    class?: string;
    children?: JSXElement;
}

export function MenuItem(props: MenuItemProps) {
    const { closeMenu } = useMenuContext();

    const handleClick = (e?: MouseEvent) => {
        if (props.disabled) return;
        props.onclick?.(e);
        if (props.closeOnClick !== false) closeMenu();
    };

    const handleKeyDown = (e: KeyboardEvent) => {
        if (e.key === "Enter" || e.key === " ") {
            e.preventDefault();
            handleClick();
        }
    };

    return (
        <button type="button" role="menuitem" class={ITEM_CLS + " " + (props.class || "")} onclick={handleClick} onKeyDown={handleKeyDown} disabled={props.disabled}>
            <Show when={props.icon}>
                <Icon icon={props.icon!} size={16} class="shrink-0"/>
            </Show>
            {props.children}
        </button>
    );
}

interface MenuLinkProps {
    href: string;
    icon?: string;
    class?: string;
    children?: JSXElement;
}

export function MenuLink(props: MenuLinkProps) {
    const { closeMenu } = useMenuContext();

    return (
        <a href={props.href} role="menuitem" class={ITEM_CLS + " " + (props.class || "")} onclick={closeMenu}>
            <Show when={props.icon}>
                <Icon icon={props.icon!} size={16} class="shrink-0"/>
            </Show>
            {props.children}
        </a>
    );
}

interface MenuAnchorProps extends MenuLinkProps {
    target?: string;
    rel?: string;
}

export function MenuAnchor(props: MenuAnchorProps) {
    const { closeMenu } = useMenuContext();

    return (
        <a href={props.href} target={props.target ?? "_blank"} rel={props.rel ?? "noopener noreferrer"} role="menuitem" class={ITEM_CLS + " " + (props.class || "")} onclick={closeMenu}>
            <Show when={props.icon}>
                <Icon icon={props.icon!} size={16} class="shrink-0"/>
            </Show>
            {props.children}
            <Show when={props.target === "_blank"}>
                <Icon icon="arrow-right" size={12} class="shrink-0 ml-auto text-neutral-400"/>
            </Show>
        </a>
    );
}

export function MenuDivider(props: { class?: string }) {
    return <hr class={DIVIDER_CLS + " " + (props.class || "")} role="separator"/>;
}

interface MenuSectionProps {
    class?: string;
    children?: JSXElement;
}

export function MenuSection(props: MenuSectionProps) {
    return (
        <div class={SECTION_CLS + " " + (props.class || "")} role="presentation">
            {props.children}
        </div>
    );
}

interface SubmenuProps {
    trigger: string;
    icon?: string;
    class?: string;
    children?: JSXElement;
}

interface SubmenuPosition {
    top: number;
    left: number;
}

export function Submenu(props: SubmenuProps) {
    const parentMenu = useMenuContext();
    const [isOpen, setIsOpen] = createSignal(false);
    const [position, setPosition] = createSignal<SubmenuPosition | null>(null);
    let closeTimeoutRef: ReturnType<typeof setTimeout> | null = null;
    let triggerRef: HTMLDivElement | undefined;
    let contentRef: HTMLDivElement | undefined;

    const clearCloseTimeout = () => {
        if (closeTimeoutRef) { clearTimeout(closeTimeoutRef); closeTimeoutRef = null; }
    };

    const scheduleClose = (delay: number) => {
        clearCloseTimeout();
        closeTimeoutRef = setTimeout(() => setIsOpen(false), delay);
    };

    const handleTriggerMouseEnter = () => {
        clearCloseTimeout();
        parentMenu.cancelParentClose?.();
        setIsOpen(true);
    };

    const handleMouseLeave = () => scheduleClose(parentMenu.hoverCloseDelay);

    const handleContentMouseEnter = () => {
        clearCloseTimeout();
        parentMenu.cancelParentClose?.();
    };

    const handleClick = () => setIsOpen((prev) => !prev);

    createEffect(() => {
        if (!isOpen() || !triggerRef) {
            setPosition(null);
            return;
        }

        const updatePosition = () => {
            if (!triggerRef) return;
            const triggerRect = triggerRef.getBoundingClientRect();
            const contentEl = contentRef;

            let top = triggerRect.top;
            let left = triggerRect.right;

            if (contentEl) {
                const contentRect = contentEl.getBoundingClientRect();
                const vw = window.innerWidth;
                const vh = window.innerHeight;

                if (left + contentRect.width > vw - 8) left = triggerRect.left - contentRect.width;
                if (top + contentRect.height > vh - 8) top = vh - contentRect.height - 8;
                if (top < 8) top = 8;
            }

            setPosition({ top, left });
        };

        updatePosition();
        window.addEventListener("scroll", updatePosition, true);
        window.addEventListener("resize", updatePosition);

        onCleanup(() => {
            window.removeEventListener("scroll", updatePosition, true);
            window.removeEventListener("resize", updatePosition);
        });
    });

    onCleanup(clearCloseTimeout);

    return (
        <MenuContext.Provider value={{
            openOnHover: parentMenu.openOnHover,
            hoverCloseDelay: parentMenu.hoverCloseDelay,
            closeMenu: parentMenu.closeMenu,
            cancelParentClose: clearCloseTimeout,
        }}>
            <div ref={(el: HTMLDivElement) => triggerRef = el} role="menuitem" aria-haspopup="menu" aria-expanded={isOpen()} class={SUBMENU_TRIGGER_CLS + " " + (props.class || "")} onMouseEnter={handleTriggerMouseEnter} onMouseLeave={handleMouseLeave} onclick={handleClick}>
                <span class="flex items-center gap-2">
                    <Show when={props.icon}>
                        <Icon icon={props.icon!} size={16} class="shrink-0"/>
                    </Show>
                    {props.trigger}
                </span>
                <Icon icon="chevron-right" size={16} class="shrink-0 ml-auto text-neutral-400"/>
            </div>

            <div ref={(el: HTMLDivElement) => contentRef = el} role="menu" class={MENU_CLS} style={{
                    position: "fixed",
                    display: isOpen() ? "block" : "none",
                    top: (position()?.top ?? 0) + "px",
                    left: (position()?.left ?? 0) + "px",
                    "z-index": 51,
                }} onMouseEnter={handleContentMouseEnter} onMouseLeave={handleMouseLeave}>
                {props.children}
            </div>
        </MenuContext.Provider>
    );
}

interface MenuGroupProps {
    class?: string;
    children?: JSXElement;
}

export function MenuGroup(props: MenuGroupProps) {
    return <div role="group" class={props.class || ""}>{props.children}</div>;
}
