import React, { useReducer } from 'react';
import PropTypes from 'prop-types';
import { createNamespace } from '../../tools/namingTools';
import Checkbox from '../checkbox/Checkbox';
import { SMALL } from '../../constants/sizes';
// IMPORTANT: (s)css should be imported after other atoms/components
import './styles/CheckboxGroup.scss';

const nameSpace = 'CheckboxGroup';
const n = createNamespace(nameSpace);

/**
 Assumptions:
    - all checkboxes at the same level in the same group have the same queryKey
*/
const initValues = items => items && items.length && items.filter(item => item.selected).map(item => item.id);

/**
    Component keeps state of all items that are checked.
*/
function mainReducer(state, action) {
    const { item: { id }, isChecked } = action;
    const newState = [...(state || [])];
    const existingIndex = newState.indexOf(id);
    if (isChecked && existingIndex === -1) {
        newState.push(id);
    } else if (!isChecked && existingIndex !== -1) {
        newState.splice(existingIndex, 1);
    }
    return newState;
}

const CheckboxGroup = ({
    items,
    title,
    name,
    hint,
    isDisplayChildren,
    state,
    required,
    checkboxSize,
    onChange,
    inputRef,
    prefix,
    error
}) => {
    // default dispatch to parent, will create a reducer if not managed by parent.
    let handleCheckboxState = onChange;
    let valuesState;
    if (!onChange) {
        // state is not managed by an ancestor, create a reducer. Each child will also have its own.
        // eslint-disable-next-line
        [valuesState, handleCheckboxState] = useReducer(mainReducer, items, initValues);
    } else {
        // state is managed by an ancestor, so we need to get the correct list of values from it
        const uniqueKeys = [...new Set(items.map(item => item.queryKey))];
        const keyValues = uniqueKeys.map(queryKey => (state && state[queryKey]) || []);
        valuesState = keyValues.reduce((filterIds, queryKeyIds) => {
            queryKeyIds.forEach(id => filterIds.push(id));
            return filterIds;
        }, []);
    }

    const getCheckboxName = item => {
        const checkboxPrefix = `${prefix ? `${prefix}-` : ''}`;

        if (name) {
            return `${checkboxPrefix}${name}`;
        }

        if (item.query && item.id) {
            return `${checkboxPrefix}${item.queryKey}-${item.id}`;
        }
        const uniqueSuffix = Math.floor(Math.random() * 1000000);
        return `${checkboxPrefix}${item.queryKey || uniqueSuffix}-${item.id || uniqueSuffix}`;
    };

    const renderInputList = () => {
        if (!items || !items.length) {
            return null;
        }

        return (
            <>
                {items.map(item => {
                    const checkboxName = getCheckboxName(item);
                    const isChecked = (valuesState.indexOf(item.id) !== -1) || false;

                    return (
                        <div {...n('group').props} key={checkboxName}>
                            <Checkbox
                                label={item.text}
                                ariaLabel={item.text}
                                name={checkboxName}
                                size={checkboxSize}
                                required={required}
                                isChecked={isChecked}
                                inputRef={inputRef}
                                error={error}
                                onChange={e => handleCheckboxState && handleCheckboxState({
                                    item,
                                    // value returns string values 'false' when being checked (previously uncheckd),
                                    // 'true' when being unchecked (previously checked)
                                    isChecked: e ? e.currentTarget.value === 'false' : false
                                })}
                            />

                            {isChecked && isDisplayChildren && item.items && item.items.length ? (
                                <CheckboxGroup
                                    items={item.items}
                                    ariaLabel={item.text}
                                    onChange={onChange}
                                    state={state}
                                    prefix={prefix}
                                    required={required}
                                    inputRef={inputRef}
                                    error={error}
                                    checkboxSize={checkboxSize}
                                />
                            ) : ''}
                        </div>
                    );
                })}
                {hint && (
                    <span {...n('hint').props}>{hint}</span>
                )}
            </>
        );
    };

    return title ? (
        <fieldset {...n('fieldset').props}>
            <legend {...n('legend').props}>{title}</legend>
            {renderInputList()}
        </fieldset>
    ) : renderInputList();
};

const itemShape = {
    selected: PropTypes.bool,
    id: PropTypes.oneOfType([
        PropTypes.string,
        PropTypes.number
    ]),
    text: PropTypes.string,
    queryKey: PropTypes.string,
    url: PropTypes.string
};
itemShape.items = PropTypes.arrayOf(PropTypes.shape({ ...itemShape }));

CheckboxGroup.propTypes = {
    items: PropTypes.arrayOf(PropTypes.shape(itemShape)),
    title: PropTypes.string,
    name: PropTypes.string,
    hint: PropTypes.string,
    // state is a key => [ values ] map, whose keys are unknown at compile time
    state: PropTypes.object, // eslint-disable-line
    required: PropTypes.bool,
    checkboxSize: PropTypes.oneOf([SMALL.value]),
    isDisplayChildren: PropTypes.bool,
    onChange: PropTypes.func,
    inputRef: PropTypes.func,
    prefix: PropTypes.string,
    error: PropTypes.string
};

CheckboxGroup.defaultProps = {
    items: [],
    title: null,
    name: null,
    hint: null,
    isDisplayChildren: true,
    state: null,
    required: false,
    checkboxSize: null,
    onChange: null,
    inputRef: null,
    prefix: null,
    error: null
};

export default CheckboxGroup;
