import React, { useState } from 'react';
import LabelIcon from '@material-ui/icons/Label';
import { useMediaQuery } from '@material-ui/core';
import { makeStyles } from '@material-ui/core/styles';
import {
    MenuItemLink,
    getResources,
    useTranslate,
    DashboardMenuItem
} from 'react-admin';
import PropTypes from 'prop-types';
import { useSelector, shallowEqual } from 'react-redux';
import classnames from 'classnames';
import DefaultIcon from '@material-ui/icons/ViewList';
import CustomMenuItem from './CustomMenuItem';

const useStyles = makeStyles(
    theme => ({
        main: {
            display: 'flex',
            flexDirection: 'column',
            justifyContent: 'flex-start',
            marginTop: '0.5em',
            [theme.breakpoints.only('xs')]: {
                marginTop: 0,
            },
            [theme.breakpoints.up('md')]: {
                marginTop: '1.5em',
            },
            transition: theme.transitions.create("width", {
                easing: theme.transitions.easing.sharp,
                duration: theme.transitions.duration.leavingScreen,
            }),
        },
        open: {
            width: 200,
        },
        closed: {
            width: 55,
        },
    }),
    { name: 'RaTreeMenu' }
);

const Menu = (props) => {
    const {
        className,
        dense,
        hasDashboard,
        onMenuClick,
        logout,
        dashboardlabel,
        resources,
        ...rest
    } = props;

    const classes = useStyles(props);
    const translate = useTranslate();
    const open = useSelector((state) => state.admin.ui.sidebarOpen);
    const pathname = useSelector((state) => state.router.location.pathname);
    const resources = resources || useSelector(getResources, shallowEqual);
    const hasList = (resource) => (resource.hasList);

    const handleToggle = (parent) => {
        /**
         * Handles toggling of parents dropdowns
         * for resource visibility
         */
        setState(state => ({ [parent]: !state[parent] }));
    };

    const isXSmall = useMediaQuery((theme) =>
        /**
         * This function is not directly used anywhere
         * but is required to fix the following error:
         * 
         * Error: Rendered fewer hooks than expected.
         * This may be caused by an accidental early
         * return statement.
         * 
         * thrown by RA at the time of rendering.
         */
        theme.breakpoints.down('xs')
    );

    const isParent = (resource) => (
        /**
         * Check if the given resource is a parent
         * i.e. dummy resource for menu parenting
         */
        resource.options &&
        resource.options.hasOwnProperty('isMenuParent') &&
        resource.options.isMenuParent
    );

    const isOrphan = (resource) => (
        /**
         * Check if the given resource is an orphan
         * i.e. has no parents defined. Needed as
         * these resources are supposed to be rendered
         * as is
         *  
         */
        resource.options &&
        !resource.options.hasOwnProperty('menuParent') &&
        !resource.options.hasOwnProperty('isMenuParent')
    );

    const isChildOfParent = (resource, parentResource) => (
        /**
         * Returns true if the given resource is the
         * mapped child of the parentResource
         */
        resource.options &&
        resource.options.hasOwnProperty('menuParent') &&
        resource.options.menuParent == parentResource.name
    );
    const geResourceName = (slug) => {
        if (!slug) return;
        var words = slug.toString().split('_');
        for (var i = 0; i < words.length; i++) {
            var word = words[i];
            words[i] = word.charAt(0).toUpperCase() + word.slice(1);
        }
        return words.join(' ');
    }

    const getPrimaryTextForResource = (resource) => {
        let resourcename = '';
        if (resource.options && resource.options.label)
            resourcename = resource.options.label;
        else if (resource.name) {
            resourcename = translate(`resources.${resource.name}.name`);
            if (resourcename.startsWith('resources.'))
                resourcename = geResourceName(resource.name);
        }
        return resourcename;
    }

    const MenuItem = (resource) => (
        /**
         * Created and returns the MenuItemLink object component
         * for a given resource.
         */
        <MenuItemLink
            key={resource.name}
            to={`/${resource.name}`}
            primaryText={getPrimaryTextForResource(resource)}
            leftIcon={
                resource.icon
                    ? <resource.icon />
                    : <DefaultIcon />
            }
            onClick={onMenuClick}
            dense={dense}
            sidebarIsOpen={open}
        />
    );

    /**
     * Mapping a "parent" entry and then all its children to the "tree" layout
     */
    const mapParentStack = (parentResource) => (
        <CustomMenuItem
            key={parentResource.name}
            handleToggle={() => handleToggle(parentResource.name)}
            isOpen={state[parentResource.name] || parentActiveResName === parentResource.name}
            sidebarIsOpen={open}
            name={getPrimaryTextForResource(parentResource)}
            icon={parentResource.icon ? <parentResource.icon /> : <LabelIcon />}
            dense={dense}
        >
            {
                // eslint-disable-next-line
                resources
                    .filter((resource) => isChildOfParent(resource, parentResource) && hasList(resource))
                    .map((childResource) => { return MenuItem(childResource); })
            }
        </CustomMenuItem>
    );

    /**
     * Mapping independent (without a parent) entries
     */
    const mapIndependent = (independentResource) => hasList(independentResource) && MenuItem(independentResource);


    /**
     * Initialising the initialExpansionState and
     * active parent resource name at the time of
     * initial menu rendering.
     */
    const initialExpansionState = {};
    let parentActiveResName = null;

    /**
     * Initialise all parents to inactive first.
     * Also find the active resource name.
     */
    resources.forEach(resource => {
        if (isParent(resource)) {
            initialExpansionState[resource.name] = false;
        } else if (pathname.startsWith(`/${resource.name}`) && resource.options.hasOwnProperty('menuParent')) {
            parentActiveResName = resource.options.menuParent;
        }
    });

    const [state, setState] = useState(initialExpansionState);

    /**
     * The final array which will hold the array
     * of resources to be rendered
     */
    const resRenderGroup = [];

    /**
     * Looping over all resources and pushing the menu tree
     * for rendering in the order we find them declared in
     */
    resources.forEach(r => {
        if (isParent(r)) resRenderGroup.push(mapParentStack(r));
        if (isOrphan(r)) resRenderGroup.push(mapIndependent(r));
    });

    return (
        <div>
            <div
                className={classnames(classes.main, className, {
                    [classes.open]: open,
                    [classes.closed]: !open,
                })}
                {...rest}
            >
                {hasDashboard && (
                    <DashboardMenuItem
                        onClick={onMenuClick}
                        dense={dense}
                        sidebarIsOpen={open}
                        primaryText={dashboardlabel}
                    />
                )}
                {resRenderGroup}
            </div>
        </div>
    );
}

Menu.propTypes = {
    classes: PropTypes.object,
    className: PropTypes.string,
    dense: PropTypes.bool,
    hasDashboard: PropTypes.bool,
    logout: PropTypes.element,
    onMenuClick: PropTypes.func,
    dashboardlabel:PropTypes.string,
    resources:PropTypes.array,
};

Menu.defaultProps = {
    onMenuClick: () => null,
    dashboardlabel: 'Dashboard'
};


export default Menu;