import {
  useMergeRefs,
  useTagName,
  createElement,
  createHook,
  forwardRef,
} from "@ariakit/react-utils";
import type { Props } from "@ariakit/react-utils";
import { isButton } from "@ariakit/utils";
import type { ElementType } from "react";
import { useEffect, useRef, useState } from "react";
import type { CommandOptions } from "../command/command.tsx";
import { useCommand } from "../command/command.tsx";
import { withDefaultButtonType } from "./utils.ts";

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

/**
 * Returns props to create a `Button` component. If the element is not a native
 * button, the hook will return additional props to make sure it's accessible.
 * @see https://ariakit.com/components/button
 * @example
 * ```jsx
 * const props = useButton({ render: <div /> });
 * <Role {...props}>Accessible button</Role>
 * ```
 */
export const useButton = createHook<TagName, ButtonOptions>(
  function useButton(props) {
    const ref = useRef<HTMLType>(null);
    const tagName = useTagName(ref, TagName);
    const [isNativeButton, setIsNativeButton] = useState(
      () => !!tagName && isButton({ tagName, type: props.type }),
    );

    useEffect(() => {
      if (!ref.current) return;
      setIsNativeButton(isButton(ref.current));
    }, []);

    props = {
      role: !isNativeButton && tagName !== "a" ? "button" : undefined,
      ...props,
      ref: useMergeRefs(ref, props.ref),
    };

    props = useCommand(props);

    return props;
  },
);

/**
 * Renders an accessible button element. If the underlying element is not a
 * native button, this component will pass additional attributes to make sure
 * it's accessible.
 * @see https://ariakit.com/components/button
 * @example
 * ```jsx
 * <Button>Button</Button>
 * ```
 */
export const Button = forwardRef(function Button(props: ButtonProps) {
  const htmlProps = useButton(withDefaultButtonType(props));
  return createElement(TagName, htmlProps);
});

export interface ButtonOptions<
  T extends ElementType = TagName,
> extends CommandOptions<T> {}

export type ButtonProps<T extends ElementType = TagName> = Props<
  T,
  ButtonOptions<T>
>;
