// @flow
import React, { useCallback, useMemo, useEffect } from 'react';
import styled from 'styled-components';
import { v4 } from 'uuid';
import type { SelectProps } from './select.proptypes';
import { FormSelect } from './select.css-rules';
import { Box } from '../../../primitives';
import {
  FormBox,
  FloatingLabel,
  ErrorMessage,
  SuccessMessage,
  Description,
} from '../internals/forms.common';
import { ChevronDown, ErrorIcon } from '../../icon';
import { FieldLabel } from '../../typography/field-label';
import { HideAccessibly } from '../../../primitives/hide-accessibly';

const noop = () => {};

export const Select = <T>({
  children,
  disabled = false,
  error,
  className,
  placeholder,
  value,
  onBlur = noop,
  onFocus = noop,
  label,
  id,
  name,
  onChange,
  spacing,
  shrink,
  grow,
  basis,
  focusOnMount,
  success,
  description,
  labelStyle = 'floating',
  'data-qa': dataQa,
  'data-cy': dataCy,
  ...props
}: SelectProps<T>) => {
  const errorString =
    error != null && typeof error !== 'boolean' ? error : null;
  const handleChange = useCallback(
    (event) => {
      const newValue = event.target.value;
      onChange(newValue === 'UNDEFINED' ? undefined : newValue);
    },
    [onChange],
  );
  const formSelectId = useMemo(() => `select-${id ?? ''}-${v4()}`, [id]);
  const descriptionId = useMemo(() => `description-${id ?? ''}-${v4()}`, [id]);
  const hasErrorMessage = error != null && typeof error !== 'boolean';
  const showMessage =
    hasErrorMessage ||
    (description != null && description !== '') ||
    (success != null && success !== '');

  useEffect(() => {
    if (value == null && placeholder == null) {
      // eslint-disable-next-line no-console
      console.warn(
        'Patchwork Selector Usage error: you must supply a placeholder prop if value is allowed to be null or undefined.\n',
        new Error().stack,
      );
    }
  }, [value, placeholder]);

  return (
    <Box
      stacked="column"
      className={className}
      {...{
        spacing,
        shrink,
        grow,
        basis,
      }}
      data-cy={dataCy}
      data-qa={dataQa}
      id={id}
      {...props}
    >
      {labelStyle === 'external' && (
        <FieldLabel htmlFor={formSelectId}>{label}</FieldLabel>
      )}
      {labelStyle === 'external-large' && (
        <FieldLabel htmlFor={formSelectId} spacing="mb2">
          {label}
        </FieldLabel>
      )}
      {labelStyle === 'hidden' && (
        <HideAccessibly>
          <label htmlFor={formSelectId}>{label}</label>
        </HideAccessibly>
      )}
      <FormBox
        stacked="row"
        align="center"
        spacing="pr1"
        disabled={disabled}
        labelStyle={labelStyle}
      >
        {labelStyle === 'floating' && (
          <FloatingLabel
            htmlFor={formSelectId}
            data-has-value={value != null && value !== ''}
          >
            {label}
          </FloatingLabel>
        )}
        <FormSelect
          disabled={disabled}
          error={error}
          value={value == null && placeholder != null ? 'UNDEFINED' : value}
          onBlur={onBlur}
          onChange={handleChange}
          onFocus={onFocus}
          id={formSelectId}
          name={name}
          autoFocus={focusOnMount}
          isPlaceholderValue={value == null && placeholder != null}
          labelStyle={labelStyle}
          aria-invalid={!!error}
          data-has-value={value != null && value !== ''}
        >
          {placeholder != null ? (
            <option value="UNDEFINED">{String(placeholder)}</option>
          ) : null}
          {children}
        </FormSelect>
        <IconContainer shrink={0} stacked="row" align="center" aria-hidden>
          {error != null && error !== false && <ErrorIcon />}
          <ChevronDown />
        </IconContainer>
      </FormBox>
      <Box id={descriptionId} spacing={showMessage ? 'mb1' : 'mb0'}>
        <Box aria-live="polite">
          {errorString != null && <ErrorMessage message={errorString} />}
          {success != null && <SuccessMessage message={success} />}
        </Box>
        {description != null && <Description message={description} />}
      </Box>
    </Box>
  );
};

Select.displayName = 'Select';

// Absolute, no pointer events because we want clicking on
// this element to activate the Select
const IconContainer = styled(Box)`
  position: absolute;
  right: 0px;
  top: 0px;
  bottom: 0px;
  pointer-events: none;
`;
