import React, { useState, forwardRef } from 'react'
import { bool, string, func, shape, node, number } from 'prop-types'
import classNames from 'classnames'
import './DemioInput.less'

const DemioInput = forwardRef(
  (
    {
      type,
      onChange,
      required,
      hasError,
      name,
      label,
      showLabel,
      onBlur,
      onFocus,
      errorMessage,
      showErrorMessage,
      className,
      style,
      addonComponent,
      onKeyUp,
      value,
      maxLength,
      autoFocus,
      autoComplete,
      hint,
      placeholder,
      validateWithPattern,
      id,
      showErrorDot,
      optionalText,
      ...props
    },
    ref
  ) => {
    const [isFocused, setFocused] = useState(false)

    const handleOnKeyDown = (inputType) => (event) => {
      if (!event.key) return

      const isNumber = /\d|Arrow|Backspace|Delete|Meta|Paste|Tab/.test(event.key)
      const isWhite = / |Spacebar/.test(event.key)
      if (!isNumber && inputType === 'number') {
        event.preventDefault()
      }

      if (isWhite && inputType === 'url') {
        event.preventDefault()
      }
    }

    const handleOnKeyUp = (event) => {
      if (onKeyUp) {
        onKeyUp(event)
      }
    }

    const handleOnChange = ({ target }) => {
      if (onChange) {
        onChange(target.value)
      }
    }

    const handleOnFocus = (event) => {
      setFocused(true)

      if (onFocus) {
        onFocus(event)
      }
    }

    const handleOnBlur = (event) => {
      setFocused(event?.relatedTarget && event?.relatedTarget?.type === 'submit')

      if (onBlur) {
        onBlur(event)
      }
    }

    const renderError = () => {
      const isEmpty = !value
      // some areas set value to null which breaks upon getting `.length`
      const valueLength = value ? value.length : 0
      let charsCounter = null
      let messageType = 'input'
      const message = errorMessage || `Please enter a valid ${messageType}`
      const showMessage =
        showErrorMessage &&
        ((!!required && isEmpty && type !== 'text' && type !== 'password') || hasError)

      if (type === 'email') {
        messageType = ' email'
      }
      if (type === 'number') {
        messageType = ' number'
      }
      if (type === 'url') {
        messageType = ' web link'
      }
      if (maxLength && valueLength >= maxLength - 20) {
        charsCounter = `(${valueLength}/${maxLength})`
      }
      return (
        (showMessage || charsCounter) && (
          <div className="Demio-Input-error">
            {showMessage && `${message}${showErrorDot ? '.' : ''}`}
            {charsCounter && <span className="Demio-Input-field-counter">{charsCounter}</span>}
          </div>
        )
      )
    }

    const renderTextareaField = () => (
      <div className="Demio-Input-container">
        <textarea
          name={name}
          id={id || name}
          placeholder={isFocused ? '' : placeholder || label}
          value={value}
          required={required}
          maxLength={maxLength}
          onChange={handleOnChange}
          onFocus={handleOnFocus}
          onBlur={handleOnBlur}
          // eslint-disable-next-line jsx-a11y/no-autofocus
          autoFocus={autoFocus}
          ref={ref}
          // eslint-disable-next-line react/jsx-props-no-spreading
          {...props}
        />
      </div>
    )

    const renderTextField = () => {
      let pattern = ''
      let inputType = type

      if (type === 'email') {
        inputType = 'email'
        pattern = '^\\S+@\\S+\\.\\S+$'
      }
      if (type === 'number') {
        inputType = 'text'
        pattern = '^[0-9]+$'
      }
      if (type === 'url') {
        inputType = 'text'
        pattern = '^\\S+\\.\\S+$'
      }

      const fieldProps = {
        value,
        type: inputType,
        name,
        placeholder: isFocused ? '' : placeholder || label,
        required,
        ...(maxLength && { maxLength }),
        onKeyDown: handleOnKeyDown(type),
        onChange: handleOnChange,
        onFocus: handleOnFocus,
        onBlur: handleOnBlur,
        onKeyUp: handleOnKeyUp,
        ...(pattern && validateWithPattern ? { pattern } : {}),
        autoFocus,
        autoComplete,
        ref,
        ...props,
      }

      return (
        <div
          className={classNames({
            'Demio-Input-container': true,
            '--addon-component': addonComponent,
          })}>
          {/* eslint-disable-next-line react/jsx-props-no-spreading */}
          <input id={id || name} {...fieldProps} />
          {addonComponent}
        </div>
      )
    }

    const renderWithLabel = (component) => (
      <div
        className={classNames({
          'Demio-Input-field': true,
          'Demio-Input-field-invalid': hasError,
          'Demio-Input-field-hint': isFocused,
          [className]: className,
        })}
        style={style}>
        {!showLabel ? (
          component
        ) : (
          <label htmlFor={name}>
            {type !== 'checkbox' && (
              <>
                <span
                  className={classNames('Demio-Input-label', {
                    'Demio-Input-label--visible': (isFocused || value) && showLabel,
                  })}>
                  {label}
                </span>
                {!required && (
                  <span className="Demio-Input-label-optional">{optionalText || 'Optional'}</span>
                )}
              </>
            )}
            {component}
          </label>
        )}
        {(hasError || (maxLength && isFocused)) && renderError()}
        {isFocused && !hasError && hint && <div className="Demio-Input-hint">{hint}</div>}
      </div>
    )

    if (type === 'textarea') return renderWithLabel(renderTextareaField())
    return renderWithLabel(renderTextField())
  }
)

DemioInput.propTypes = {
  id: string,
  type: string,
  label: string,
  placeholder: string,
  validateWithPattern: string,
  onChange: func,
  required: bool,
  hasError: bool,
  name: string,
  showLabel: bool,
  onBlur: func,
  onFocus: func,
  errorMessage: string,
  showErrorMessage: bool,
  className: string,
  style: shape({}),
  addonComponent: node,
  value: string,
  hint: string,
  maxLength: number,
  autoFocus: bool,
  autoComplete: string,
  onKeyUp: func,
  showErrorDot: bool,
  optionalText: string,
}

DemioInput.displayName = 'DemioInput'

DemioInput.defaultProps = {
  type: 'text',
  hint: '',
  label: '',
  id: '',
  placeholder: '',
  required: false,
  showLabel: false,
  autoFocus: false,
  errorMessage: null,
  showErrorMessage: true,
  hasError: false,
  value: '',
  name: '',
  className: '',
  style: {},
  onFocus: () => {},
  onKeyUp: () => {},
  onBlur: () => {},
  onChange: () => {},
  addonComponent: null,
  autoComplete: 'off',
  maxLength: 524288,
  validateWithPattern: null,
  showErrorDot: true,
  optionalText: '',
}

export default DemioInput
