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 { DropdownMenuCheckboxItem } from "./dropdown-menu-checkbox-item";
import { DropdownMenuContent } from "./dropdown-menu-content";
import {
  DropdownMenuPublicContextProvider,
  InternalDropdownMenuContextProvider,
  useDropdownMenuContext,
} from "./dropdown-menu-context";
import type { InternalDropdownMenuContextValue } from "./dropdown-menu-context";
import { DropdownMenuGroup } from "./dropdown-menu-group";
import { DropdownMenuGroupLabel } from "./dropdown-menu-group-label";
import { DropdownMenuItem } from "./dropdown-menu-item";
import { DropdownMenuRadioGroup } from "./dropdown-menu-radio-group";
import { DropdownMenuRadioItem } from "./dropdown-menu-radio-item";
import { DropdownMenuSeparator } from "./dropdown-menu-separator";
import { DropdownMenuTrigger } from "./dropdown-menu-trigger";

export interface DropdownMenuRootProps {
  /** 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;
}

/**
 * Root component for DropdownMenu. Manages open state, floating
 * positioning, and keyboard navigation via context.
 * Items self-register their values on mount.
 */
export function DropdownMenuRoot(props: DropdownMenuRootProps): JSX.Element {
  const [local] = splitProps(props, [
    "open",
    "defaultOpen",
    "onOpenChange",
    "children",
  ]);

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

  const [triggerRef, setTriggerRef] = createSignal<HTMLElement | null>(null);
  const [contentRef, setContentRef] = createSignal<HTMLElement | null>(null);
  const [registeredItems, setRegisteredItems] = createSignal<string[]>([]);

  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: registeredItems,
    mode: "activation",
    onActivate: (value: string) => {
      const handler = itemHandlers.get(value);
      if (handler) handler();
      disclosure.close();
    },
    baseId,
  });

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

  const ctx: InternalDropdownMenuContextValue = {
    isOpen: disclosure.isOpen,
    open: disclosure.open,
    close: disclosure.close,
    toggle: disclosure.toggle,
    navigation,
    triggerRef,
    setTriggerRef,
    contentRef,
    setContentRef,
    contentId,
    triggerId,
    floatingStyle: floating.style,
    onItemActivate: (value: string) => {
      const handler = itemHandlers.get(value);
      if (handler) handler();
      disclosure.close();
    },
    registerItem: (value: string, handler?: (() => void) | undefined) => {
      if (handler) itemHandlers.set(value, handler);
      setRegisteredItems((prev) => (prev.includes(value) ? prev : [...prev, value]));
    },
    unregisterItem: (value: string) => {
      itemHandlers.delete(value);
      setRegisteredItems((prev) => prev.filter((v) => v !== value));
    },
  };

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

/** Compound DropdownMenu component with all sub-components as static properties. */
export const DropdownMenu = Object.assign(DropdownMenuRoot, {
  Trigger: DropdownMenuTrigger,
  Content: DropdownMenuContent,
  Item: DropdownMenuItem,
  Group: DropdownMenuGroup,
  GroupLabel: DropdownMenuGroupLabel,
  Separator: DropdownMenuSeparator,
  CheckboxItem: DropdownMenuCheckboxItem,
  RadioGroup: DropdownMenuRadioGroup,
  RadioItem: DropdownMenuRadioItem,
  useContext: useDropdownMenuContext,
});
