'use client';

import { useCallback, useRef, useState } from 'react';
import { Loader2 } from 'lucide-react';
import type { Virtualizer } from '@tanstack/react-virtual';

import { Button } from '@/components/ui/button';
import {
  Combobox,
  ComboboxContent,
  ComboboxEmpty,
  ComboboxInput,
  ComboboxList,
  ComboboxTrigger,
  ComboboxValue,
} from '@/components/ui/combobox';

import type { AsyncComboboxOption, AsyncComboboxProps } from './AsyncCombobox.types';
import { VirtualComboboxList } from './VirtualComboboxList';

type VirtualizerHandle = Virtualizer<HTMLDivElement, Element>;

export function AsyncCombobox({
  lookup,
  onValueChange,
  placeholder = 'Select option',
  searchPlaceholder = 'Search...',
  emptyMessage = 'No items found.',
  disabled = false,
  portalContainer,
}: AsyncComboboxProps) {
  const [open, setOpen] = useState(false);
  const virtualizerRef = useRef<VirtualizerHandle | null>(null);
  const {
    options,
    selectedOption,
    search,
    setSearch,
    isLoading,
    isFetchingNextPage,
    isError,
    errorMessage,
    hasNextPage,
    fetchNextPage,
  } = lookup;

  const handleOpenChange = useCallback(
    (nextOpen: boolean) => {
      setOpen(nextOpen);
      if (!nextOpen) {
        setSearch('');
      }
    },
    [setSearch],
  );

  const handleValueChange = useCallback(
    (item: AsyncComboboxOption | null) => {
      if (item?.value) {
        onValueChange(item.value);
      }
    },
    [onValueChange],
  );

  const handleItemHighlighted = useCallback(
    (
      _item: AsyncComboboxOption | undefined,
      event: { reason: string; index: number },
    ) => {
      const virtualizer = virtualizerRef.current;
      if (!virtualizer) {
        return;
      }

      const { reason, index } = event;
      const isStart = index === 0;
      const isEnd = index === virtualizer.options.count - 1;
      const shouldScroll =
        reason === 'none' || (reason === 'keyboard' && (isStart || isEnd));

      if (shouldScroll) {
        queueMicrotask(() => {
          virtualizer.scrollToIndex(index, { align: isEnd ? 'start' : 'end' });
        });
      }
    },
    [],
  );

  const handleLoadMore = useCallback(() => {
    void fetchNextPage();
  }, [fetchNextPage]);

  const isDisabled = disabled || isLoading;
  const triggerPlaceholder = isLoading ? 'Loading...' : placeholder;

  return (
    <Combobox
      virtualized
      items={options}
      value={selectedOption}
      onValueChange={handleValueChange}
      open={open}
      onOpenChange={handleOpenChange}
      inputValue={search}
      onInputValueChange={setSearch}
      filter={null}
      disabled={isDisabled}
      itemToStringLabel={(item) => item.label}
      isItemEqualToValue={(item, currentValue) => item.value === currentValue.value}
      onItemHighlighted={handleItemHighlighted}
    >
      <ComboboxTrigger
        render={
          <Button
            type="button"
            variant="outline"
            className="w-full justify-between font-normal"
            disabled={isDisabled}
          >
            {isLoading && !selectedOption ? (
              <span className="flex items-center gap-2 text-muted-foreground">
                <Loader2 className="size-4 animate-spin" />
                Loading...
              </span>
            ) : (
              <ComboboxValue placeholder={triggerPlaceholder} />
            )}
          </Button>
        }
      />
      <ComboboxContent container={portalContainer}>
        <ComboboxInput showTrigger={false} placeholder={searchPlaceholder} disabled={isDisabled} />
        {isError ? (
          <div className="px-3 py-2 text-sm text-destructive">
            {errorMessage || 'Unable to load options.'}
          </div>
        ) : (
          <>
            <ComboboxEmpty>{emptyMessage}</ComboboxEmpty>
            <ComboboxList className="p-0">
              <VirtualComboboxList
                open={open}
                virtualizerRef={virtualizerRef}
                hasNextPage={hasNextPage}
                isFetchingNextPage={isFetchingNextPage}
                onLoadMore={handleLoadMore}
              />
            </ComboboxList>
          </>
        )}
      </ComboboxContent>
    </Combobox>
  );
}
