import React, { useCallback, useEffect, useRef } from "react";

export interface SelectableListProps<T> {
  items: T[];
  selectedValue?: T | null;
  renderLabel: (item: T) => React.ReactNode;
  onSelect: (item: T) => void;
}

export const SelectableList = <T,>(
  props: SelectableListProps<T>,
): JSX.Element => {
  const { items, selectedValue, renderLabel, onSelect } = props;

  const listRef = useRef<HTMLUListElement | null>(null);
  const isFocusedRef = useRef(false);
  const prevSelectedValueRef = useRef<T | null | undefined>(selectedValue);

  const itemRefs = useRef<(HTMLLIElement | null)[]>([]);

  const handleKeyDown = useCallback(
    (e: React.KeyboardEvent<HTMLUListElement>) => {
      if (!isFocusedRef.current) return;
      if (!items.length) return;

      const currentIndex = selectedValue ? items.indexOf(selectedValue) : -1;

      if (e.key === "ArrowDown") {
        e.preventDefault();
        const nextIndex =
          currentIndex < items.length - 1 ? currentIndex + 1 : 0;
        onSelect(items[nextIndex]);
      }

      if (e.key === "ArrowUp") {
        e.preventDefault();
        const prevIndex =
          currentIndex > 0 ? currentIndex - 1 : items.length - 1;
        onSelect(items[prevIndex]);
      }
    },
    [items, selectedValue, onSelect],
  );

  useEffect(() => {
    if (!isFocusedRef.current) return;
    if (!selectedValue) return;

    const index = items.indexOf(selectedValue);
    if (index < 0) return;

    const el = itemRefs.current[index];
    if (el) {
      el.focus({ preventScroll: false });
    }
  }, [items, selectedValue]);

  // Separate effect for scrolling - only when selection changes
  useEffect(() => {
    // Only scroll if the selected value actually changed
    if (prevSelectedValueRef.current === selectedValue) return;

    prevSelectedValueRef.current = selectedValue;

    if (!selectedValue) return;

    const index = items.indexOf(selectedValue);
    if (index < 0) return;

    const el = itemRefs.current[index];
    if (el) {
      el.scrollIntoView({ block: "nearest", behavior: "smooth" });
    }
  }, [selectedValue, items]);

  return (
    <ul
      ref={listRef}
      className="selectable-list"
      tabIndex={0}
      role="listbox"
      aria-activedescendant={
        selectedValue ? `option-${items.indexOf(selectedValue)}` : undefined
      }
      onFocus={() => (isFocusedRef.current = true)}
      onBlur={() => (isFocusedRef.current = false)}
      onKeyDown={handleKeyDown}
    >
      {items.map((item, index) => {
        const isSelected = selectedValue === item;
        const className = isSelected ? "selected" : "";

        return (
          <li
            key={index}
            id={`option-${index}`}
            ref={(el) => (itemRefs.current[index] = el)}
            tabIndex={isSelected ? 0 : -1}
            role="option"
            aria-selected={isSelected}
            onClick={() => onSelect(item)}
            className={className}
          >
            {renderLabel(item)}
          </li>
        );
      })}
    </ul>
  );
};
