// @flow
import React, { useMemo } from 'react';
import { v4 } from 'uuid';
import styled from 'styled-components';
import { isArray } from 'lodash';
import type { TextInputProps } from './text-input.proptypes';
import { Box } from '../../../primitives';
import { getFontFamily, getFontSize } from '../../../mixins/typography';
import { FormInput } from '../internals/form-input';
import { ErrorLabel } from '../internals/forms.common';
import { FieldLabel } from '../../typography/field-label';
import { HideAccessibly } from '../../../primitives/hide-accessibly';
import { MaskedTextInput } from '../internals/masked-text-input';
import { getBaseFocusState } from '../../../mixins/focus';

const ErrorLabelWithMargin = styled(ErrorLabel).attrs(() => ({
  fontSize: 'f1',
}))`
  font-family: ${getFontFamily};
  font-size: ${getFontSize};
  line-height: 1.5;
`;

// FireFox doesn't respect the align-items: stretch property on
// inputs for some reason. See PAT-202.
const FullWidthFormInput = styled(FormInput)`
  width: 100%;
  ${getBaseFocusState({
    hoverTransition: false,
    activeTransition: false,
    pointerCursor: false,
  })}
`;

const generateErrorString = (error: mixed) => {
  if (isArray(error))
    return <ErrorLabelWithMargin>{error.join(', ')}</ErrorLabelWithMargin>;

  if (typeof error === 'string' || (typeof error === 'object' && error != null))
    return <ErrorLabelWithMargin>{error}</ErrorLabelWithMargin>;
};

export const TextInput = ({
  id = '',
  type,
  placeholder,
  iconType,
  iconPosition,
  label,
  value,
  disabled,
  focusOnMount,
  fontSize,
  buttonText,
  error,
  mask,
  maskOptions,
  large = false,
  hideLabel = false,
  hideBorder = false,
  inputMode,
  onChange = (
    _value: string,
    _event: SyntheticInputEvent<HTMLInputElement>,
  ) => {},
  onBlur,
  onFocus,
  onRef = (_ref) => {},
  buttonPress = () => {},
  className,
  spacing,
  grow,
  shrink,
  basis,
  hideValueButtonLabel,
  revealValueButtonLabel,
  floatLabel,
  'data-qa': dataQa,
  'data-cy': dataCy,
  name,
  ...props
}: TextInputProps<string>) => {
  const inputId = useMemo(() => `input-${id}-${v4()}`, [id]);
  const errorString = generateErrorString(error);
  const hasError = !!errorString || error === true;
  const icon = hasError ? 'error' : iconType;
  const position = hasError ? 'right' : iconPosition;

  let Label = null;
  if (!floatLabel) {
    Label = hideLabel ? (
      <HideAccessibly>
        <FieldLabel htmlFor={inputId}>{label}</FieldLabel>
      </HideAccessibly>
    ) : (
      <FieldLabel htmlFor={inputId} large={large}>
        {label}
      </FieldLabel>
    );
  }
  return (
    <Box
      stacked="column"
      className={className}
      {...{
        // $FlowFixMe
        spacing,
        grow,
        shrink,
        basis,
      }}
      data-cy={dataCy}
      data-qa={dataQa}
      id={id}
      {...props}
    >
      {Label}
      {mask ? (
        <MaskedTextInput
          id={inputId}
          type={type}
          placeholder={placeholder}
          value={value}
          error={error}
          focusOnMount={focusOnMount}
          fontSize={fontSize}
          onChange={onChange}
          onBlur={onBlur}
          onFocus={onFocus}
          onRef={onRef}
          large={large}
          disabled={disabled}
          iconType={icon}
          iconPosition={position}
          options={mask ? maskOptions : undefined}
          inputMode={inputMode}
          hideBorder={hideBorder}
          floatLabel={floatLabel}
          label={label}
          name={name}
          hideValueButtonLabel={hideValueButtonLabel}
          revealValueButtonLabel={revealValueButtonLabel}
        />
      ) : (
        <FullWidthFormInput
          id={inputId}
          type={type}
          placeholder={placeholder}
          value={value}
          buttonText={buttonText}
          error={error}
          focusOnMount={focusOnMount}
          fontSize={fontSize}
          onChange={onChange}
          onBlur={onBlur}
          onFocus={onFocus}
          onRef={onRef}
          buttonPress={buttonPress}
          large={large}
          disabled={disabled}
          iconType={icon}
          iconPosition={position}
          options={mask ? maskOptions : undefined}
          inputMode={inputMode}
          hideBorder={hideBorder}
          hideValueButtonLabel={hideValueButtonLabel}
          revealValueButtonLabel={revealValueButtonLabel}
          floatLabel={floatLabel}
          label={label}
          name={name}
        />
      )}
      {errorString}
    </Box>
  );
};

TextInput.displayName = 'TextInput';
