import classNames from "classnames/dedupe";
import { Option } from "./Option";
import type {
  ComboboxBaseProps,
  ComboboxOption,
  ComboboxOptions,
} from "./types";
import { isOptionGroup } from "./utils";

const getOptionKey = (
  option: ComboboxOption,
  index: number,
  groupIndex?: number,
): string => {
  let key: string;
  if (typeof option === "object" && option.id) key = option.id;
  else if (typeof option === "object") key = `${option.value}::${option.label}`;
  else if (typeof option === "string") key = option;
  else key = String(index);

  return groupIndex !== undefined ? `${groupIndex}:${key}` : key;
};

export type ListboxProps<T extends ComboboxOption> = {
  id: string;
  isOpen?: boolean;
  isLoading?: boolean;
  options: ComboboxOptions<T> | undefined;
  highlightedIndex: number;
  highlightedGroupIndex: number;
  onOptionSelect: (option: T) => void;
  optionComponent?: ComboboxBaseProps<T>["optionComponent"];
  optionTestIdPrefix?: string;
};

export const Listbox = <T extends ComboboxOption>({
  id,
  isOpen = false,
  options,
  highlightedIndex,
  highlightedGroupIndex,
  onOptionSelect,
  optionComponent,
  optionTestIdPrefix,
}: ListboxProps<T>) => {
  const classes = classNames("mobius-combobox__list", {
    "mobius-combobox__list--hidden": !isOpen,
  });

  function getOptionId(
    option: ComboboxOption,
    groupIndex: number,
    index: number,
  ) {
    if (
      typeof option === "object" &&
      "id" in option &&
      typeof option.id === "string"
    ) {
      return option.id;
    }
    return isOptionGroup(options)
      ? `${id}-option-${groupIndex}-${index}`
      : `${id}-option-${index}`;
  }

  // If not open, render empty listbox but keep it in DOM for accessibility
  if (!isOpen) {
    return <div role="listbox" id={id} className={classes} />;
  }

  if (options && options.length === 0) {
    return (
      <div role="listbox" id={id} className={classes}>
        <div className="mobius-combobox__no-options">No options</div>
      </div>
    );
  }

  return (
    <div role="listbox" id={id} className={classes}>
      {isOptionGroup(options)
        ? options.map((option, groupIndex) => (
            <ul
              role="group"
              key={option.heading}
              aria-labelledby={`${id}-group-${groupIndex}`}
              className="mobius-combobox__group"
            >
              <li
                role="presentation"
                id={`${id}-group-${groupIndex}`}
                className="mobius-combobox__group-label"
              >
                {option.heading}
              </li>
              {option.options.map((groupOption, index) => (
                <Option
                  key={getOptionKey(groupOption, index, groupIndex)}
                  option={groupOption}
                  isHighlighted={
                    highlightedIndex === index &&
                    highlightedGroupIndex === groupIndex
                  }
                  onOptionSelect={onOptionSelect}
                  optionComponent={optionComponent}
                  optionTestIdPrefix={optionTestIdPrefix}
                  id={getOptionId(groupOption, groupIndex, index)}
                />
              ))}
            </ul>
          ))
        : typeof options !== "undefined"
          ? options.map((option, index) => (
              <Option
                key={getOptionKey(option, index)}
                option={option}
                isHighlighted={highlightedIndex === index}
                onOptionSelect={onOptionSelect}
                optionComponent={optionComponent}
                optionTestIdPrefix={optionTestIdPrefix}
                id={getOptionId(option, 0, index)}
              />
            ))
          : null}
    </div>
  );
};
