import React, {
  ReactElement,
  forwardRef,
  ChangeEvent,
  useState,
  useRef,
  useCallback,
  useEffect,
} from 'react';
import { useTheme } from 'styled-components';
import css from '../../utils/css';

import StyledInput from '../Input/StyledInput';
import { getThemeState } from '../Input/utils';
import { TagInputWrapper, TagInputContainer } from './StyledTagInput';
import Affix from '../Input/Affix';
import { IconName } from '../Icon';
import Tag from '../Tag';
import { useResizeObserver } from '../../utils/hooks';

import { CommonProps } from '../common';
import { isNonEmptyArray } from '../../fp/NonEmptyArray';

export interface TagInputProps extends Omit<CommonProps, 'id'> {
  /**
   * Whether the input is disabled.
   */
  disabled?: boolean;
  /**
   * Id of <input> element.
   */
  id?: string;
  /**
   * Whether the input is invalid
   */
  invalid?: boolean;
  /**
   * Name of <input> element, is used to refer to the form data for submission.
   */
  name?: string;
  /**
   * Change event handler. Use `event.target.value` for new value
   */
  onChange?: (e: ChangeEvent) => void;
  /**
   * Callback invoked when the user clicks the X button on a tag. Receives value of removed tag.
   */
  onRemove?: (tag: {
    text: string | ReactElement;
    value?: string | number;
  }) => void;
  /**
   * Placeholder text in the absence of any value.
   */
  placeholder?: string;
  /**
   * Name of Icon or an Icon element to render on the left side of the input, before the user's cursor.
   */
  prefix?: IconName | ReactElement;
  /**
   * Whether or not Input's value is read only.
   */
  readonly?: boolean;
  /**
   * The size of the input box.
   */
  size?: 'small' | 'medium' | 'large';
  /**
   * Name of Icon or an Icon element to render on the right side of the input.
   */
  suffix?: IconName | ReactElement;
  /**
   * Array of tags to be rendered inside the input.
   */
  tags: {
    text: string | ReactElement;
    value?: string | number;
  }[];
  /**
   * The input's content value.
   */
  value?: string;
}

const TagInput = forwardRef<HTMLInputElement, TagInputProps>(
  (
    {
      tags,
      size = 'medium',
      invalid = false,
      disabled = false,
      readonly = false,
      placeholder,
      onRemove,
      prefix,
      suffix,
      name,
      id,
      className,
      style,
      sx = {},
      'data-test-id': dataTestId,
      ...inputAttrs
    }: TagInputProps,
    forwardedRef
  ): ReactElement => {
    const theme = useTheme();
    const themeHeightStrs = theme.sizes.input[size].match(/\d+/g);
    const themeHeight = parseInt(
      themeHeightStrs !== null && isNonEmptyArray(themeHeightStrs)
        ? themeHeightStrs[0]
        : '0',
      10
    );
    const [wrapperHeight, setWrapperHeight] = useState<number | string>(
      themeHeight
    );
    const containerRef = useRef<HTMLDivElement>(null);

    const expandInput = useCallback(
      ({ height }) => {
        if (height > themeHeight) setWrapperHeight('auto');
        else setWrapperHeight(themeHeight);
      },
      [themeHeight, setWrapperHeight]
    );

    useResizeObserver(expandInput, containerRef.current);

    useEffect(() => {
      if (
        containerRef.current !== null &&
        containerRef.current.clientHeight > themeHeight
      ) {
        setWrapperHeight('auto');
      }
    }, [containerRef, themeHeight]);

    return (
      <TagInputWrapper
        themeSize={size}
        themeState={getThemeState({ disabled, invalid })}
        className={className}
        data-test-id={dataTestId}
        style={{
          height: wrapperHeight,
          ...style,
          ...css(sx),
        }}
      >
        <Affix affix={prefix} themeSize={size} />
        <TagInputContainer
          themePaddingX={prefix === undefined ? size : 'none'}
          ref={containerRef}
        >
          {tags.map(tag => (
            <Tag
              key={tag.value}
              value={tag.value}
              text={tag.text}
              onRemove={disabled === false ? onRemove : undefined}
              size={size}
            />
          ))}
          <StyledInput
            disabled={disabled}
            placeholder={tags.length > 0 ? undefined : placeholder}
            readOnly={readonly}
            id={id}
            name={name}
            ref={forwardedRef}
            themePaddingLeft="none"
            themePaddingRight="none"
            {...inputAttrs}
          />
        </TagInputContainer>
        <Affix affix={suffix} themeSize={size} themeMarginLeft="auto" />
      </TagInputWrapper>
    );
  }
);

TagInput.displayName = 'TagInput';

export default TagInput;
