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 { SelectContent } from "./select-content";
import { useSelectContext } from "./select-context";
import { InternalSelectContextProvider, SelectContextProvider } from "./select-context";
import type { InternalSelectContextValue } from "./select-context";
import { SelectGroup } from "./select-group";
import { SelectGroupLabel } from "./select-group-label";
import { SelectHiddenSelect } from "./select-hidden-select";
import { SelectItem } from "./select-item";
import { SelectTrigger } from "./select-trigger";
import { SelectValue } from "./select-value";

export interface SelectRootProps {
  /** Ordered list of active item values. */
  items: string[];
  /** Controlled selected value. */
  value?: string;
  /** Initial selected value (uncontrolled). */
  defaultValue?: string;
  /** Called when value changes. */
  onValueChange?: (value: string) => void;
  /** Controlled open state. */
  open?: boolean;
  /** Initial open state (uncontrolled). */
  defaultOpen?: boolean;
  /** Called when open state changes. */
  onOpenChange?: (open: boolean) => void;
  /** Resolve value to display label. */
  getLabel?: (value: string) => string;
  /** Whether the select is disabled. */
  disabled?: boolean;
  /** Whether selection is required. */
  required?: boolean;
  /** Form field name for hidden input. */
  name?: string;
  children: JSX.Element;
}

/**
 * Root component for Select. Manages open state, selection, floating
 * positioning, and keyboard navigation via context.
 */
export function SelectRoot(props: SelectRootProps): JSX.Element {
  const [local] = splitProps(props, [
    "items",
    "value",
    "defaultValue",
    "onValueChange",
    "open",
    "defaultOpen",
    "onOpenChange",
    "getLabel",
    "disabled",
    "required",
    "name",
    "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 disclosure = createDisclosureState({
    get open() {
      return local.open;
    },
    get defaultOpen() {
      return local.defaultOpen;
    },
    get onOpenChange() {
      return local.onOpenChange;
    },
  });

  const navigation = createListNavigation({
    items: () => local.items,
    mode: "selection",
    value: local.value !== undefined ? () => local.value : undefined,
    defaultValue: local.defaultValue,
    onValueChange: (v) => {
      local.onValueChange?.(v);
      disclosure.close();
    },
    getLabel: local.getLabel,
    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: InternalSelectContextValue = {
    isOpen: disclosure.isOpen,
    open: disclosure.open,
    close: disclosure.close,
    toggle: disclosure.toggle,
    navigation,
    triggerRef,
    setTriggerRef,
    contentRef,
    setContentRef,
    selectedValue: navigation.selectedValue,
    onSelect: (value: string) => navigation.getItemProps(value).onClick(),
    disabled: () => local.disabled ?? false,
    required: () => local.required ?? false,
    name: () => local.name,
    contentId,
    triggerId,
    floatingStyle: floating.style,
  };

  return (
    <InternalSelectContextProvider value={ctx}>
      <SelectContextProvider value={{ value: navigation.selectedValue, open: disclosure.isOpen }}>
        {local.children}
      </SelectContextProvider>
    </InternalSelectContextProvider>
  );
}

/** Compound Select component with all sub-components as static properties. */
export const Select = Object.assign(SelectRoot, {
  Trigger: SelectTrigger,
  Value: SelectValue,
  Content: SelectContent,
  Item: SelectItem,
  Group: SelectGroup,
  GroupLabel: SelectGroupLabel,
  HiddenSelect: SelectHiddenSelect,
  useContext: useSelectContext,
});
