import {
    clone,
    includes,
    isArray,
    isFunction,
    remove,
} from 'lodash';
import ClassNames from 'classnames';
import PropTypes from 'prop-types';
import React from 'react';
import KeyCode from '../../global/keyCode';
import RadioItem from './radioItem';
import withStyles from '../../styles/withStyles';

const propTypes = {
    align: PropTypes.oneOf(['left', 'right']),
    checked: PropTypes.oneOfType([
        PropTypes.bool,
        PropTypes.number,
    ]),
    children: PropTypes.node,
    classes: PropTypes.shape({
        input: PropTypes.string,
        isAlignedRight: PropTypes.string,
        isChecked: PropTypes.string,
        isDisabled: PropTypes.string,
        isFluid: PropTypes.string,
        isPill: PropTypes.string,
        label: PropTypes.string,
        labelNotClickable: PropTypes.string,
        root: PropTypes.string,
    }),
    className: PropTypes.string,
    /**
     * Deprecated prop. Please use `disabled` instead.
     */
    disable: PropTypes.bool,
    /**
     * A Radio can be disabled.
     */
    disabled: PropTypes.bool,
    fluid: PropTypes.bool,
    id: PropTypes.string,
    label: PropTypes.string,
    labelClick: PropTypes.bool,
    multi: PropTypes.bool,
    name: PropTypes.string,
    onChange: PropTypes.func,
    onKeyDown: PropTypes.func,
    pill: PropTypes.bool,
    style: PropTypes.shape({}),
    tabIndex: PropTypes.number,
    value: PropTypes.string,
};

const defaultProps = {
    align: null,
    checked: null,
    children: null,
    classes: null,
    className: null,
    disable: false,
    disabled: false,
    fluid: false,
    id: null,
    label: null,
    labelClick: true,
    multi: false,
    name: null,
    onChange: null,
    onKeyDown: null,
    pill: false,
    style: null,
    tabIndex: -1,
    value: '',
};

const isCheckedSingle = (id, index, isChecked) => (id === isChecked || index === isChecked);

const isCheckedMulti = (id, index, isChecked) => {
    const newId = id || index;

    return includes(isChecked, newId);
};

const styles = (theme) => {
    const size = 22;
    const sizeDot = 14;

    const inputStyle = {
        display: 'none',
        '&:checked +': {
            '& $label::after': {
                opacity: 1,
            },
        },
    };

    return {
        isAlignedRight: {},
        isDisabled: {},
        isFluid: {},
        isPill: {},
        input: {
            ...inputStyle,
        },
        label: {},
        labelNotClickable: {},
        root: {
            display: 'inline-block',
            marginRight: theme.spacing(2),
            minHeight: size,
            outline: 'none',
            position: 'relative',
            textAlign: 'left',
            '&:focus': {
                boxShadow: `0 0 0 1px ${theme.palette.active.primary}`,
            },
            '& .label': {
                color: theme.palette.text.primary,
                cursor: 'pointer',
                display: 'block',
                fontSize: 14,
                position: 'relative',
                '&::before, &::after': {
                    content: '""',
                    height: size,
                    left: 0,
                    position: 'absolute',
                    top: 0,
                    transition: 'opacity 150ms ease',
                    width: size,
                },
                '&::before': {
                    background: theme.palette.background.primary,
                    border: `1px solid ${theme.palette.border.primary}`,
                    borderRadius: 11,
                },
                '&::after': {
                    backgroundColor: theme.palette.cyan[500],
                    borderRadius: sizeDot / 2,
                    height: sizeDot,
                    margin: (size - sizeDot) / 2,
                    opacity: 0,
                    width: sizeDot,
                },
                '&$labelNotClickable': {
                    cursor: 'auto',
                    '&::before, &::after': {
                        cursor: 'pointer',
                    },
                },
                '& > span': {
                    display: 'inline-block',
                    paddingLeft: 33,
                    paddingTop: 2,
                },
            },
            '&$isAlignedRight': {
                marginLeft: 11,
                marginRight: 0,
                textAlign: 'left',
                '& .label': {
                    '&::before, &::after': {
                        left: 'auto',
                        right: 0,
                    },
                    '& > span': {
                        paddingLeft: 0,
                        paddingRight: 33,
                    },
                },
            },
            '&$isDisabled': {
                '& .radio-item .label': {
                    cursor: 'auto',
                    '&::before': {
                        background: theme.palette.background.secondary,
                    },
                    '&::after': {
                        backgroundColor: theme.palette.grey[400],
                    },
                },
                '&$isPill': {
                    '& .radio-item': {
                        '& .label': {
                            cursor: 'default',
                        },
                        '& .input:checked + .label': {
                            backgroundColor: theme.palette.grey[400],
                            borderColor: theme.palette.grey[400],
                        },
                        '&:last-child': {
                            input: {
                                '&:checked + .label': {
                                    borderRight: `1px solid ${theme.palette.grey[400]}`,
                                },
                            },
                        },
                        '&.radio-item-is-checked + .radio-item .label': {
                            borderLeft: `1px solid ${theme.palette.grey[400]}`,
                        },
                    },
                },
            },
            '&$isFluid': {
                display: 'block',
                marginRight: 0,
            },
            '&$isPill': {
                '& .radio-item': {
                    cursor: 'pointer',
                    display: 'inline-block',
                    outline: 'none',
                    '&:focus': {
                        boxShadow: `0 0 0 1px ${theme.palette.active.primary}`,
                    },
                    '&:first-child:focus': {
                        borderRadius: '16px 0 0 16px',
                    },
                    '&:last-child:focus': {
                        borderRadius: '0 16px 16px 0',
                    },
                    '& .input': {
                        ...inputStyle,
                    },
                    '& .label': {
                        backgroundColor: theme.palette.background.primary,
                        border: `1px solid ${theme.palette.border.primary}`,
                        borderRight: 0,
                        display: 'inline-block',
                        fontSize: 14,
                        fontWeight: theme.typography.fontWeightRegular,
                        outline: 'none',
                        padding: '6px 22px',
                        textAlign: 'center',
                        textDecoration: 'none',
                        transition: [
                            [
                                'background-color',
                                '125ms',
                                'linear',
                            ],
                            [
                                'border',
                                '125ms',
                                'linear',
                            ],
                            [
                                'color',
                                '125ms',
                                'linear',
                            ],
                        ],
                        whiteSpace: 'nowrap',
                        zIndex: -1,
                        '&::before, &::after': {
                            display: 'none',
                        },
                    },
                    '& .input:checked + .label': {
                        backgroundColor: theme.palette.cyan[500],
                        borderColor: theme.palette.cyan[500],
                        color: theme.palette.text.contrastText,
                    },
                    '&:first-child .label': {
                        borderRadius: '15.5px 0 0 15.5px',
                    },
                    '&:last-child': {
                        '& .label': {
                            borderRight: `1px solid ${theme.palette.border.primary}`,
                            borderRadius: '0 15.5px 15.5px 0',
                        },
                        '& .input:checked + .label': {
                            borderRight: `1px solid ${theme.palette.cyan[500]}`,
                        },
                    },
                    '&.radio-item-is-checked + .radio-item .label': {
                        borderLeft: `1px solid ${theme.palette.cyan[500]}`,
                    },
                },
            },
        },
    };
};

class Radio extends React.Component {
    constructor(props) {
        super(props);

        this.state = { isChecked: props.checked };

        this.onClick = this.onClick.bind(this);
        this.onKeyDown = this.onKeyDown.bind(this);
        this.onLabelClick = this.onLabelClick.bind(this);
        this.onLabelKeyDown = this.onLabelKeyDown.bind(this);
        this.onMouseDown = this.onMouseDown.bind(this);
    }

    componentDidUpdate(prevProps) {
        const {
            checked: prevChecked,
        } = prevProps;

        const {
            checked,
        } = this.props;

        if (checked !== prevChecked) {
            this.setState({ isChecked: checked });
        }
    }

    onClick(idArg, customValue) {
        const {
            disable,
            disabled,
            id,
            onChange,
            pill,
        } = this.props;

        const newValue = this.setIsChecked(idArg);
        const isNotDisabled = !disable && !disabled;

        if (isNotDisabled) {
            if (isFunction(onChange)) {
                onChange(pill ? idArg : id, newValue, customValue);
            } else {
                this.setState({ isChecked: newValue });
            }
        }
    }

    onKeyDown(event, idArg) {
        const {
            disable,
            disabled,
            id,
            onKeyDown,
            pill,
        } = this.props;

        if (event.keyCode === KeyCode.Enter) {
            const newValue = this.setIsChecked(idArg);
            const isNotDisabled = !disable && !disabled;

            if (!isNotDisabled) {
                if (isFunction(onKeyDown)) {
                    onKeyDown(pill ? idArg : id, newValue);
                } else {
                    this.setState({ isChecked: newValue });
                }
            }
        }
    }

    onLabelClick(event) {
        const { labelClick } = this.props;

        if (labelClick === false) {
            event.stopPropagation();
        }
    }

    onLabelKeyDown() {
        /**
         * NOTE: Need to use a prop function here someday.
         */

        return null;
    }

    onMouseDown(event) {
        event.preventDefault();
    }

    setIsChecked(idArg) {
        const {
            pill,
            multi,
        } = this.props;

        const {
            isChecked,
        } = this.state;

        let newValue = clone(isChecked);

        if (multi) {
            if (includes(newValue, idArg)) {
                remove(newValue, (v) => v === idArg);
            } else if (isArray(newValue)) {
                newValue.push(idArg);
            } else {
                newValue = [idArg];
            }
        } else if (pill) {
            newValue = idArg;
        } else {
            newValue = true;
        }

        return newValue;
    }

    render() {
        const {
            align,
            children,
            classes,
            className,
            disable,
            disabled,
            fluid,
            id,
            label,
            labelClick,
            multi,
            name,
            pill,
            style,
            tabIndex,
            value,
        } = this.props;

        const { isChecked } = this.state;
        const isDisabled = disable || disabled;

        const rootClasses = ClassNames(
            'ui',
            'radio',
            classes.root,
            className,
            {
                [classes.isAlignedRight]: align === 'right',
                'radio-align-right': align === 'right',
                [classes.isDisabled]: isDisabled,
                'radio-disabled': isDisabled,
                [classes.isFluid]: fluid,
                'radio-full-width': fluid,
                [classes.isChecked]: !pill && isChecked,
                'radio-is-checked': !pill && isChecked,
                [classes.isPill]: pill,
                'radio-pill': pill,
            },
        );

        if (pill) {
            const isCheckedItem = multi ? isCheckedMulti : isCheckedSingle;

            return (
                <div
                    className={rootClasses}
                    style={style}
                >
                    {React.Children.map(children, (child, index) => React.cloneElement(child, {
                        checked: isCheckedItem(child.props.id, index, isChecked),
                        className: child.props.className,
                        id: child.props.id,
                        index,
                        label: child.props.label,
                        name: !multi ? name : null,
                        onClick: this.onClick,
                        onKeyDown: this.onKeyDown,
                        style: child.props.style,
                        tabIndex: child.props.tabIndex,
                    }))}
                </div>
            );
        }

        return (
            <div
                aria-checked={isChecked}
                aria-labelledby={id}
                className={rootClasses}
                onClick={this.onClick}
                onKeyDown={this.onKeyDown}
                onMouseDown={this.onMouseDown}
                role="radio"
                style={style}
                tabIndex={isDisabled ? -1 : tabIndex}
            >
                <input
                    checked={isChecked}
                    className={ClassNames(
                        'input',
                        classes.input,
                    )}
                    disabled={isDisabled}
                    id={id}
                    name={name}
                    readOnly
                    type="radio"
                    value={value}
                />

                {/* eslint-disable-next-line jsx-a11y/label-has-associated-control */}
                <label
                    className={ClassNames(
                        'label',
                        classes.label,
                        {
                            [classes.labelNotClickable]: !labelClick,
                            'label-not-clickable': labelClick === false,
                        },
                    )}
                >
                    {label && (
                        // eslint-disable-next-line jsx-a11y/no-static-element-interactions
                        <span
                            onClick={this.onLabelClick}
                            onKeyDown={this.onLabelKeyDown}
                        >
                            {label}
                        </span>
                    )}
                </label>
            </div>
        );
    }
}

Radio.Item = RadioItem;

Radio.propTypes = propTypes;
Radio.defaultProps = defaultProps;

export default withStyles(styles)(Radio);
