import React, { Fragment, memo, useCallback, useMemo } from 'react';
import type { KeyboardEvent } from 'react';
import { useLocation } from 'react-router-dom';

import clsx from 'clsx';
import join from 'lodash/join';
import { useToggle } from 'react-use';

import Collapse from '@mui/material/Collapse';
import Grid from '@mui/material/Grid';
import MuiListItem from '@mui/material/ListItem';
import ListItemIcon from '@mui/material/ListItemIcon';
import ListItemText from '@mui/material/ListItemText';
import Typography from '@mui/material/Typography';
import ExpandMoreRoundedIcon from '@mui/icons-material/ExpandMoreRounded';
import ChevronRightRoundedIcon from '@mui/icons-material/ChevronRightRounded';

import { Link } from '../../@navigation/link';
import type { NavItem as Item } from '../../@navigation/breadcrumbs';
import { trimSpaces } from '../../../utils';
import type { NestedListItemProps, SidebarItemProps } from './types';

const BaseSidebarItem = (props: SidebarItemProps) => {
  const {
    Icon: IconComponent,
    items,
    name,
    rootPath,
    classes,
    styles,
    badgeText,
    sidebarState
  } = props;

  const [open, toggleOpen] = useToggle(false);

  const location = useLocation();
  const currentPath = location.pathname;

  const parentCheck = useMemo(() => {
    let isParent = false;
    if (currentPath === rootPath) {
      return true;
    }
    items?.forEach(item => {
      if (item.path && (item.path === currentPath || currentPath.includes(item.path))) {
        isParent = true;
      } else if (item.subItems?.length) {
        item.subItems.forEach(subItem => {
          if (
            subItem.path &&
            (subItem.path === currentPath || currentPath.includes(subItem.path))
          ) {
            isParent = true;
          }
        });
      }
    });
    return isParent;
  }, [items, currentPath, rootPath]);

  const childCheck = useCallback(
    (path, subItems) => {
      let isChild = false;
      if (currentPath === path || (path && currentPath.includes(path))) {
        return true;
      } else if (subItems?.length) {
        subItems.forEach((subItem: Item) => {
          if (
            subItem.path &&
            (subItem.path === currentPath || currentPath.includes(subItem.path))
          ) {
            isChild = true;
          }
        });
      }
      return isChild;
    },
    [currentPath]
  );

  // For accessibility
  const handleKeyDown = useCallback(
    (e: KeyboardEvent<HTMLLIElement>) => {
      switch (e.code) {
        case 'Space':
        case 'Enter':
          e.preventDefault();
          toggleOpen();
          break;
        default:
      }
    },
    [toggleOpen]
  );

  return items?.length && !rootPath ? (
    <Fragment key={name}>
      <MuiListItem
        component="li"
        data-testid={`menu-${trimSpaces(name)}`}
        tabIndex={0}
        className={clsx(
          styles.listItem,
          classes?.listItem,
          (parentCheck || open) &&
            sidebarState !== 'closed' &&
            join([styles.listItemOpen, classes?.listItemOpen], ' ')
        )}
        onKeyDown={handleKeyDown}
        onClick={toggleOpen}
      >
        {IconComponent && (
          <ListItemIcon className={clsx(styles.listItemIcon, parentCheck && styles.iconSelected)}>
            <IconComponent color="inherit" fontSize="inherit" />
          </ListItemIcon>
        )}
        <ListItemText id="listItemText" className={styles.itemWrap} primary={name} />
        {badgeText && (
          <Typography
            id="new"
            component="span"
            className={styles.new}
            data-testid={`badge-${trimSpaces(name)}`}
          >
            {badgeText}
          </Typography>
        )}
        {open ? (
          <ExpandMoreRoundedIcon
            className={clsx(parentCheck && styles.iconSelected)}
            id="listItemIcon"
            fontSize="small"
          />
        ) : (
          <ChevronRightRoundedIcon
            className={clsx(parentCheck && styles.iconSelected)}
            id="listItemIcon"
            fontSize="small"
          />
        )}
      </MuiListItem>

      <Collapse component="li" in={open} className={clsx(styles.padding0)}>
        <Grid component="ul" className={clsx(styles.marginVertical4)}>
          {items?.map(item => {
            const { name: innerName, path = '', subItems, badgeText: itemBadgeText } = item;
            return subItems?.length ? (
              <NestedListItem
                innerName={innerName}
                subItems={subItems}
                styles={styles}
                classes={classes}
                path={path}
                itemBadgeText={itemBadgeText}
                currentPath={currentPath}
                childCheck={childCheck}
              />
            ) : (
              <MuiListItem
                id="innerListItem"
                key={innerName}
                className={styles.subListItemContainer}
              >
                <Link
                  classes={{
                    root: clsx(
                      styles.subListItem,
                      classes?.subListItem,
                      childCheck(path, subItems) &&
                        join([styles.subListItemSelected, classes?.subListItemSelected], ' ')
                    )
                  }}
                  href={path}
                  data-testid={`subMenu-${trimSpaces(innerName)}`}
                >
                  <ListItemText
                    primary={innerName}
                    primaryTypographyProps={{
                      variant: 'subtitle1'
                    }}
                  />
                  {itemBadgeText && (
                    <Typography
                      id="new"
                      component="span"
                      className={styles.new}
                      data-testid={`badge-${trimSpaces(innerName)}`}
                    >
                      {itemBadgeText}
                    </Typography>
                  )}
                </Link>
              </MuiListItem>
            );
          })}
        </Grid>
      </Collapse>
    </Fragment>
  ) : (
    <MuiListItem
      key={name}
      component="li"
      classes={{
        gutters: styles.padding0,
        root: clsx(
          styles.listItem,
          classes?.listItem,
          rootPath === currentPath &&
            join([styles.listItemSelected, classes?.listItemSelected], ' ')
        )
      }}
    >
      <Link
        href={rootPath || ''}
        classes={{
          root: styles.linkRoot
        }}
        data-testid={`menu-${trimSpaces(name)}`}
      >
        {IconComponent && (
          <ListItemIcon className={styles.listItemIcon}>
            <IconComponent fontSize="inherit" />
          </ListItemIcon>
        )}
        <ListItemText id="listItemText" className={styles.itemWrap} primary={name} />
        {badgeText && (
          <Typography
            id="new"
            component="span"
            className={styles.new}
            data-testid={`badge-${trimSpaces(name)}`}
          >
            {badgeText}
          </Typography>
        )}
      </Link>
    </MuiListItem>
  );
};

BaseSidebarItem.defaultProps = {
  items: [],
  rootPath: ''
};
export const SidebarItemBase = memo(BaseSidebarItem);

const NestedListItem = memo((props: NestedListItemProps) => {
  const {
    innerName,
    styles,
    classes,
    currentPath,
    itemBadgeText,
    path,
    subItems = [],
    childCheck
  } = props;

  const [open, toggleOpen] = useToggle(false);

  return (
    <Fragment key={innerName}>
      <MuiListItem
        data-testid={`subMenu-${trimSpaces(innerName)}`}
        component="li"
        className={styles.innerListItemDropDownContainer}
        id="innerListItemDropDownContainer"
        onKeyDown={toggleOpen}
        onClick={toggleOpen}
      >
        <Grid
          container
          tabIndex={0}
          alignItems="center"
          className={clsx(
            styles.subListItem,
            classes?.subListItem,
            childCheck(path, subItems) &&
              join([styles.innerListItemDropDownSelected, classes?.subListItemSelected], ' '),
            open && styles.innerListItemDropDownOpen
          )}
        >
          <ListItemText
            primaryTypographyProps={{
              variant: 'subtitle1'
            }}
            primary={innerName}
          />
          {itemBadgeText && (
            <Typography
              id="new"
              component="span"
              className={styles.new}
              data-testid={`badge-${trimSpaces(innerName)}`}
            >
              {itemBadgeText}
            </Typography>
          )}

          {open ? (
            <ExpandMoreRoundedIcon id="listItemIcon" fontSize="small" />
          ) : (
            <ChevronRightRoundedIcon id="listItemIcon" fontSize="small" />
          )}
        </Grid>
      </MuiListItem>
      <Collapse in={open} component="li">
        <Grid component="ul">
          {subItems.map(subItem => {
            const { name: subName, path: subPath, badgeText: subItemBadgeText } = subItem;
            return (
              <MuiListItem
                id="innerListItem"
                key={subName}
                className={styles.innerListItemContainer}
              >
                <Link
                  classes={{
                    root: clsx(
                      styles.subListItem,
                      classes?.subListItem,
                      styles.subSubListItem,
                      subPath === currentPath &&
                        join([styles.subListItemSelected, classes?.subListItemSelected], ' ')
                    )
                  }}
                  href={subPath || ''}
                  data-testid={`subSubMenu-${trimSpaces(subName)}`}
                >
                  <ListItemText
                    primary={subName}
                    primaryTypographyProps={{
                      variant: 'subtitle1'
                    }}
                  />
                  {subItemBadgeText && (
                    <Typography
                      id="new"
                      component="span"
                      className={styles.new}
                      data-testid={`badge-${trimSpaces(subName)}`}
                    >
                      {subItemBadgeText}
                    </Typography>
                  )}
                </Link>
              </MuiListItem>
            );
          })}
        </Grid>
      </Collapse>
    </Fragment>
  );
});
