/* eslint-disable no-nested-ternary */
import { last } from 'lodash';
import PropTypes from 'prop-types';
import { ThemeProvider } from 'styled-components';
import React, { useState } from 'react';

import {
  Icon,
  Input,
  Button,
  Label,
  InputContainer,
  ArrowsContainer,
} from './styledComponents';

import IconPlus from '../images/icon-plus-input-white.svg';
import IconMinus from '../images/icon-minus-input-white.svg';

import { getTheme } from '../utils/theme';

import { Props } from './interfaces';

import colorPalette from '../utils/colorPalette';

const KEYBOARD = {
  UP: 38,
  DOWN: 40,
};

const ACTIONS = {
  ADD: '+',
  SUBSTRACT: '-',
  MANUAL: 'keyboard',
};

const getBackgroundColor = (
  isHighlighted?: boolean,
  isDisabled?: boolean,
  isButtonHovered?: boolean
) => {
  const backgroundColor = {
    default: colorPalette.WHITE,
    hovered: colorPalette.WHITE,
  };

  if (isDisabled) {
    backgroundColor.default = colorPalette.LMGrey;
    backgroundColor.hovered = colorPalette.LMGrey;
  } else if (isHighlighted) {
    backgroundColor.default = colorPalette.IP_LBlue;
    backgroundColor.hovered = colorPalette.IP_Blue;
  } else if (!isButtonHovered) {
    backgroundColor.hovered = colorPalette.LGrey;
  }

  return backgroundColor;
};

const getBorderColor = (isHighlighted?: boolean, focused?: boolean) => {
  const borderColor = {
    default: colorPalette.LMGrey,
    hovered: colorPalette.LMGrey,
    error: colorPalette.Info_Red,
  };

  if (isHighlighted) {
    borderColor.default = colorPalette.IP_Blue;
    borderColor.hovered = colorPalette.IP_DBlue;
    borderColor.error = colorPalette.IP_DBlue;
    borderColor.error = colorPalette.Info_Red;
  } else if (focused) {
    borderColor.default = colorPalette.IP_Black;
    borderColor.hovered = colorPalette.IP_Black;
    borderColor.error = colorPalette.Info_Red;
  }

  return borderColor;
};

const NUMBER_CHECK_REGEXP = new RegExp(/^(-)?\d*($|(\.|,)(\d+|$))$/);

const ZERO_AFTER_COMMA = new RegExp(/^\d+(\.(0+))$/);
const LAST_CHARACTER_IS_NUMBER = new RegExp(/^\d+(\.(\d))\d+$/);

// Interesting link to understand the purpose of this method
// https://stackoverflow.com/questions/588004/is-floating-point-math-broken
const formatFloatNumber = (floatNumber: number) => {
  if (!floatNumber) {
    return floatNumber;
  }

  return +floatNumber.toFixed(10);
};

const InputIncrement = (props: Props): JSX.Element => {
  const {
    label,
    height,
    width,
    theme,
    value,
    setValue,
    required,
    allowNull,
    allowDecimals,
    isDisabled,
    limitValues,
    placeholder,
    hasErrors,
    disableUpDownBehaviour,
    isHighlighted,
    isButtonHidden,
  } = props;

  const updatedTheme = getTheme(theme, 'inputIncrement');
  const [focused, setFocused] = React.useState(false);
  const onFocus = () => setFocused(true);
  const onBlur = () => setFocused(false);

  const [isButtonHovered, setIsButtonHovered] = useState(false);

  const changeValue = (type: string, inputValue?: string) => {
    if (!isDisabled) {
      if (
        !value &&
        value !== 0 &&
        allowNull &&
        [ACTIONS.ADD, ACTIONS.SUBSTRACT].includes(type)
      ) {
        setValue(limitValues.min);

        return;
      }

      if (type === ACTIONS.ADD) {
        const updatedValue = !Number.isNaN(value)
          ? limitValues.max || limitValues.max === 0
            ? Math.min(
                formatFloatNumber(
                  Number.parseFloat(value as unknown as string) + 1
                ),
                limitValues.max
              )
            : formatFloatNumber(
                Number.parseFloat(value as unknown as string) + 1
              )
          : 0;
        setValue(updatedValue);

        return;
      }

      if (type === ACTIONS.SUBSTRACT) {
        const updatedValue = !Number.isNaN(value)
          ? limitValues.min || limitValues.min === 0
            ? Math.max(
                limitValues.min,
                formatFloatNumber(
                  Number.parseFloat(value as unknown as string) - 1
                )
              )
            : formatFloatNumber(
                Number.parseFloat(value as unknown as string) - 1
              )
          : 0;

        setValue(updatedValue);

        return;
      }

      if (type === ACTIONS.MANUAL) {
        if (!inputValue) {
          setValue(allowNull ? null : 0);

          return;
        }

        const formattedValue = inputValue.replace(',', '.');
        if (
          !allowDecimals &&
          (!Number.isInteger(+formattedValue) || last(formattedValue) === '.')
        ) {
          /* eslint-disable no-restricted-globals */
          if (isNaN(+formattedValue)) {
            return;
          }

          setValue(Math.ceil(+formattedValue));
          return;
        }

        if (!NUMBER_CHECK_REGEXP.test(inputValue)) {
          return;
        }

        if (
          last(formattedValue) === '.' ||
          ZERO_AFTER_COMMA.test(formattedValue) ||
          LAST_CHARACTER_IS_NUMBER.test(formattedValue)
        ) {
          setValue(formattedValue);

          return;
        }

        const parsedValue = Number.parseFloat(formattedValue);
        const updatedValue = !Number.isNaN(parsedValue)
          ? limitValues.min || limitValues.min === 0
            ? Math.max(limitValues.min, parsedValue)
            : !limitValues.min
            ? parsedValue
            : value
          : formattedValue;

        setValue(updatedValue);

        return;
      }
    }

    setValue(value);
  };

  return (
    <ThemeProvider theme={updatedTheme}>
      {label && (
        <Label required={required} isDisabled={isDisabled}>
          {label}
        </Label>
      )}
      <InputContainer
        height={height}
        width={width}
        isDisabled={isDisabled}
        limitValues={limitValues}
        focused={focused}
        isButtonHovered={isButtonHovered}
      >
        <Input
          isButtonHidden={isButtonHidden}
          type="text"
          hasErrors={hasErrors}
          onChange={(e) => changeValue(ACTIONS.MANUAL, e.target.value)}
          value={value}
          onFocus={onFocus}
          onBlur={onBlur}
          onKeyDown={(event) => {
            if (
              disableUpDownBehaviour &&
              [KEYBOARD.UP, KEYBOARD.DOWN].includes(event.keyCode)
            ) {
              event.preventDefault();
            }
          }}
          disabled={isDisabled}
          isDisabled={isDisabled}
          focused={focused}
          isButtonHovered={isButtonHovered}
          placeholder={placeholder}
          backgroundColor={getBackgroundColor(
            isHighlighted,
            isDisabled,
            isButtonHovered
          )}
          borderColor={getBorderColor(isHighlighted, focused)}
          isHighlighted={isHighlighted}
        />
        {!isButtonHidden && (
          <ArrowsContainer isDisabled={isDisabled}>
            <Button
              onClick={() =>
                !isDisabled &&
                (value || value === 0) &&
                changeValue(ACTIONS.SUBSTRACT)
              }
              focused={focused}
              isDisabled={
                isDisabled ||
                value === limitValues.min ||
                (!value && value !== 0)
              }
              onMouseEnter={() => setIsButtonHovered(true)}
              onMouseLeave={() => setIsButtonHovered(false)}
            >
              <Icon bottom src={IconMinus} alt="icon-arrow-down-grey" />
            </Button>
            <Button
              backgroundColor={
                updatedTheme?.buttons.default.backgroundColorRight
              }
              backgroundColorFocused={
                updatedTheme?.buttons.default.backgroundColorRight
              }
              right
              onClick={() => !isDisabled && changeValue(ACTIONS.ADD)}
              focused={focused}
              isDisabled={isDisabled || value >= limitValues.max}
              onMouseEnter={() => setIsButtonHovered(true)}
              onMouseLeave={() => setIsButtonHovered(false)}
            >
              <Icon right src={IconPlus} alt="icon-arrow-up-grey" />
            </Button>
          </ArrowsContainer>
        )}
      </InputContainer>
    </ThemeProvider>
  );
};

InputIncrement.propTypes = {
  // eslint-disable-next-line react/forbid-prop-types
  theme: PropTypes.objectOf(PropTypes.any),
  limitValues: PropTypes.shape({
    min: PropTypes.oneOfType([PropTypes.number]),
    max: PropTypes.oneOfType([PropTypes.number]),
  }),
  label: PropTypes.string,
  width: PropTypes.string,
  height: PropTypes.string,
  required: PropTypes.bool,
  hasErrors: PropTypes.bool,
  isDisabled: PropTypes.bool,
  placeholder: PropTypes.string,
  allowNull: PropTypes.bool,
  allowDecimals: PropTypes.bool,
  disableUpDownBehaviour: PropTypes.bool,
  value: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
  setValue: PropTypes.func.isRequired,
  isHighlighted: PropTypes.bool,
  isButtonHidden: PropTypes.bool,
};

InputIncrement.defaultProps = {
  theme: null,
  label: null,
  value: '',
  height: '40px',
  width: '240px',
  limitValues: {},
  required: false,
  hasErrors: false,
  placeholder: '0',
  isDisabled: false,
  allowNull: false,
  allowDecimals: true,
  disableUpDownBehaviour: true,
  isHighlighted: false,
  isButtonHidden: false,
};

export default InputIncrement;
