import clsx from "clsx";
import { Select as SelectPrimitive } from "radix-ui";
import * as React from "react";
import { useGetKey, useGetSet } from "../../hooks";
import { shallow, useDebugEvents } from "../../utils";
import { mergeDefaults } from "../../utils/mergeDefaults";
import { useComposedRefs } from "../../utils/mergeRefs";
import * as styles from "./styles.module.css";

interface SelectProps
  extends Omit<React.ComponentPropsWithoutRef<"div">, "onChange"> {
  placeholder?: string;
  options: any[];
  type: any;
  "data-label": string;
  "data-id": string;
  getOptionLabel: (option: any, idx: number) => string;
  defaultValue?: any;
  onChange?: (value: any) => void;
  position?: "item-aligned" | "popper";
  required?: boolean;
  size?: "xs" | "sm" | "md" | "lg";
  variant?: "solid" | "outline" | "ghost" | "subtle" | "elvated";
  colorPalette?: string;
}

export const Select = React.forwardRef(function Select(
  args: SelectProps,
  ref: React.ForwardedRef<HTMLButtonElement>,
) {
  const {
    options: _options,
    type: _type,
    defaultValue,
    onChange,
    placeholder,
    getOptionLabel,
    position = "popper",
    required,
    variant = "outline",
    size = "md",
    ...rest
  } = mergeDefaults(args, {
    options: [],
    getOptionLabel: (option) =>
      typeof option === "string" ? option : JSON.stringify(option),
  });
  const optionsRef = React.useRef(_options);
  const options = React.useMemo(() => {
    if (!shallow(optionsRef.current, _options, true)) {
      optionsRef.current = _options;
    }
    return optionsRef.current;
  }, [_options]);
  const selectRef = React.useRef<HTMLButtonElement>(null);
  const key = useGetKey(rest);
  const dfv = React.useMemo(() => {
    return options?.findIndex((o) => shallow(o, defaultValue, true)) ?? -1;
  }, [options, defaultValue]);

  const initalValue = React.useMemo(() => {
    const newValue =
      dfv >= 0 ? options[dfv] : placeholder ? null : options?.[0];
    return { value: newValue };
  }, [dfv, options, placeholder]);

  const [{ value }, setState] = useGetSet(key, initalValue);

  const localValue = React.useMemo(() => {
    const index = options?.findIndex((o) => shallow(o, value, true)) ?? -1;
    return index >= 0 ? String(index) : "";
  }, [value, options]);

  const {
    className,
    onChange: handleChange,
    ...props
  } = useDebugEvents<
    Omit<SelectProps, "options" | "defaultValue" | "getOptionLabel" | "type">
  >(
    Object.assign(rest, {
      onChange: (idxStr: string) => {
        if (
          idxStr === "" ||
          idxStr === "__empty__" ||
          idxStr === undefined ||
          idxStr === null
        ) {
          setState(
            { value: undefined },
            process.env.PREVIEW ? `onChange` : undefined,
          );
          onChange?.(null as any);
          return;
        }
        const idx = +idxStr;
        const value = options[idx];
        setState({ value }, process.env.PREVIEW ? `onChange` : undefined);
        onChange?.(value ?? null);
      },
    }),
  );

  React.useEffect(() => {
    const parentForm = selectRef.current?.closest("form");
    const handler = () => {
      setState({ value: defaultValue as string });
    };
    parentForm?.addEventListener("reset", handler);
    return () => {
      parentForm?.removeEventListener("reset", handler);
    };
  }, []);

  return (
    <div className={styles.root} onChange={(e) => e.stopPropagation()}>
      <SelectPrimitive.Root
        name={key}
        value={localValue}
        onValueChange={handleChange}
        required={required}
      >
        <SelectPrimitive.Trigger
          ref={useComposedRefs(selectRef, ref)}
          data-component="Select$Brevity"
          {...(props as any)}
          data-variant={variant}
          data-size={size}
          className={clsx("select-trigger", styles.trigger, className)}
          onPointerDownCapture={(e) => {
            if (
              process.env.PREVIEW &&
              document.body.classList.contains("__design")
            ) {
              e.preventDefault();
              e.target?.dispatchEvent(
                new Event("mousedown", { bubbles: true, cancelable: true }),
              );
            }
          }}
        >
          <span
            className={clsx("select-trigger_content", styles.triggerContent)}
          >
            <SelectPrimitive.Value placeholder={placeholder ?? ""} />
          </span>
          <SelectPrimitive.Icon asChild>
            <svg
              className={clsx("select-icon", styles.svg)}
              viewBox="0 0 24 24"
              role="presentation"
              focusable="false"
              aria-hidden="true"
            >
              <path
                fill="currentColor"
                d="M16.59 8.59L12 13.17 7.41 8.59 6 10l6 6 6-6z"
              />
            </svg>
          </SelectPrimitive.Icon>
        </SelectPrimitive.Trigger>
        <SelectPrimitive.Portal>
          <SelectPrimitive.Content
            className={clsx(
              "select-content",
              styles.content,
              position === "popper" && styles.popper,
            )}
            position={position}
          >
            <SelectPrimitive.Viewport
              className={clsx(
                "select-viewport",
                styles.viewport,
                position === "popper" && styles.popper,
              )}
            >
              {options?.map((option, idx) => {
                const label = getOptionLabel(option, idx);
                return (
                  <SelectPrimitive.Item
                    key={String(idx)}
                    className={clsx("select-item", styles.item)}
                    value={String(idx) || "__empty__"}
                  >
                    <SelectPrimitive.ItemIndicator>
                      <svg
                        className={clsx("select-indicator", styles.indicator)}
                        xmlns="http://www.w3.org/2000/svg"
                        width="24"
                        height="24"
                        viewBox="0 -960 960 960"
                      >
                        <path d="M382-354l339-339q12-12 28.5-12t28.5 12q12 12 12 28.5T778-636L410-268q-12 12-28 12t-28-12L182-440q-12-12-11.5-28.5T183-497q12-12 28.5-12t28.5 12l142 143z"></path>
                      </svg>
                    </SelectPrimitive.ItemIndicator>
                    <SelectPrimitive.ItemText>{label}</SelectPrimitive.ItemText>
                  </SelectPrimitive.Item>
                );
              })}
            </SelectPrimitive.Viewport>
          </SelectPrimitive.Content>
        </SelectPrimitive.Portal>
      </SelectPrimitive.Root>
    </div>
  );
});
