import * as React from 'react'
import * as PropTypes from 'prop-types'
import * as ReachSlider from '@reach/slider'
import styled from 'styled-components'
import stringify from '../../services/stringify'

const Wrapper = styled('div')({
  'position': 'relative',
  'display': 'inline-block',
  'verticalAlign': 'top',
  ':focus-within': {
    '--color-accent': 'var(--color-active, Highlight)',
  },
})

Wrapper.displayName = 'div'

const Base = styled(ReachSlider.SliderInput)({
  'boxSizing': 'border-box',
  'position': 'absolute',
  'bottom': 0,
  'right': 0,
  ':active': {
    cursor: 'grabbing',
  },
})

Base.displayName = 'input'

const Track = styled(ReachSlider.SliderTrack)({
  'borderRadius': '0.125rem',
  'position': 'relative',
  'width': '100%',
  'height': '100%',
  '::before': {
    display: 'block',
    position: 'absolute',
    content: "''",
    opacity: 0.25,
    width: '100%',
    height: '100%',
    backgroundColor: 'currentColor',
    borderRadius: '0.125rem',
  },
})

const Handle = styled(ReachSlider.SliderHandle)({
  'cursor': 'grab',
  'width': '1.25rem',
  'height': '1.25rem',
  'backgroundColor': 'var(--color-accent, blue)',
  'borderRadius': '50%',
  'zIndex': 1,
  'transformOrigin': 'center',
  'outline': 0,
  'position': 'relative',
  '::before': {
    position: 'absolute',
    top: 0,
    left: 0,
    width: '100%',
    height: '100%',
    content: "''",
    borderRadius: '50%',
    opacity: 0.5,
    pointerEvents: 'none',
  },
  ':focus::before': {
    boxShadow: '0 0 0 0.25rem var(--color-accent, blue)',
  },
})

const Highlight = styled(ReachSlider.SliderTrackHighlight)({
  backgroundColor: 'var(--color-accent, blue)',
  borderRadius: '0.125rem',
})

const SizingWrapper = styled('span')({
  width: '100%',
  display: 'inline-block',
  verticalAlign: 'top',
  position: 'relative',
})

SizingWrapper.displayName = 'SizingWrapper'

const TransformWrapper = styled('span')({
  display: 'inline-block',
  verticalAlign: 'top',
  transformOrigin: 'top left',
})

TransformWrapper.displayName = 'TransformWrapper'

const LabelWrapper = styled('span')({
  color: 'var(--color-accent, blue)',
  boxSizing: 'border-box',
  fontSize: '0.75em',
  maxWidth: '100%',
  overflow: 'hidden',
  textOverflow: 'ellipsis',
  whiteSpace: 'nowrap',
  fontWeight: 'bolder',
  zIndex: 2,
  pointerEvents: 'none',
  transformOrigin: 'top left',
  position: 'absolute',
  top: 0,
  left: 0,
  transitionProperty: 'color',
})

LabelWrapper.displayName = 'span'

const FallbackTrack = styled('span')({
  'padding': '1.875rem 0.75rem 0.875rem',
  'boxSizing': 'border-box',
  'position': 'absolute',
  'top': 0,
  'left': 0,
  'pointerEvents': 'none',
  '::before': {
    display: 'block',
    content: "''",
    opacity: 0.25,
    width: '100%',
    height: '100%',
    backgroundColor: 'currentColor',
    borderRadius: '0.125rem',
  },
})

const FallbackSlider = styled('input')({
  'boxSizing': 'border-box',
  'backgroundColor': 'transparent',
  'verticalAlign': 'top',
  'margin': 0,
  'width': '100%',
  'height': '2rem',
  'marginTop': '1rem',
  'appearance': 'none',
  'outline': 0,
  'position': 'absolute',
  'left': 0,
  '::-moz-focus-inner': {
    outline: 0,
    border: 0,
  },
  '::-webkit-slider-thumb': {
    cursor: 'grab',
    width: '1.25rem',
    height: '1.25rem',
    backgroundColor: 'var(--color-accent, blue)',
    borderRadius: '50%',
    zIndex: 1,
    transformOrigin: 'center',
    outline: 0,
    position: 'relative',
    appearance: 'none',
  },
  '::-moz-range-thumb': {
    cursor: 'grab',
    border: 0,
    width: '1.25rem',
    height: '1.25rem',
    backgroundColor: 'var(--color-accent, blue)',
    borderRadius: '50%',
    zIndex: 1,
    transformOrigin: 'center',
    outline: 0,
    position: 'relative',
    appearance: 'none',
  },
})

FallbackSlider.displayName = 'input'

const ClickArea = styled('label')({
  verticalAlign: 'top',
  width: '100%',
  height: '100%',
})

ClickArea.displayName = 'label'

export type Orientation = 'vertical' | 'horizontal'

type Dimension = 'width' | 'height'

const propTypes = {
  /**
   * The component orientation.
   */
  orientation: PropTypes.oneOf<Orientation>(['vertical', 'horizontal']),
  /**
   * Short textual description indicating the nature of the component's value.
   */
  label: PropTypes.any,
  /**
   * CSS size for the component length.
   */
  length: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
  /**
   * Class name used for styling.
   */
  className: PropTypes.string,
  /**
   * Is the component active?
   */
  disabled: PropTypes.bool,
}

type Props = PropTypes.InferProps<typeof propTypes>

/**
 * Component for inputting numeric values in a graphical manner.
 *
 * The component is styled using client-side scripts. When the component is rendered server-side,
 * the component falls back into the original `<input type="range">` element.
 *
 * @see {@link //reacttraining.com/reach-ui/slider/#sliderinput|Reach UI Slider} for the client-side implementation.
 * @param {'vertical' | 'horizontal'} [orientation] - The component orientation.
 * @param {*} [label] - Short textual description indicating the nature of the component's value.
 * @param {string | number} [length] - CSS size for the component length.
 * @param {string} [className] - Class name used for styling.
 * @param {boolean} [disabled] - Is the component active?
 * @param {object} etcProps - The component props.
 * @returns {React.ReactElement} The component elements.
 */
const Slider: React.FC<Props> = ({
  orientation = 'horizontal',
  label = '',
  length = '16rem',
  className = '',
  disabled = false,
  ...etcProps
}) => {
  const [isClient, setIsClient] = React.useState(false)

  React.useEffect(() => {
    window.document.body.style.setProperty('--reach-slider', '1')
    setIsClient(true)
  }, [])

  const parallelDimension: Dimension = orientation === 'horizontal' ? 'width' : 'height'

  const perpendicularDimension: Dimension = orientation === 'horizontal' ? 'height' : 'width'

  const perpendicularReference = orientation === 'horizontal' ? 'top' : 'left'

  const perpendicularTransform = orientation === 'horizontal' ? 'translateY' : 'translateX'

  if (isClient) {
    return (
      <Wrapper
        className={className!}
        style={{
          opacity: disabled ? 0.5 : undefined,
          [parallelDimension]: length!,
        }}
      >
        <ClickArea>
          <SizingWrapper
            style={{
              [parallelDimension]: length!,
              [perpendicularDimension]: '3rem',
            }}
          >
            <TransformWrapper
              style={{
                [parallelDimension]: length!,
                [perpendicularDimension]: '100%',
                transform:
                  orientation === 'horizontal'
                    ? undefined
                    : `rotate(-90deg) translateX(calc(${
                        typeof length! === 'number' ? `${length as number}px` : (length as string)
                      } * -1))`,
              }}
            >
              <LabelWrapper
                style={{
                  maxWidth: length!,
                }}
              >
                {stringify(label)}
              </LabelWrapper>
            </TransformWrapper>
          </SizingWrapper>
          <Base
            {...etcProps}
            disabled={disabled!}
            style={{
              [parallelDimension]: length,
              [perpendicularDimension]: '2rem',
              padding: orientation === 'horizontal' ? '0.875rem 0.75rem' : '0.75rem 0.875rem',
              cursor: disabled ? 'not-allowed' : undefined,
            }}
          >
            <Track>
              <Highlight
                style={{
                  [perpendicularDimension]: '100%',
                }}
              />
              <Handle
                style={{
                  [perpendicularReference]: '50%',
                  transform: `${perpendicularTransform}(-50%)`,
                  cursor: disabled ? 'not-allowed' : undefined,
                }}
              />
            </Track>
          </Base>
        </ClickArea>
      </Wrapper>
    )
  }

  return (
    <Wrapper
      className={className!}
      style={{
        opacity: disabled ? 0.5 : undefined,
        [parallelDimension]: length!,
      }}
    >
      <SizingWrapper
        style={{
          [parallelDimension]: length!,
          [perpendicularDimension]: '3rem',
        }}
      >
        <TransformWrapper
          style={{
            [parallelDimension]: length!,
            [perpendicularDimension]: '100%',
            transform:
              orientation === 'horizontal'
                ? undefined
                : `rotate(-90deg) translateX(calc(${typeof length! === 'number' ? `${length}px` : length} * -1))`,
          }}
        >
          <FallbackTrack
            style={{
              width: length!,
              height: '3rem',
              padding: '1.875rem 0.75rem 0.875rem',
            }}
          />
          <ClickArea>
            <LabelWrapper
              style={{
                maxWidth: length!,
              }}
            >
              {stringify(label)}
            </LabelWrapper>
            {stringify(label).length > 0 && ' '}
            <FallbackSlider
              {...etcProps}
              style={{
                width: length!,
              }}
              disabled={disabled!}
              type="range"
            />
          </ClickArea>
        </TransformWrapper>
      </SizingWrapper>
    </Wrapper>
  )
}

Slider.propTypes = propTypes

Slider.displayName = 'Slider'

export default Slider
