import type { Middleware, Placement } from "@floating-ui/dom";
import { flip, offset, shift } from "@floating-ui/dom";
import type { Accessor, JSX } from "solid-js";
import { createSignal, createUniqueId, splitProps } from "solid-js";
import { createDisclosureState } from "../../primitives/create-disclosure-state";
import { createFloating } from "../../primitives/create-floating";
import { createListNavigation } from "../../primitives/create-list-navigation";
import { ContextMenuContent } from "./context-menu-content";
import {
  ContextMenuPublicContextProvider,
  InternalContextMenuContextProvider,
  useContextMenuContext,
} from "./context-menu-context";
import type { InternalContextMenuContextValue } from "./context-menu-context";
import { ContextMenuGroup } from "./context-menu-group";
import { ContextMenuGroupLabel } from "./context-menu-group-label";
import { ContextMenuItem } from "./context-menu-item";
import { ContextMenuSeparator } from "./context-menu-separator";
import { ContextMenuTrigger } from "./context-menu-trigger";

export interface ContextMenuRootProps {
  /** Ordered list of active item values for keyboard navigation. */
  items?: string[] | undefined;
  /** Controlled open state. */
  open?: boolean | undefined;
  /** Initial open state (uncontrolled). */
  defaultOpen?: boolean | undefined;
  /** Called when open state changes. */
  onOpenChange?: ((open: boolean) => void) | undefined;
  children: JSX.Element;
}

/** Virtual anchor element that positions the floating menu at pointer coordinates. */
interface VirtualAnchor {
  getBoundingClientRect: () => {
    x: number;
    y: number;
    width: number;
    height: number;
    top: number;
    left: number;
    right: number;
    bottom: number;
  };
}

/** Creates a virtual anchor element from pointer coordinates. */
function createVirtualAnchorElement(x: number, y: number): VirtualAnchor {
  return {
    getBoundingClientRect: () => ({
      x,
      y,
      width: 0,
      height: 0,
      top: y,
      left: x,
      right: x,
      bottom: y,
    }),
  };
}

/**
 * Root component for ContextMenu. Manages open state, virtual anchor
 * positioning at pointer coordinates, and keyboard navigation via context.
 */
export function ContextMenuRoot(props: ContextMenuRootProps): JSX.Element {
  const [local] = splitProps(props, [
    "items",
    "open",
    "defaultOpen",
    "onOpenChange",
    "children",
  ]);

  const contentId = createUniqueId();
  const baseId = createUniqueId();

  const [virtualAnchor, setVirtualAnchor] = createSignal<VirtualAnchor | null>(null);
  const [contentRef, setContentRef] = createSignal<HTMLElement | null>(null);

  const itemHandlers = new Map<string, () => void>();

  const disclosure = createDisclosureState({
    get open() {
      return local.open;
    },
    get defaultOpen() {
      return local.defaultOpen;
    },
    get onOpenChange() {
      return local.onOpenChange;
    },
  });

  const navigation = createListNavigation({
    items: () => local.items ?? [],
    mode: "activation",
    onActivate: (value: string) => {
      const handler = itemHandlers.get(value);
      if (handler) handler();
      disclosure.close();
    },
    baseId,
  });

  const floating = createFloating({
    anchor: virtualAnchor,
    floating: contentRef,
    placement: (() => "bottom-start") as Accessor<Placement>,
    middleware: (() => [offset(2), flip(), shift({ padding: 8 })]) as Accessor<Middleware[]>,
    open: disclosure.isOpen,
  });

  const ctx: InternalContextMenuContextValue = {
    isOpen: disclosure.isOpen,
    open: disclosure.open,
    close: disclosure.close,
    navigation,
    contentRef,
    setContentRef,
    contentId,
    floatingStyle: floating.style,
    setAnchorPosition: (x: number, y: number) => {
      setVirtualAnchor(createVirtualAnchorElement(x, y));
    },
    onItemActivate: (value: string) => {
      const handler = itemHandlers.get(value);
      if (handler) handler();
      disclosure.close();
    },
    registerItemHandler: (value: string, handler: () => void) => {
      itemHandlers.set(value, handler);
    },
    unregisterItemHandler: (value: string) => {
      itemHandlers.delete(value);
    },
  };

  return (
    <InternalContextMenuContextProvider value={ctx}>
      <ContextMenuPublicContextProvider value={{ open: disclosure.isOpen }}>
        {local.children}
      </ContextMenuPublicContextProvider>
    </InternalContextMenuContextProvider>
  );
}

/** Compound ContextMenu component with all sub-components as static properties. */
export const ContextMenu = Object.assign(ContextMenuRoot, {
  Trigger: ContextMenuTrigger,
  Content: ContextMenuContent,
  Item: ContextMenuItem,
  Group: ContextMenuGroup,
  GroupLabel: ContextMenuGroupLabel,
  Separator: ContextMenuSeparator,
  useContext: useContextMenuContext,
});
