import type { DetailedHTMLProps } from 'react';
import React, {
  forwardRef,
  useCallback,
  useEffect,
  useRef,
  useState,
} from 'react';

export interface AutoSizingInputProps
  extends DetailedHTMLProps<
    React.InputHTMLAttributes<HTMLInputElement>,
    HTMLInputElement
  > {
  value?: string;
  onChangeValue?: (value: string) => void;
  onChange?: React.ChangeEventHandler<HTMLInputElement>;
}

export const AutoSizingInput = forwardRef<
  HTMLInputElement,
  AutoSizingInputProps
>(
  (
    { value: _value, onChangeValue, onChange, ...rest },
    ref
  ): React.ReactNode => {
    const [value, setValue] = useState(_value);
    const [width, setWidth] = useState(0);
    const span = useRef<HTMLSpanElement>(null);
    const defaultRef = useRef<HTMLInputElement>(null);
    const inputRef = ref || defaultRef;

    useEffect(() => {
      span.current && setWidth(span.current.offsetWidth);
    }, [value]);

    const changeHandler = useCallback<
      React.ChangeEventHandler<HTMLInputElement>
    >(
      (evt) => {
        onChange?.(evt);
        setValue(evt.target.value);
      },
      [onChange]
    );

    useEffect(() => {
      setValue(_value || '');
    }, [_value]);

    useEffect(() => {
      onChangeValue?.(value || '');
    }, [value, onChangeValue]);

    const onClick = useCallback(() => {
      if ('current' in inputRef) {
        inputRef.current?.focus();
      }
    }, [inputRef]);

    const onKeyDown = useCallback<React.KeyboardEventHandler<HTMLDivElement>>(
      (evt) => {
        if (evt.key === 'Enter') {
          onClick();
        }
      },
      [onClick]
    );

    return (
      <div
        className="auto-sizing-input wrapper"
        onClick={onClick}
        onKeyDown={onKeyDown}
        role="button"
        style={{ width: `${width + 24}px` }}
        tabIndex={0}
      >
        <span aria-hidden="true" ref={span} style={rest.style}>
          {value || rest.placeholder || ''}
        </span>
        <input
          onChange={changeHandler}
          ref={inputRef}
          value={value}
          {...rest}
        />
      </div>
    );
  }
);

AutoSizingInput.displayName = 'AutoSizingInput';
