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

import Box from '../box'
import { IconButton } from '../button'
import { styled, useStyleConfig, vui, VuiComponent } from '../core'
import Label from '../label'
import { T } from '../t'
import { ChangeEvent, cs, filterUndefined, isString } from '../utils'
import AutoCompletePopover from './autoCompletePopover'
import { clearIconSize, displayValueOnlyTextSize, helpTextSize, inputColors, inputStateMapping } from './consts'
import { InputProvider } from './context'
import { getInitialCount } from './helpers'
import HelpText from './helpText'
import { InputProps } from './input.types'
import InputIcon from './inputIcon'
import InputInput from './inputInput'

export const InputBase = styled.divBox`
  align-items: center;
  color: sandstone.10;
  background-color: white;
  border-radius: none;
  border-width: 1px;
  display: flex;
  flex-shrink: 0;
  outline: none;
  position: relative;
  transition-duration: 0s;
  transform: translate3d(0, 0, 0);
  width: 100%;

  &[aria-disabled='true'] {
    opacity: 0.5;
    cursor: not-allowed;
    background-color: sandstone.79;
    border-color: sandstone.79;
  }
`

/**
 * Displays an input element wrapped in a div to allow extra content, like counter, side icons or buttons.
 * Forwards many relevant props to the inner input. Handles different states, like loading or error.
 * Exposes some props to the children via context.
 */
export const Input = vui<'div', InputProps>((props, ref) => {
  const {
    allowClear,
    ariaLabel,
    autoComplete,
    autoCompleteMaxHeight,
    autoCompleteOptions,
    autoFocus,
    children,
    className,
    defaultValue,
    disabled,
    displayValueOnly,
    errorText,
    helpText,
    iconLeft,
    iconRight,
    id: idProp,
    input,
    inputProps,
    inputRef,
    itemLeft,
    itemRight,
    label,
    labelMarkOptional,
    labelTooltipText,
    max,
    maxLength,
    min,
    minLength,
    name,
    onBlur,
    onChange: onChangeProp,
    onFocus,
    pattern,
    placeholder,
    readOnly,
    required,
    showCount,
    size = 'lg',
    state = '',
    stateMapping,
    step,
    type = 'text',
    value,
    variant,
    ...rest
  } = props

  const generatedId = useId()
  const [count, setCount] = useState(getInitialCount(props))
  const [valueInternal, setValueInternal] = useState(defaultValue || '')
  const states = { ...inputStateMapping, ...stateMapping }

  const id = idProp || generatedId

  const styles = useStyleConfig('Input', props)

  const context = useMemo(() => filterUndefined({ disabled, size, variant }), [disabled, size, variant])

  function onChange(e: ChangeEvent) {
    const inputValue = e.target.value

    // Limit input length if maxLength is set
    const value = maxLength ? inputValue.slice(0, maxLength) : inputValue

    setValueInternal(value)
    setCount(value?.length)
    onChangeProp?.(e)
  }

  function onAutoCompleteSelect(value: string) {
    const e = { target: { value }, type: 'autoCompleteSelect' }
    onChange(e as unknown as ChangeEvent)
  }

  function onClear() {
    const value = ''
    const e = { target: { value }, type: 'onClear' }
    setValueInternal(value)
    setCount(0)
    onChange(e as unknown as ChangeEvent)
  }

  const aliasedProps = filterUndefined({
    'aria-disabled': disabled,
    bg: readOnly ? 'sandstone.97' : undefined,
    borderColor: readOnly ? 'sandstone.55' : undefined,
    focusWithinBorderColor: !readOnly ? 'transparent' : undefined,
    focusWithinRingColor: readOnly ? 'transparent' : undefined,
  })

  useEffect(() => {
    if (value === undefined && defaultValue !== undefined) return
    setValueInternal(value ?? '')
    setCount(value?.toString()?.length ? `${value}`.length : 0)
  }, [value])

  const filterAutoCompleteOptions = (i: string) =>
    valueInternal === i ? false : !valueInternal || i.toLowerCase().includes(`${valueInternal}`.toLowerCase())

  return (
    <InputProvider value={context}>
      {displayValueOnly ? (
        <T className={cs('vui-input-value', className)} size={displayValueOnlyTextSize[size]}>
          {value || defaultValue}
        </T>
      ) : (
        <AutoCompletePopover
          autoCompleteMaxHeight={autoCompleteMaxHeight}
          autoCompleteOptions={autoCompleteOptions}
          filterAutoCompleteOptions={filterAutoCompleteOptions}
          onAutoCompleteSelect={onAutoCompleteSelect}
        >
          <Box className="vui-inputContainer" column ref={ref} {...rest}>
            {isString(label) ? (
              <Label
                aria-disabled={disabled}
                className="vui-inputLabel"
                htmlFor={id}
                markAsOptional={labelMarkOptional}
                mb={0.5}
                text={label}
                tooltipText={labelTooltipText}
                {...styles.label}
              />
            ) : (
              label
            )}
            <InputBase className={cs('vui-input', className)} {...styles.container} {...aliasedProps}>
              {itemLeft}
              {isString(iconLeft) ? <InputIcon ml={1} name={iconLeft} /> : iconLeft}
              {children ?? input ?? (
                <InputInput
                  ref={inputRef}
                  {...{
                    autoFocus,
                    ariaLabel,
                    disabled,
                    id,
                    max,
                    maxLength,
                    min,
                    minLength,
                    name,
                    onBlur,
                    onChange,
                    onFocus,
                    pattern,
                    placeholder,
                    readOnly,
                    required,
                    step,
                    type,
                  }}
                  autoComplete={autoCompleteOptions?.length ? 'off' : autoComplete}
                  value={valueInternal}
                  {...inputProps}
                />
              )}
              {isString(iconRight) ? <InputIcon mr={1} name={iconRight} /> : iconRight}
              {itemRight}
              {state && <InputIcon mr={1} {...states[state]?.iconProps} />}
              {allowClear && (
                <IconButton
                  disabled={!valueInternal}
                  icon="falTimes"
                  mr={0.5}
                  onClick={onClear}
                  size={clearIconSize[size]}
                  title="Clear"
                />
              )}
              {showCount && (
                <T
                  className="vui-inputCount"
                  color={maxLength && count > maxLength ? inputColors.error : inputColors.helpText}
                  position="absolute"
                  right={0}
                  size="sm"
                  top="calc(100% + 1px)"
                >
                  {count} {maxLength ? `/ ${maxLength}` : null}
                </T>
              )}
            </InputBase>
            {!!helpText && <HelpText size={helpTextSize[size]}>{helpText}</HelpText>}
            {!!errorText && (
              <HelpText isError size={helpTextSize[size]}>
                {errorText}
              </HelpText>
            )}
          </Box>
        </AutoCompletePopover>
      )}
    </InputProvider>
  )
}) as VuiComponent<'div', InputProps> & {
  Icon: typeof InputIcon
  Input: typeof InputInput
  HelpText: typeof HelpText
}

Input.Icon = InputIcon
Input.Input = InputInput
Input.HelpText = HelpText
Input.displayName = 'Input'

export default Input
