import type { ChangeEvent, FocusEvent } from 'react'
import { useState, useCallback } from 'react'

import isUndefined from 'lodash/isUndefined'
import toLower from 'lodash/toLower'
import find from 'lodash/find'
import trim from 'lodash/trim'

import type { Props, Option } from '../types'
import { matchSort } from '../helpers'
import { useKeyboardScroll } from './useKeyboardScroll'

const isOptionClicked = (target: HTMLElement) => (
  target?.getAttribute('role') === 'option'
)

const useQuery = <T extends Option>({ onChange, value }: Props<T>) => {
  const [query, setQuery] = useState('')

  const onQueryChange = useCallback(({ target }: ChangeEvent<HTMLInputElement>) => {
    setQuery(target.value)
  }, [])

  return {
    onQueryChange: !isUndefined(onChange) ? onChange : onQueryChange,
    query: !isUndefined(value) ? value : query,
    setQuery,
  }
}

export const useCombobox = <T extends Option>(props: Props<T>) => {
  const {
    filterOptions = true,
    onSelect,
    options,
  } = props

  const {
    onQueryChange,
    query,
    setQuery,
  } = useQuery(props)

  const results = matchSort(
    options,
    'name',
    query,
  )

  const findOptionByName = (optionName: string) => (
    find(
      options,
      ({ name }) => toLower(name) === toLower(trim(optionName)),
    ) || null
  )

  const onOptionSelect = (option: string) => {
    const selectedOption = findOptionByName(option)
    setQuery(selectedOption?.name || '')
    onSelect?.(selectedOption)
  }

  const onInputBlur = (event: FocusEvent<HTMLInputElement>) => {
    const target = event.relatedTarget as HTMLElement | null
    // клик по элементу списка тоже вызывает onBlur
    // если кликали элемент списка то событие обрабатывает onOptionSelect
    if (target && isOptionClicked(target)) return

    onOptionSelect(query)
  }

  return {
    ...useKeyboardScroll(),
    onInputBlur,
    onOptionSelect,
    onQueryChange,
    options: filterOptions ? results : options,
    query,
  }
}
