import PropTypes from 'prop-types';
import React, { useState } from 'react';
import { MAX_RATING } from '../../constants/ratings';
import { generateUniqueID } from '../../tools/helpers';
import { createNamespace } from '../../tools/namingTools';
import Icon from '../icon/Icon';
import './styles/ratingInput.scss';

const n = createNamespace('RatingInput');

const RatingInput = ({
    rating,
    ratingScore,
    name,
    height,
    error,
    inputRef,
    i18n: {
        labels: labelsProp
    }
}) => {
    const [curHover, setHover] = useState(null);
    const [curValue, setValue] = useState(rating);
    const labels = { ...RatingInput.defaultProps.i18n.labels, ...labelsProp };

    const errorId = generateUniqueID(['error', name]);

    // when hovering, display candidate value instead of set value
    const displayValue = curHover || curValue;

    // defaults to state if no change listener is defined in props
    const onChange = e => setValue(parseInt(e.target.value, 10));

    const renderRatings = () => [...Array(ratingScore).keys()].map(index => {
        const status = displayValue > index ? 'positive' : 'neutral';
        const labelModifiers = displayValue ? `rating${displayValue}` : '';
        const stars = index + 1;
        const uid = `star-${stars}`;
        const label = labels[`ratingLevel${stars}`];
        const highlightRating = () => setHover(stars);

        return (
            <li key={uid} {...n('ratingWrapper').props}>
                <input
                    {...n('radioBtn').withTestId().props}
                    type="radio"
                    id={uid}
                    name={name}
                    value={stars}
                    checked={curValue === stars}
                    onChange={onChange}
                    onKeyPress={onChange}
                    aria-describedby={Boolean(error) && errorId}
                    ref={inputRef}
                />
                {/* eslint-disable-next-line jsx-a11y/mouse-events-have-key-events */}
                <label
                    {...n('ratingLabel', labelModifiers).withTestId().props}
                    htmlFor={uid}
                    onMouseOver={highlightRating}
                >
                    <Icon type="star" style={{ height: `${height}px` }} {...n(status).withTestId('star').props} aria-hidden="true" />
                    <span {...n('starValue').props}>{label}</span>
                </label>
            </li>
        );
    });

    const selectedRatingLabel = labels[`ratingLevel${displayValue}`];

    return (
        <div {...n('wrapper').props}>
            <div {...n('score').props} onMouseLeave={() => setHover(null)}>
                <ol {...n('ratingsWrapper').props} aria-label={labels.ratingLabel}>
                    {renderRatings()}
                </ol>
                {displayValue && (
                    <span
                        {...n('currentValue').withTestId().props}
                        aria-hidden="true"
                    >
                        {selectedRatingLabel || '&nbsp;'}
                    </span>
                )}
            </div>
            {error && (
                <span {...n('error').props} id={errorId} aria-live="polite">{error}</span>
            )}
        </div>
    );
};

RatingInput.propTypes = {
    rating: PropTypes.number,
    ratingScore: PropTypes.number,
    name: PropTypes.string,
    height: PropTypes.number,
    error: PropTypes.string,
    inputRef: PropTypes.func,
    i18n: PropTypes.shape({
        labels: PropTypes.shape({
            ratingLevel1: PropTypes.string,
            ratingLevel2: PropTypes.string,
            ratingLevel3: PropTypes.string,
            ratingLevel4: PropTypes.string,
            ratingLevel5: PropTypes.string,
            ratingLabel: PropTypes.string
        })
    })
};

RatingInput.defaultProps = {
    rating: null,
    ratingScore: MAX_RATING,
    name: 'rating',
    height: null,
    error: null,
    inputRef: null,
    i18n: {
        labels: {
            ratingLevel1: 'Poor',
            ratingLevel2: 'Fair',
            ratingLevel3: 'Average',
            ratingLevel4: 'Good',
            ratingLevel5: 'Excellent',
            ratingLabel: 'Select your rating'
        }
    }
};

export default RatingInput;
