import { useEffect, useId, useState } from 'react'

import { Box } from '../box'
import { Input } from '../input'
import { helpTextSize } from '../input/consts'
import HelpText from '../input/helpText'
import { Popover } from '../popover'
import { __DEV__ } from '../utils'
import { SelectProvider } from './context'
import { SelectOptionData, SelectProps } from './select.types'
import SelectButton from './selectButton'
import SelectContent from './selectContent'
import SelectGroup from './selectGroup'
import SelectLabel from './selectLabel'
import SelectOption from './selectOption'
import useSelect from './useSelect'

/**
 * Wraps trigger and content elements, providing them with context.
 * When no children are provided, content can be displayed based on 'options' data prop.
 * Supports single select and multi select, as well as controlled and uncontrolled modes.
 */
export function Select(props: SelectProps) {
  const {
    children,
    defaultValue,
    disabled,
    errorText,
    closeOnScroll,
    helpText,
    id: idProp,
    isInvalid,
    isMultiple,
    label,
    labelMarkOptional,
    labelTooltipText,
    maxHeight,
    name,
    onChange,
    options,
    matchWidth = true,
    placeholder = 'Please select',
    selectedText,
    readOnly,
    selectButton,
    selectButtonProps,
    showOptionsFilter,
    size = 'lg',
    value,
    variant,
    ...rest
  } = props

  const selectProps = useSelect({ defaultValue, isMultiple, onChange, value, options, selectedText, children })

  if (__DEV__ && isMultiple && !Array.isArray(selectProps.value))
    console.error('<Select /> is used with isMultiple but its value is not an array: ', value)

  const generatedId = useId()
  const [query, setQuery] = useState<string>('')
  const [filteredOptions, setFilteredOptions] = useState(options)

  const id = idProp || generatedId

  const context = {
    disabled,
    isInvalid,
    isMultiple,
    name,
    options,
    placeholder,
    selectedText,
    readOnly,
    size,
    variant,
    ...selectProps,
  }
  const onQueryChange = (e: any) => setQuery(e.target.value?.toLowerCase())

  const filterOptions = (i: SelectOptionData) =>
    !showOptionsFilter || !query?.length ? true : i?.text?.toLowerCase()?.includes(query)

  useEffect(() => {
    setFilteredOptions(options?.filter(filterOptions))
  }, [query, options])

  return (
    <SelectProvider value={context}>
      <Box className="vui-selectContainer" column {...rest}>
        {!!label && (
          <SelectLabel
            disabled={disabled}
            id={id}
            label={label}
            labelMarkOptional={labelMarkOptional}
            labelTooltipText={labelTooltipText}
          />
        )}
        <Popover matchWidth={matchWidth}>
          <>
            {selectButton ?? <SelectButton {...selectButtonProps} />}
            <SelectContent overflow="hidden">
              {showOptionsFilter && (
                <Box>
                  <Input
                    iconLeft="falSearch"
                    id={id}
                    m={1}
                    mt={0}
                    onChange={onQueryChange}
                    placeholder="Filter…"
                    size={size}
                    value={query}
                    w="100%"
                  />
                </Box>
              )}
              <SelectGroup maxH={maxHeight} overflowY="auto" tabIndex={1000}>
                {children ??
                  filteredOptions?.map?.(option => (
                    <SelectOption closeOnScroll={closeOnScroll} key={option.value} title={option.text} {...option} />
                  ))}
              </SelectGroup>
            </SelectContent>
          </>
        </Popover>
        {!!helpText && <HelpText size={helpTextSize[size]}>{helpText}</HelpText>}
        {!!errorText && (
          <HelpText isError size={helpTextSize[size]}>
            {errorText}
          </HelpText>
        )}
      </Box>
    </SelectProvider>
  )
}

Select.Button = SelectButton
Select.Content = SelectContent
Select.Group = SelectGroup
Select.Option = SelectOption
Select.displayName = 'Select'

export default Select
