/* eslint-disable better-tailwindcss/no-unregistered-classes */
import { useVirtualizer } from "@tanstack/react-virtual";
import { useRef, useState, type JSX } from "react";
import ReactSelect, {
  type MenuListProps,
  type GroupBase,
  type CSSObjectWithLabel,
} from "react-select";
import { cn } from "~/lib/utils";

export type ListOption = {
  label: string;
  value: string;
};

type BaseProps = {
  elementId: string;
  elementName: string;
  options: ListOption[];
  placeholder: string;
  disabled?: boolean;
  menuPlacement?: "auto" | "bottom" | "top";
  defaultValue?: ListOption | ListOption[];
};

type MultiProps = {
  isMulti: true;
  onValueChange: (value: ListOption[]) => void;
};
type SigletProps = {
  isMulti: false;
  onValueChange: (value: ListOption) => void;
};
type MultiSelectProps = BaseProps & (MultiProps | SigletProps);

const VirtualizedMenuList = (
  props: MenuListProps<
    ListOption,
    MultiSelectProps["isMulti"],
    GroupBase<ListOption>
  >,
) => {
  const rows = props.children as JSX.Element[];
  const parentRef = useRef<HTMLDivElement>(null);
  const count = rows.length;

  const virtualizer = useVirtualizer({
    count,
    getScrollElement: () => parentRef.current,
    estimateSize: () => 45,
  });
  const items = virtualizer.getVirtualItems();

  return (
    <div
      ref={parentRef}
      className="List"
      style={{
        height: `${virtualizer.getTotalSize()}px`,
        maxHeight: "300px",
        width: "auto",
        overflowY: "auto",
        contain: "strict",
      }}
    >
      <div
        style={{
          height: virtualizer.getTotalSize(),
          width: "100%",
          position: "relative",
        }}
      >
        <div
          style={{
            position: "absolute",
            top: 0,
            left: 0,
            width: "100%",
            transform: `translateY(${items[0]?.start ?? 0}px)`,
          }}
        >
          {items.map((virtualRow) => (
            <div
              key={virtualRow.key}
              data-index={virtualRow.index}
              ref={virtualizer.measureElement}
              className={virtualRow.index % 2 ? "ListItemOdd" : "ListItemEven"}
            >
              {rows[virtualRow.index]}
            </div>
          ))}
        </div>
      </div>
    </div>
  );
};

/**
 *
 * @example
 * <MultiSelect
 *   elementId="select-naz"
 *   elementName="nazioneNascita"
 *   options={UnpackOptions({ options: nazioni_options })}
 *   defaultValue={RepackValues({
 *     options: nazioni_options,
 *     values: field.value,
 *   })}
 *   onValueChange={(value) => {
 *     field.onChange(value.value);
 *     //if isMulti
 *     field.onChange(value.map((v) => v.value));
 *   }}
 *   placeholder="Seleziona..."
 *   isMulti={false}
 * />
 */
export const MultiSelect = ({
  elementId,
  defaultValue,
  elementName,
  isMulti,
  onValueChange,
  options,
  placeholder,
  disabled,
  menuPlacement,
}: MultiSelectProps) => {
  const [menuOpen, setMenuOpen] = useState(false);

  return (
    <ReactSelect
      menuPlacement={menuPlacement}
      components={{ MenuList: VirtualizedMenuList }}
      inputId={elementId}
      isMulti={isMulti}
      isDisabled={disabled}
      instanceId={elementId}
      name={elementName}
      options={options}
      onChange={(values) => {
        if (isMulti) {
          const valore = values as ListOption[];
          onValueChange(valore);
        } else {
          const valore = values as ListOption;
          onValueChange(valore);
        }
      }}
      defaultValue={defaultValue}
      placeholder={placeholder}
      onMenuOpen={() => setMenuOpen(true)}
      onMenuClose={() => setMenuOpen(false)}
      styles={{
        container: (base, state) =>
          ({
            ...base,

            ":hover .select__indicator-separator": {
              backgroundColor: state.isFocused
                ? "rgb(161,161,170)"
                : "rgb(161,161,170)",
            },

            ":hover .select__dropdown-indicator": {
              color: state.isFocused ? "rgb(161,161,170)" : "rgb(161,161,170)",
            },
          }) as CSSObjectWithLabel,
        control: (base, state) =>
          ({
            ...base,
            minHeight: "42px",
            borderRadius: "0.5rem",
            boxShadow: undefined,

            border: state.isFocused
              ? "1px solid rgb(161,161,170)"
              : "1px solid rgb(212 ,212 ,216)",

            "&:hover": {
              borderColor: state.isFocused
                ? "rgb(161,161,170)"
                : "rgb(161 ,161 ,170)",
            },

            "& .select__indicator-separator": {
              backgroundColor: state.isFocused
                ? "rgb(161,161,170)"
                : "rgb(212 ,212 ,216)",
            },

            "& .select__dropdown-indicator": {
              color: state.isFocused
                ? "rgb(161,161,170)"
                : "rgb(212 ,212 ,216)",
              transition: "none",
            },
          }) as CSSObjectWithLabel,
        multiValue: (base) =>
          ({
            ...base,
            backgroundColor: undefined,
            borderRadius: "0.5rem",
          }) as CSSObjectWithLabel,
        multiValueLabel: (base) =>
          ({
            ...base,
            borderRadius: "0.5rem",
          }) as CSSObjectWithLabel,
        menu: (base) =>
          ({
            ...base,
            borderRadius: "0.5rem",
          }) as CSSObjectWithLabel,
        option: (base, state) =>
          ({
            ...base,
            backgroundColor: undefined,
            color: state.isSelected ? undefined : undefined,

            "&:hover": {
              backgroundColor: undefined,
              color: state.isSelected ? undefined : undefined,
            },

            "&:active": {
              backgroundColor: "rgb(161, 161, 170)",
              color: "rgb(255, 255, 255)",
            },

            "&:focus": {
              backgroundColor: undefined,
            },
          }) as CSSObjectWithLabel,
      }}
      classNames={{
        valueContainer: () => cn("min-h-[38px]"),
        control: () =>
          cn(
            "mt-2 w-full rounded-lg h-min-[42px] dark:bg-primary bg-white shadow-xs outline-hidden text-foreground dark:text-white",
          ),
        option: () =>
          cn(
            "dark:bg-black text-foreground bg-white dark:text-white border-b border-neutral-300 dark:border-foreground shadow-xs outline-hidden hover:text-primary hover:bg-neutral-200 dark:hover:bg-neutral-700",
          ),
        menu: () =>
          cn(
            `dark:bg-black text-foreground bg-white dark:text-white border shadow-xs outline-hidden
                          dark:border-foreground border-neutral-300 overflow-hidden`,
            menuOpen
              ? "animate-in fade-in-0 zoom-in-90"
              : "animate-out fade-out-0 zoom-out-90",
          ),
        multiValueLabel: () =>
          cn(
            "dark:bg-black rounded-lg text-primary bg-white dark:text-white outline-hidden",
          ),
        multiValue: () =>
          cn(
            "overflow-hidden dark:bg-black rounded-lg text-primary bg-white dark:text-white border border-neutral-300 dark:border-foreground shadow-xs outline-hidden",
          ),
        multiValueRemove: () => cn("rounded-s-none rounded-e-md"),
        input: () => cn("*:ring-0 focus:*:ring-0"),
        singleValue: () => cn("text-primary font-medium"),
      }}
      classNamePrefix="select"
    />
  );
};

type UnpackOptions = {
  options: string[];
};
export const UnpackOptions = ({ options }: UnpackOptions) => {
  return options.map((v, idx) => ({ value: idx.toString(), label: v }));
};

type RepackValues = {
  values: string | string[];
  options: string[];
};
export const RepackValues = ({ values, options }: RepackValues) => {
  if (Array.isArray(values)) {
    return values.map((v) => ({
      label: options[Number.parseInt(v)]!,
      value: v,
    }));
  }
  return {
    label: options[Number.parseInt(values)]!,
    value: values,
  };
};
