'use client'

import { useState } from 'react'
import {
  Combobox as HeadlessCombobox,
  ComboboxButton,
  ComboboxInput,
  ComboboxOption,
  ComboboxOptions,
  Label,
} from '@headlessui/react'
import { ChevronDownIcon, UserIcon } from '@heroicons/react/20/solid'

export type ComboboxVariant = 'simple' | 'status' | 'image' | 'secondary'

export type ComboboxItem = {
  id: string | number | null
  name: string
  online?: boolean
  imageUrl?: string
  secondaryText?: string
  username?: string
}

type ComboboxProps = {
  label?: string
  items: ComboboxItem[]
  value: ComboboxItem | null
  onChange: (item: ComboboxItem | null) => void
  variant?: ComboboxVariant
  placeholder?: string
  allowCustomValue?: boolean
  createOptionLabel?: (value: string) => string
  emptyText?: string
  disabled?: boolean
  required?: boolean
  className?: string
  inputClassName?: string
}

function classNames(...classes: Array<string | false | null | undefined>) {
  return classes.filter(Boolean).join(' ')
}

function getSecondaryText(item: ComboboxItem) {
  return item.secondaryText ?? item.username
}

export default function Combobox({
  label,
  items,
  value,
  onChange,
  variant = 'simple',
  placeholder,
  allowCustomValue = false,
  createOptionLabel,
  emptyText = 'Keine Ergebnisse gefunden.',
  disabled = false,
  required = false,
  className,
  inputClassName,
}: ComboboxProps) {
  const [query, setQuery] = useState('')

  const normalizedQuery = query.trim().toLowerCase()

  const filteredItems =
    normalizedQuery === ''
      ? items
      : items.filter((item) => {
          const secondaryText = getSecondaryText(item)

          return (
            item.name.toLowerCase().includes(normalizedQuery) ||
            secondaryText?.toLowerCase().includes(normalizedQuery)
          )
        })

  function renderOptionContent(item: ComboboxItem, isCustomValue = false) {
    if (variant === 'status') {
      return (
        <div className="flex items-center">
          <span
            className={classNames(
              'inline-block size-2 shrink-0 rounded-full',
              isCustomValue
                ? 'border border-gray-400 in-data-focus:border-white/75 dark:border-gray-600'
                : item.online
                  ? 'bg-green-400 dark:bg-green-500'
                  : 'bg-gray-200 dark:bg-white/20',
            )}
            aria-hidden="true"
          />

          <span className="ml-3 block truncate">
            {item.name}
            {!isCustomValue && (
              <span className="sr-only">
                {' '}
                ist {item.online ? 'online' : 'offline'}
              </span>
            )}
          </span>
        </div>
      )
    }

    if (variant === 'image') {
      return (
        <div className="flex items-center">
          {item.imageUrl ? (
            <img
              src={item.imageUrl}
              alt=""
              className="size-6 shrink-0 rounded-full bg-gray-100 object-cover outline -outline-offset-1 outline-black/5 dark:bg-gray-700 dark:outline-white/10"
            />
          ) : (
            <div className="grid size-6 shrink-0 place-items-center rounded-full bg-gray-300 in-data-focus:bg-white dark:bg-gray-600">
              <UserIcon
                aria-hidden="true"
                className="size-4 fill-white in-data-focus:fill-indigo-600 dark:in-data-focus:fill-indigo-500"
              />
            </div>
          )}

          <span className="ml-3 block truncate">{item.name}</span>
        </div>
      )
    }

    if (variant === 'secondary') {
      const secondaryText = getSecondaryText(item)

      return (
        <div className="flex min-w-0">
          <span className="block truncate">{item.name}</span>

          {secondaryText && (
            <span className="ml-2 block truncate text-gray-500 in-data-focus:text-white dark:text-gray-400 dark:in-data-focus:text-white">
              {secondaryText}
            </span>
          )}
        </div>
      )
    }

    return <span className="block truncate">{item.name}</span>
  }

  return (
    <HeadlessCombobox
      as="div"
      value={value}
      onChange={(item: ComboboxItem | null) => {
        setQuery('')
        onChange(item)
      }}
      disabled={disabled}
      className={className}
    >
      {label && (
        <Label className="block text-sm/6 font-medium text-gray-900 dark:text-white">
          {label}
        </Label>
      )}

      <div className={classNames('relative', label && 'mt-2')}>
        <ComboboxInput
          required={required}
          placeholder={placeholder}
          className={classNames(
            'block w-full rounded-md bg-white py-1.5 pr-12 pl-3 text-base text-gray-900 outline-1 -outline-offset-1 outline-gray-300 placeholder:text-gray-400 focus:outline-2 focus:-outline-offset-2 focus:outline-indigo-600 disabled:cursor-not-allowed disabled:bg-gray-100 disabled:text-gray-500 sm:text-sm/6 dark:bg-white/5 dark:text-white dark:outline-white/10 dark:placeholder:text-gray-500 dark:focus:outline-indigo-500 dark:disabled:bg-white/5 dark:disabled:text-white/40',
            inputClassName,
          )}
          onChange={(event) => setQuery(event.target.value)}
          onBlur={() => setQuery('')}
          displayValue={(item: ComboboxItem | null) => item?.name ?? ''}
        />

        <ComboboxButton className="absolute inset-y-0 right-0 flex items-center rounded-r-md px-2 focus:outline-hidden disabled:cursor-not-allowed">
          <ChevronDownIcon
            className="size-5 text-gray-400"
            aria-hidden="true"
          />
        </ComboboxButton>

        <ComboboxOptions
          transition
          className="absolute z-10 mt-1 max-h-60 w-full overflow-auto rounded-md bg-white py-1 text-base shadow-lg outline outline-black/5 data-leave:transition data-leave:duration-100 data-leave:ease-in data-closed:data-leave:opacity-0 sm:text-sm dark:bg-gray-800 dark:shadow-none dark:-outline-offset-1 dark:outline-white/10"
        >
          {allowCustomValue && query.trim().length > 0 && (
            <ComboboxOption
              value={{
                id: null,
                name: query.trim(),
              }}
              className="cursor-default px-3 py-2 text-gray-900 select-none data-focus:bg-indigo-600 data-focus:text-white data-focus:outline-hidden dark:text-white dark:data-focus:bg-indigo-500"
            >
              {renderOptionContent(
                {
                  id: null,
                  name: createOptionLabel
                    ? createOptionLabel(query.trim())
                    : query.trim(),
                },
                true,
              )}
            </ComboboxOption>
          )}

          {filteredItems.map((item) => (
            <ComboboxOption
              key={item.id}
              value={item}
              className="cursor-default px-3 py-2 text-gray-900 select-none data-focus:bg-indigo-600 data-focus:text-white data-focus:outline-hidden dark:text-white dark:data-focus:bg-indigo-500"
            >
              {renderOptionContent(item)}
            </ComboboxOption>
          ))}

          {filteredItems.length === 0 && !allowCustomValue && (
            <div className="px-3 py-2 text-sm text-gray-500 dark:text-gray-400">
              {emptyText}
            </div>
          )}
        </ComboboxOptions>
      </div>
    </HeadlessCombobox>
  )
}