import {
  Field,
  Label,
  Combobox as HeadlessCombobox,
  ComboboxInput,
  ComboboxButton,
  ComboboxOptions,
  ComboboxOption,
} from '@headlessui/react'
import { CheckIcon, ChevronDownIcon } from '@heroicons/react/24/solid'
import { useState, type ComponentProps, type ReactNode } from 'react'
import {
  get,
  type FieldError,
  type FieldValues,
  type Path,
  type RegisterOptions,
  Controller,
} from 'react-hook-form'
import { useRemixFormContext } from 'remix-hook-form'
import { twMerge } from 'tailwind-merge'

type TComboboxOption = {
  code: string
  name: string
  id: string
}

type TComboboxProperties<T extends FieldValues> = ComponentProps<
  typeof HeadlessCombobox
> & {
  id: string
  label?: ReactNode
  name: Path<T>
  rules?: RegisterOptions
  placeholder?: string
  options?: TComboboxOption[]
  labelClassName?: string
  containerClassName?: string
}

export const Combobox = <TFormValues extends Record<string, unknown>>(
  properties: TComboboxProperties<TFormValues>,
) => {
  const {
    id,
    label,
    name,
    rules,
    disabled,
    placeholder,
    options,
    className,
    labelClassName,
    containerClassName,
    ...restProperties
  } = properties
  const {
    control,
    formState: { errors },
  } = useRemixFormContext()
  const [query, setQuery] = useState('')
  const filteredOptions =
    query === ''
      ? options
      : options?.filter((option) =>
          option.name.toLowerCase().includes(query.toLowerCase()),
        )

  const error: FieldError = get(errors, name)

  return (
    <Field
      className={twMerge('relative', containerClassName)}
      disabled={disabled}
      id={id}
    >
      <Label className={twMerge('mb-1 block text-gray-700', labelClassName)}>
        {label} {error && <span className="text-red-500">{error.message}</span>}
      </Label>
      <Controller
        name={name}
        control={control}
        rules={rules}
        render={({ field }) => (
          <HeadlessCombobox
            value={field.value}
            onChange={field.onChange}
            disabled={disabled}
            immediate
            {...restProperties}
          >
            <div className="relative">
              <ComboboxInput
                placeholder={placeholder}
                displayValue={(option: TComboboxOption) => option?.name}
                onChange={(event) => setQuery(event.target.value)}
                className={twMerge(
                  'focus:inheriten h-[42px] w-full rounded-md border border-[#DFDFDF] p-2 placeholder:text-inherit',
                  className,
                )}
              />
              <ComboboxButton className="group absolute inset-y-0 right-0 px-2.5">
                <ChevronDownIcon className="size-4 fill-gray-500" />
              </ComboboxButton>
            </div>
            <ComboboxOptions
              anchor={{ to: 'bottom', gap: '8px' }}
              transition
              className={twMerge(
                'w-[var(--input-width)] rounded-md border border-[#DFDFDF] p-1 empty:invisible',
                'bg-white transition duration-100 ease-in data-[leave]:data-[closed]:opacity-0',
              )}
            >
              {filteredOptions?.map((option) => (
                <ComboboxOption
                  key={option.id}
                  value={option}
                  className="group flex cursor-default items-center gap-2 rounded-lg px-3 py-1.5 select-none data-[focus]:bg-white/10"
                >
                  <CheckIcon className="invisible size-4 group-data-[selected]:visible" />
                  <div className="text-sm/6">{option.name}</div>
                </ComboboxOption>
              ))}
            </ComboboxOptions>
          </HeadlessCombobox>
        )}
      />
    </Field>
  )
}
