// @flow
import React from 'react';
import randomstring from 'randomstring';
import type { ComponentType } from 'react';
import styled, { withTheme } from 'styled-components';
import { isArray } from 'lodash';
import type { TextInputProps } from './text-input.proptypes';
import type { ThemeProps } from '../../../types';
import { Box } from '../../../primitives';
import { getFontFamily, getFontSize } from '../../../mixins/typography';
import { FormInput } from '../form-input';
import { ErrorLabel } from '../forms.common';
import { FieldLabel } from '../../typography/field-label';
import { HideAccessibly } from '../../../primitives/hide-accessibly';
import { MaskedTextInput } from '../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>;
};

const _TextInput = ({
  id = '',
  type,
  placeholder,
  iconType,
  iconPosition,
  label,
  value,
  disabled,
  focusOnMount,
  buttonText,
  error,
  mask,
  maskOptions,
  large = false,
  hideLabel = false,
  hideBorder = false,
  inputMode,
  onChange = (
    _value: string,
    _event: SyntheticInputEvent<HTMLInputElement>,
  ) => {},
  onBlur = () => {},
  onFocus = () => {},
  onRef = (_ref) => {},
  buttonPress = () => {},
  className = '',
}: {|
  ...TextInputProps<string>,
  ...ThemeProps,
|}) => {
  const inputId = `input-${id}-${randomstring.generate(5)}`;
  const errorString = generateErrorString(error);
  const hasError = !!errorString || error === true;
  const icon = hasError ? 'error' : iconType;
  const position = hasError ? 'right' : iconPosition;

  return (
    <Box stacked="column" className={className}>
      {hideLabel ? (
        <HideAccessibly>
          <FieldLabel htmlFor={inputId}>{label}</FieldLabel>
        </HideAccessibly>
      ) : (
        <FieldLabel htmlFor={inputId} large={large}>
          {label}
        </FieldLabel>
      )}

      {mask ? (
        <MaskedTextInput
          id={inputId}
          type={type}
          placeholder={placeholder}
          value={value}
          error={error}
          focusOnMount={focusOnMount}
          onChange={onChange}
          onBlur={onBlur}
          onFocus={onFocus}
          onRef={onRef}
          large={large}
          disabled={disabled}
          iconType={icon}
          iconPosition={position}
          options={mask ? maskOptions : undefined}
          inputMode={inputMode}
          hideBorder={hideBorder}
        />
      ) : (
        <FullWidthFormInput
          id={inputId}
          type={type}
          placeholder={placeholder}
          value={value}
          buttonText={buttonText}
          error={error}
          focusOnMount={focusOnMount}
          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}
        />
      )}
      {errorString}
    </Box>
  );
};

export const TextInput: ComponentType<TextInputProps<string>> = withTheme(
  _TextInput,
);

TextInput.displayName = 'TextInput';
