import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { uniqueId } from '../../libs/utils';
import RenderIf from '../RenderIf';
import StyledContainer from './styled/container';
import StyledLabel from './styled/label';
import StyledSlider from './styled/slider';
import StyledInputRange from './styled/inputRange';
import StyledValue from './styled/value';
import ErrorText from '../Input/styled/errorText';
import withReduxForm from '../../libs/hocs/withReduxForm';

/**
 * An input range slider lets the user specify a numeric value which must be between
 * two specified values.
 * @category Form
 */
class Slider extends Component {
    constructor(props) {
        super(props);
        this.sliderId = uniqueId('slider-id');
        this.errorMessageId = uniqueId('error-message');
        this.sliderRef = React.createRef();
    }

    getAriaDescribedBy() {
        const { error } = this.props;
        if (error) {
            return this.errorMessageId;
        }
        return undefined;
    }

    /**
     * Sets click on the element.
     * @public
     */
    click() {
        this.sliderRef.current.click();
    }

    /**
     * Sets focus on the element.
     * @public
     */
    focus() {
        this.sliderRef.current.focus();
    }

    /**
     * Sets blur on the element.
     * @public
     */
    blur() {
        this.sliderRef.current.blur();
    }

    render() {
        const {
            label,
            value,
            name,
            min,
            max,
            step,
            error,
            disabled,
            onBlur,
            onChange,
            onClick,
            onFocus,
            style,
            className,
            labelAlignment,
            hideLabel,
            required,
        } = this.props;
        const valueWidth = Math.max(`${max}`.length, `${min}`.length) + 1;
        return (
            <StyledContainer className={className} style={style}>
                <RenderIf isTrue={label}>
                    <StyledLabel
                        label={label}
                        labelAlignment={labelAlignment}
                        hideLabel={hideLabel}
                        inputId={this.sliderId}
                        required={required}
                    />
                </RenderIf>
                <StyledSlider>
                    <StyledInputRange
                        id={this.sliderId}
                        type="range"
                        name={name}
                        value={value}
                        min={min}
                        max={max}
                        step={step}
                        aria-describedby={this.getAriaDescribedBy()}
                        disabled={disabled}
                        required={required}
                        onClick={onClick}
                        onChange={onChange}
                        onBlur={onBlur}
                        onFocus={onFocus}
                        ref={this.sliderRef}
                    />
                    <StyledValue width={valueWidth} aria-hidden>
                        {value}
                    </StyledValue>
                </StyledSlider>
                <RenderIf isTrue={error}>
                    <ErrorText id={this.errorMessageId}>{error}</ErrorText>
                </RenderIf>
            </StyledContainer>
        );
    }
}

Slider.propTypes = {
    /** The text label for the slider. Provide your own label to describe the slider.
     * Otherwise, no label is displayed. */
    label: PropTypes.oneOfType([PropTypes.string, PropTypes.node]),
    /** Describes the position of the Slider label. Options include left, center and right.
     * This value defaults to center. */
    labelAlignment: PropTypes.oneOf(['left', 'center', 'right']),
    /** A boolean to hide the Slider label */
    hideLabel: PropTypes.bool,
    /** The numerical value of the slider. This value defaults to 0. */
    value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
    /** The name of the Slider. */
    name: PropTypes.string,
    /** The min value of the slider. This value defaults to 0. */
    min: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
    /** The max value of the slider. This value defaults to 100. */
    max: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
    /** The step increment value of the slider. Example steps include 0.1, 1, or 10.
     * This value defaults to 1. */
    step: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
    /** Specifies that the slider element must have a value selected before submitting the form. */
    error: PropTypes.oneOfType([PropTypes.string, PropTypes.node]),
    /** Specifies that the slider element should be disabled. This value defaults to false. */
    disabled: PropTypes.bool,
    /** Specifies that an input field must be filled out before submitting the form.
     * This value defaults to false. */
    required: PropTypes.bool,
    /** The action triggered when a value attribute changes. */
    onChange: PropTypes.func,
    /** The action triggered when the element is clicked. */
    onClick: PropTypes.func,
    /** The action triggered when the element receives focus. */
    onFocus: PropTypes.func,
    /** The action triggered when the element releases focus. */
    onBlur: PropTypes.func,
    /** A CSS class for the outer element, in addition to the component's base classes. */
    className: PropTypes.string,
    /** An object with custom style applied to the outer element. */
    style: PropTypes.object,
};

Slider.defaultProps = {
    label: null,
    value: undefined,
    name: undefined,
    min: 0,
    max: 100,
    step: 1,
    error: undefined,
    disabled: false,
    required: false,
    onChange: () => {},
    onClick: () => {},
    onFocus: () => {},
    onBlur: () => {},
    className: undefined,
    style: null,
    labelAlignment: 'center',
    hideLabel: false,
};

export default withReduxForm(Slider);
