import React, { forwardRef, useCallback, useState } from 'react'
import { AppIcon } from '@codeleap/styles'
import { ForwardRefComponentWithDefaultProps } from '../../types'
import { TextInputProps, TextInput } from '../TextInput'
import { TypeGuards } from '@codeleap/types'
import { TextInput as RNTextInput } from 'react-native'

/**
 * `onSearchChange` fires after the debounce delay (default 500 ms), while `onValueChange`
 * fires on every keystroke — use `onTypingChange` to show a loading indicator between the two.
 * If `debounce` is `undefined` (not 0), `onSearchChange` fires synchronously with no timer.
 */
export type SearchInputProps = {
  onTypingChange?: (isTyping: boolean) => void
  onSearchChange: (search: string) => void
  onValueChange?: (search: string) => void
  onClear?: () => void
  showClear?: (search: string) => boolean
  debugName: string
  debounce?: number
  clearIcon?: AppIcon
  searchIcon?: AppIcon
  placeholder?: string
} & Partial<TextInputProps>

type Component = React.ForwardRefExoticComponent<SearchInputProps> & {
  defaultProps?: Partial<SearchInputProps>
}

export const SearchInput: Component = forwardRef((props: SearchInputProps, ref) => {
  const {
    debugName,
    onClear,
    onSearchChange,
    onTypingChange,
    clearIcon,
    searchIcon,
    debounce,
    placeholder,
    value,
    onValueChange,
    showClear,
    ...others
  } = {
    ...SearchInput.defaultProps,
    ...props,
  }

  /** When `value` + `onValueChange` are both provided the component is fully controlled — the internal `useState` is skipped entirely, so the caller owns the displayed text and must update it on every keystroke. */
  const [search, setSearch] = !TypeGuards.isNil(value) && !!onValueChange ? [value, onValueChange] : useState('')

  const setSearchTimeout = React.useRef<ReturnType<typeof setTimeout> | null>(null)

  const handleChangeSearch = useCallback((value: string) => {
    onTypingChange?.(true)

    setSearch(value)

    if (TypeGuards.isNil(debounce)) {
      onSearchChange?.(value)
    } else {
      if (setSearchTimeout.current) {
        clearTimeout(setSearchTimeout.current)
        setSearchTimeout.current = null
      }

      setSearchTimeout.current = setTimeout(() => {
        onSearchChange(value)
        onTypingChange?.(false)
      }, debounce ?? 0)
    }
  }, [onSearchChange, onTypingChange])

  const handleClear = useCallback(() => {
    setSearch('')
    onSearchChange?.('')
    onClear?.()
  }, [])

  return (
    <TextInput
      value={search}
      onValueChange={handleChangeSearch}
      placeholder={placeholder}
      debugName={`Search ${debugName}`}
      rightIcon={(showClear?.(search) ?? true) && {
        name: clearIcon,
        onPress: handleClear,
      }}
      leftIcon={{
        name: searchIcon,
      }}
      ref={ref as any}
      {...others}
    />
  )
})

SearchInput.defaultProps = {
  debounce: 500,
  clearIcon: 'x' as AppIcon,
  searchIcon: 'search' as AppIcon,
  showClear: (s) => !!s?.trim?.()
} as Partial<SearchInputProps>
