import React, { useEffect } from 'react';
import PropTypes from 'prop-types';
import throttle from 'lodash/throttle';

// Style
/* istanbul ignore next */
if (typeof window !== 'undefined') require('./style.scss');

/* istanbul ignore next */
const nearest10s = (num, base = 10) => Math.round(num / base) * base;

const defaultOpts = {
  showMarks: false,
  showLabels: undefined,
  roundLabels: undefined,
  limit: {
    lower: undefined,
    upper: undefined,
  },
};

const Range = ({ list, max, min, options, step, defaultValue, ...props }) => {
  const opts = { ...defaultOpts, ...options };

  const ROOT = React.createRef();
  const INPUT = React.createRef();

  /* istanbul ignore next */
  const updateLabel = num => {
    if (opts.showLabels === false) return '';
    if (opts.roundLabels === false) return num;

    let label;
    const datalist = INPUT.current.list;

    if (datalist) {
      let selector = `option[value="${num}"]`;
      let option = datalist.querySelector(selector);
      if (!option) {
        const rounded = nearest10s(num, opts.roundLabels || step);
        selector = `option[value="${rounded}"]`;
        option = datalist.querySelector(selector);
      }
      label = (option && (option.label || num)) || (!opts.showLabels ? '' : num);
      if ('updateInfo' in props) props.updateInfo(option); // TODO: change callback to [updateLabels]
    } else {
      label = num;
      if ('updateInfo' in props) props.updateInfo(label); // TODO: change callback to [updateLabels]
    }

    return label;
  };

  /* istanbul ignore next */
  const handleChange = throttle(e => {
    const { target } = e;

    const val = target.value;
    const out = ROOT.current.querySelector('output');
    const wrap = ROOT.current.closest('.Range');

    out.value = updateLabel(val);
    wrap.style.setProperty('--range-val', val);

    return val;
  }, 88);

  useEffect(() => {
    const event = new Event('input', { bubbles: true });
    const target = INPUT.current;
    target.dispatchEvent(event);
  });

  return (
    <div
      ref={ROOT}
      className={`Range ${opts.showMarks ? 'Range_marks' : /* istanbul ignore next */ ''}`}
      style={{
        ...props.style,
        '--range-min': min,
        '--range-max': max,
        /* istanbul ignore next */
        '--range-marks': typeof opts.showMarks === 'number' ? opts.showMarks : step,
        '--range-val': defaultValue,
      }}
    >
      <div className="Range-progress" />

      <input
        ref={INPUT}
        className="Range-input"
        defaultValue={defaultValue}
        list={list}
        max={max}
        min={min}
        onInput={e => {
          if (opts.limit.lower && e.target.value < opts.limit.lower) {
            INPUT.current.value = opts.limit.lower;
          }
          e.persist();
          return handleChange(e);
        }}
        step={step}
        style={{ width: '100%' }}
        type="range"
      />

      <output className="Range-output" htmlFor="TEST">
        {opts.showLabels && defaultValue}
      </output>
      {props.children}
    </div>
  );
};
Range.propTypes = {
  defaultValue: PropTypes.number,
  id: PropTypes.string,
  list: PropTypes.string,
  max: PropTypes.number,
  min: PropTypes.number,
  options: PropTypes.shape({
    limit: PropTypes.shape({ lower: PropTypes.number, upper: PropTypes.number }),
    roundLabels: PropTypes.oneOfType([PropTypes.number, PropTypes.bool]),
    showLabels: PropTypes.bool,
    showMarks: PropTypes.oneOfType([PropTypes.bool, PropTypes.number]),
  }),
  step: PropTypes.number,
  style: PropTypes.object,
  updateInfo: PropTypes.func,
};
Range.defaultProps = {
  defaultValue: 12,
  list: '',
  max: 10,
  min: 0,
  options: {
    showLabels: undefined,
    showMarks: false,
  },
  step: 1,
};

export default Range;
