import {
  useEvent,
  useId,
  useMergeRefs,
  createElement,
  createHook,
  forwardRef,
  memo,
} from "@ariakit/react-utils";
import type { Options, Props } from "@ariakit/react-utils";
import { invariant } from "@ariakit/utils";
import type { ElementType, MouseEvent } from "react";
import { useSelectProviderContext } from "./select-context.tsx";
import type { SelectStore } from "./select-store.ts";

const TagName = "div" satisfies ElementType;
type TagName = typeof TagName;
type HTMLType = HTMLElementTagNameMap[TagName];

/**
 * Returns props to create a `SelectLabel` component. Since it's not a native
 * select element, we can't use the native label element. The `SelectLabel`
 * component will move focus to the `Select` component when the user clicks on
 * the label.
 * @deprecated Use `useComboboxSelectLabel` instead.
 * @see https://ariakit.com/components/select
 * @example
 * ```jsx
 * const store = useSelectStore();
 * const props = useSelectLabel({ store });
 * <Role {...props}>Favorite fruit</Role>
 * <Select store={store} />
 * ```
 */
export const useSelectLabel = createHook<TagName, SelectLabelOptions>(
  function useSelectLabel({ store, ...props }) {
    const context = useSelectProviderContext();
    store = store || context;

    invariant(
      store,
      process.env.NODE_ENV !== "production" &&
        "SelectLabel must receive a `store` prop or be wrapped in a SelectProvider component.",
    );

    const id = useId(props.id);

    const onClickProp = props.onClick;

    const onClick = useEvent((event: MouseEvent<HTMLType>) => {
      onClickProp?.(event);
      if (event.defaultPrevented) return;
      // queueMicrotask will guarantee that the focus event will be triggered
      // only after the current event queue is flushed (which includes this
      // click event).
      queueMicrotask(() => {
        const select = store?.getState().selectElement;
        select?.focus();
      });
    });

    props = {
      ...props,
      id,
      ref: useMergeRefs(store.setLabelElement, props.ref),
      onClick,
      style: {
        cursor: "default",
        ...props.style,
      },
    };

    return props;
  },
);

/**
 * Renders a label for the [`Select`](https://ariakit.com/reference/select)
 * component. Since it's not a native select element, we can't use the native
 * label element. This component will move focus to the
 * [`Select`](https://ariakit.com/reference/select) component when clicked.
 * @deprecated Use
 * [`ComboboxSelectLabel`](https://ariakit.com/reference/combobox-select-label)
 * instead.
 * @see https://ariakit.com/components/select
 * @example
 * ```jsx {2}
 * <SelectProvider defaultValue="Apple">
 *   <SelectLabel>Favorite fruit</SelectLabel>
 *   <Select />
 *   <SelectPopover>
 *     <SelectItem value="Apple" />
 *     <SelectItem value="Orange" />
 *   </SelectPopover>
 * </SelectProvider>
 * ```
 */
export const SelectLabel = memo(
  forwardRef(function SelectLabel(props: SelectLabelProps) {
    const htmlProps = useSelectLabel(props);
    return createElement(TagName, htmlProps);
  }),
);

/** @deprecated Use `ComboboxSelectLabelOptions` instead. */
export interface SelectLabelOptions<
  _T extends ElementType = TagName,
> extends Options {
  /**
   * Object returned by the
   * [`useSelectStore`](https://ariakit.com/reference/use-select-store) hook. If
   * not provided, the closest
   * [`SelectProvider`](https://ariakit.com/reference/select-provider)
   * component's context will be used.
   */
  store?: SelectStore;
}

/** @deprecated Use `ComboboxSelectLabelProps` instead. */
export type SelectLabelProps<T extends ElementType = TagName> = Props<
  T,
  SelectLabelOptions<T>
>;
