// @flow
import { indexOf, isEmpty } from 'lodash';
import React, { useMemo } from 'react';
import styled, { css } from 'styled-components';
import { v4 } from 'uuid';
import type { ThemeProps, TranslatedContent } from '../../../types';
import { Box } from '../../../primitives';
import { Text, FieldLabel } from '../../typography';
import { getPaletteColor } from '../../../mixins/branding';
import {
  focusedBoxShadow,
  focusedBorderShadow,
  focusedBorderColor,
  transitionFade,
} from '../../../mixins/focus';
import type { RangeProps } from './range.proptypes';

const focusStateMixin = css`
  box-shadow: ${focusedBoxShadow}, ${focusedBorderShadow} ${focusedBorderColor};
`;

const getRangeInputBackgroundColor = ({
  theme,
  greyOut,
}: {|
  ...ThemeProps,
  greyOut: boolean,
|}) => {
  const colorCode = greyOut ? 'greyscale-medium' : 'brand-secondary-base';
  return getPaletteColor(colorCode)({ theme });
};

const getWidth = ({ value, options }) => {
  const index = indexOf(options, value);
  // want 0-indexed length
  return `${(index / (options.length - 1)) * 100}`;
};

const StyledRangeInput = styled.input`
  appearance: none;
  margin: 12px 0 0;
  width: 100%;
  height: 6px;

  background: linear-gradient(
    to right,
    ${getRangeInputBackgroundColor} 0%,
    ${getRangeInputBackgroundColor} ${getWidth}%,
    ${getPaletteColor('greyscale-light')} ${getWidth}%,
    ${getPaletteColor('greyscale-light')} 100%
  );

  ::-webkit-slider-runnable-track {
    border-radius: 3px;
    cursor: pointer;
    height: 6px;
    width: 100%;
  }

  ::-webkit-slider-thumb {
    appearance: none;
    background: #fff;
    transition: ${transitionFade};
    border-radius: 10px;
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
    cursor: pointer;
    height: 20px;
    margin-top: -7px;
    width: 20px;
  }

  ::-moz-range-track {
    background: ${getPaletteColor('greyscale-light')};
    border-radius: 3px;
    cursor: pointer;
    height: 6px;
    width: 100%;
  }

  ::-moz-range-thumb {
    background: #fff;
    border-radius: 10px;
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
    cursor: pointer;
    height: 20px;
    width: 20px;
    transition: ${transitionFade};
  }

  ::-moz-range-progress {
    background: ${getRangeInputBackgroundColor};
    border-radius: 3px;
    cursor: pointer;
    height: 6px;
    width: 100%;
  }

  ::-ms-track {
    background: transparent;
    border-color: transparent;
    color: transparent;
    cursor: pointer;
    height: 6px;
    width: 100%;
  }

  ::-ms-fill-lower {
    background: ${getRangeInputBackgroundColor};
    border-radius: 3px;
  }

  ::-ms-fill-upper {
    background: ${getPaletteColor('greyscale-light')};
    border-radius: 3px;
  }

  ::-ms-thumb {
    background: #fff;
    border-radius: 10px;
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
    cursor: pointer;
    height: 20px;
    width: 20px;
    transition: ${transitionFade};
  }

  :focus {
    outline: none;

    ::-webkit-slider-thumb {
      ${focusStateMixin}
    }

    ::-moz-range-thumb {
      ${focusStateMixin}
    }
    ::-ms-thumb {
      ${focusStateMixin}
    }
  }
`;

export const NoMarginText = styled(Text)`
  margin-bottom: 0;
`;

const SliderContainer = styled(Box)`
  position: relative;
  /*width: 100%; */
`;

const SliderLabel = styled(Text)`
  color: ${getPaletteColor('greyscale-dark')};
`;

const Labels = ({
  minLabel,
  maxLabel,
}: {|
  minLabel: TranslatedContent,
  maxLabel: TranslatedContent,
|}) => (
  <>
    <SliderLabel align="left" fontSize="f1" bold spacing="m0">
      {minLabel}
    </SliderLabel>
    <SliderLabel align="right" fontSize="f1" bold spacing="m0">
      {maxLabel}
    </SliderLabel>
  </>
);

const LegendContainer = ({ children }: {| children: React$Node |}) => (
  <Box stacked="row" justify="space-between" align="center" spacing="pt1">
    {children}
  </Box>
);

const range = ({
  start,
  stop,
  step,
}: {
  start: number,
  stop: number,
  step: number,
}): number[] =>
  Array.from({ length: (stop - start) / step + 1 }, (_, i) => start + i * step);

export const Range = (props: RangeProps) => {
  const {
    onRangeChange,
    minLabel = '',
    maxLabel = '',
    min,
    max,
    value,
    ariaValuetext,
    spacing = 'mt1',
    grow,
    shrink,
    basis,
    onBlur,
    onFocus,
    className,
    id,
    name,
    ...rest
  } = props;

  const rangeInputId = useMemo(() => `range-${v4()}`, []);

  const options = range({ start: min, stop: max, step: 1 });

  let rangeInputLabel;
  let showLabel = true;
  let label = '';

  if (props.ariaLabelledBy != null) {
    rangeInputLabel = { 'aria-labelledby': props.ariaLabelledBy };
  } else if (props.label != null) {
    rangeInputLabel = { 'aria-label': props.label };
    // $FlowFixMe the type guard above should prevent this from being an issue.
    showLabel = !props.hideLabel;
    label = props.label;
  }

  return (
    <SliderContainer
      {...{ spacing, grow, shrink, basis }}
      data-cy={props['data-cy']}
      data-qa={props['data-qa']}
      id={id}
      className={className}
      {...rest}
    >
      {showLabel && <FieldLabel htmlFor={rangeInputId}>{label}</FieldLabel>}
      <StyledRangeInput
        type="range"
        role="slider"
        min={min}
        max={max}
        options={options}
        value={value}
        id={rangeInputId}
        name={name}
        onChange={(event) => onRangeChange(parseInt(event.target.value, 10))}
        aria-orientation="horizontal"
        aria-valuetext={ariaValuetext}
        onFocus={onFocus}
        onBlur={onBlur}
        {...rangeInputLabel}
      />
      <LegendContainer>
        <Labels
          minLabel={isEmpty(minLabel) ? min.toString() : minLabel}
          maxLabel={isEmpty(maxLabel) ? max.toString() : maxLabel}
        />
      </LegendContainer>
    </SliderContainer>
  );
};

Range.displayName = 'Range';
