import { useCallback, useMemo, useRef, useState } from 'react'
import { IconButton, InputAdornment, TextField } from '@mui/material'
import { Search as SearchIcon } from '@mui/icons-material'
import { Clear as ClearIcon } from '@mui/icons-material'
import { debounce } from '@carto/ps-utils'
import { getWidgetStore, useWidget, useWidgetId } from '../../stores'
import { setSearcherText } from './searcher-toggle'
import { DEFAULT_SEARCHER_LABELS, type SearcherLabels } from './labels'
import { styles } from './style'

export interface SearcherProps {
  labels?: Partial<SearcherLabels>
  /** Debounce delay before the input value is written to the widget store. */
  debounceMs?: number
}

const DEFAULT_DEBOUNCE = 300

/**
 * Renders only when the matching `<SearcherToggle />` is enabled — drives the
 * `transformStates['searcher'].searchText` field via a debounced setter.
 */
export function Searcher({
  labels,
  debounceMs = DEFAULT_DEBOUNCE,
}: SearcherProps) {
  const id = useWidgetId()
  const resolved = { ...DEFAULT_SEARCHER_LABELS, ...labels }
  const enabled = useWidget(
    id,
    (s) => s.transformStates.searcher?.enabled ?? false,
  )

  const inputRef = useRef<HTMLInputElement | null>(null)

  // Hydrate from the store so re-toggling the input preserves any prior text.
  const [local, setLocal] = useState<string>(
    () =>
      (getWidgetStore(id).getState().transformStates.searcher?.searchText as
        | string
        | undefined) ?? '',
  )

  const debouncedWrite = useMemo(
    () =>
      debounce(
        (value: unknown) => setSearcherText(id, value as string),
        debounceMs,
      ),
    [id, debounceMs],
  )

  const handleSearchTextChange = useCallback(
    (e: React.ChangeEvent<HTMLInputElement>) => {
      const value = e.target.value
      setLocal(value)
      debouncedWrite(value)
    },
    [debouncedWrite],
  )

  const handleClear = useCallback(() => {
    setLocal('')
    setSearcherText(id, '')
    // Re-arm the debounced timer with '' so any pending stale write resolves
    // to the cleared value rather than re-applying the previous text.
    debouncedWrite('')
    inputRef.current?.focus()
  }, [id, debouncedWrite])

  if (!enabled) return null

  return (
    <TextField
      inputRef={inputRef}
      size='small'
      fullWidth
      variant='filled'
      placeholder={resolved.placeholder}
      value={local}
      onChange={handleSearchTextChange}
      // The Searcher only mounts when the user explicitly toggles the
      // SearcherToggle action — focusing the input on mount matches user
      // intent, so the jsx-a11y/no-autofocus warning is the false-positive
      // case (the rule targets unsolicited autofocus that disorients
      // screen readers).
      // eslint-disable-next-line
      autoFocus
      aria-label={resolved.placeholder}
      sx={styles.input}
      InputProps={{
        startAdornment: (
          <InputAdornment position='start'>
            <SearchIcon fontSize='small' />
          </InputAdornment>
        ),
        endAdornment: local ? (
          <InputAdornment position='end'>
            <IconButton
              size='small'
              edge='end'
              aria-label={resolved.clearAriaLabel}
              onClick={handleClear}
            >
              <ClearIcon fontSize='small' />
            </IconButton>
          </InputAdornment>
        ) : null,
      }}
    />
  )
}
