import {
  Dispatch,
  MouseEvent,
  SetStateAction,
  useEffect,
  useRef,
  useState
} from 'react';

import { equals } from 'ramda';
import { useSetAtom, useAtomValue } from 'jotai';
import { makeStyles } from 'tss-react/mui';

import Collapse from '@mui/material/Collapse';
import List from '@mui/material/List';
import ListSubheader from '@mui/material/ListSubheader';

import { useMemoComponent } from '@centreon/ui';

import { Page } from '../../models';
import {
  selectedNavigationItemsAtom,
  hoveredNavigationItemsAtom,
  setHoveredNavigationItemsDerivedAtom
} from '../sideBarAtoms';

import MenuItems from './MenuItems';

interface Props {
  collapseMenu: () => void;
  collapseScrollMaxHeight?: number;
  collapseScrollMaxWidth?: number;
  currentTop?: number;
  currentWidth: number;
  data?: Array<Page>;
  isCollapsed: boolean;
  isSubHeader?: boolean;
  level: number;
  setCollapseScrollMaxHeight: Dispatch<SetStateAction<number | undefined>>;
  setCollapseScrollMaxWidth: Dispatch<SetStateAction<number | undefined>>;
}

interface StyleProps {
  collapseScrollMaxHeight?: number;
  collapseScrollMaxWidth?: number;
  currentTop?: number;
  currentWidth: number;
}

const collapseWidth = 24;

const useStyles = makeStyles<StyleProps>()(
  (
    theme,
    {
      currentWidth,
      collapseScrollMaxHeight,
      collapseScrollMaxWidth,
      currentTop
    }
  ) => ({
    label: {
      fontWeight: 'bold'
    },
    root: {
      '& .MuiListItemIcon-root': {
        color: theme.palette.text.primary,
        minWidth: theme.spacing(2.25),
        padding: theme.spacing(0, 0.25, 0, 0.1)
      },
      boxShadow: theme.shadows[3],
      outline: 'none'
    },
    subHeader: {
      backgroundColor: 'rgba(0,0,0,.05)',
      color: theme.palette.text.secondary,
      fontSize: theme.typography.caption.fontSize,
      fontWeight: 'bold',
      lineHeight: 1,
      padding: theme.spacing(1),
      textAlign: 'center'
    },
    toggled: {
      '&::-webkit-scrollbar': {
        width: theme.spacing(1)
      },
      '&::-webkit-scrollbar-corner': {
        backgroundColor: theme.palette.background.default
      },
      '&::-webkit-scrollbar-thumb': {
        backgroundColor: theme.palette.action.disabled
      },
      '&::-webkit-scrollbar-track': {
        border: `solid ${theme.palette.action.hover} 0.5px`
      },
      backgroundColor: theme.palette.background.default,
      left: theme.spacing(currentWidth),
      maxHeight: collapseScrollMaxHeight
        ? theme.spacing(collapseScrollMaxHeight)
        : theme.spacing(50),
      maxWidth: collapseScrollMaxWidth
        ? theme.spacing(collapseScrollMaxWidth)
        : theme.spacing(collapseWidth),
      overflow: 'auto',
      position: 'fixed',
      top: currentTop,
      whiteSpace: 'normal',
      width: theme.spacing(collapseWidth),
      zIndex: theme.zIndex.mobileStepper
    }
  })
);

const CollapsibleItems = ({
  data,
  isCollapsed,
  isSubHeader,
  collapseMenu,
  currentTop,
  currentWidth,
  level,
  collapseScrollMaxHeight,
  collapseScrollMaxWidth,
  setCollapseScrollMaxWidth,
  setCollapseScrollMaxHeight
}: Props): JSX.Element => {
  const { classes, cx } = useStyles({
    collapseScrollMaxHeight,
    collapseScrollMaxWidth,
    currentTop,
    currentWidth
  });
  const [hoveredIndex, setHoveredIndex] = useState<number | null>(null);
  const [itemTop, setItemTop] = useState<number>();
  const [nestedScrollCollapsMaxHeight, setNestedScrollCollapsMaxHeight] =
    useState<undefined | number>(undefined);
  const [nestedScrollCollapsMaxWidth, setNestedScrollCollapsMaxWidth] =
    useState<undefined | number>(undefined);
  const collapsRef = useRef<HTMLElement | null>(null);
  const hoveredNavigationItems = useAtomValue(hoveredNavigationItemsAtom);
  const selectedNavigationItems = useAtomValue(selectedNavigationItemsAtom);
  const setHoveredNavigationItems = useSetAtom(
    setHoveredNavigationItemsDerivedAtom
  );

  const levelName = `level_${level}`;
  const itemWidth = currentWidth + collapseWidth;
  const minimumMarginBottom = 4;

  const hoverItem = ({ e, index, currentPage }): void => {
    const rect = e.currentTarget.getBoundingClientRect();
    const { top } = rect;
    setItemTop(top);
    setHoveredIndex(index);
    setHoveredNavigationItems({ currentPage, levelName });
  };

  const isItemHovered = ({
    navigationItem,
    levelTitle,
    currentPage
  }): boolean => {
    if (navigationItem && navigationItem[levelTitle]) {
      return (
        navigationItem[levelTitle].label === currentPage.label &&
        navigationItem[levelTitle]?.url === currentPage?.url
      );
    }

    return false;
  };

  const getNestedIndex = ({ itemIndex, childIndex, content }): number => {
    if (itemIndex > 1) {
      return (
        Number(content[0].children?.length) +
        Number(content[itemIndex - 1].children?.length) +
        childIndex
      );
    }
    if (itemIndex === 1) {
      return childIndex + Number(content[0].children?.length);
    }

    return childIndex;
  };

  const isArrayItem = (item: unknown): boolean => {
    if (Array.isArray(item)) {
      return item.length > 0;
    }

    return false;
  };

  const updateCollapseSize = (el: HTMLElement): void => {
    const rect = el.getBoundingClientRect();
    setCollapseScrollMaxHeight(
      (window.innerHeight - rect.top) / 8 - minimumMarginBottom
    );
    setCollapseScrollMaxWidth((window.innerWidth - rect.left) / 8);
  };

  const isItemClicked = (): void => {
    collapseMenu();
  };

  useEffect(() => {
    if (isCollapsed && collapsRef && collapsRef.current) {
      updateCollapseSize(collapsRef.current);
    }
  }, [isCollapsed]);

  return useMemoComponent({
    Component: (
      <Collapse
        unmountOnExit
        className={cx(classes.root, classes.toggled)}
        data-cy="collapse"
        enter={false}
        exit={false}
        in={isCollapsed}
        ref={collapsRef}
        timeout={0}
      >
        {data?.map((item, index) => {
          const hover =
            isItemHovered({
              currentPage: item,
              levelTitle: levelName,
              navigationItem: selectedNavigationItems
            }) || equals(hoveredIndex, index);

          const mouseEnterItem = (e: MouseEvent<HTMLElement>): void =>
            hoverItem({ currentPage: item, e, index });

          const isCollapseWithSubheader =
            Array.isArray(item?.groups) &&
            item.groups.length > 1 &&
            equals(index, hoveredIndex);

          const isSimpleCollapse =
            isArrayItem(item?.groups) && equals(index, hoveredIndex);

          return (
            <List
              disablePadding
              key={item.label}
              subheader={
                isSubHeader && (
                  <ListSubheader
                    disableGutters
                    disableSticky
                    className={classes.subHeader}
                  >
                    {item.label}
                  </ListSubheader>
                )
              }
            >
              {isSubHeader ? (
                isArrayItem(item?.children) &&
                item?.children?.map((content, ind) => {
                  const nestedIndex = getNestedIndex({
                    childIndex: ind,
                    content: data,
                    itemIndex: index
                  });
                  const nestedHover =
                    isItemHovered({
                      currentPage: content,
                      levelTitle: levelName,
                      navigationItem: selectedNavigationItems
                    }) || equals(hoveredIndex, nestedIndex);

                  const mouseEnterContent = (
                    e: MouseEvent<HTMLElement>
                  ): void =>
                    hoverItem({ currentPage: content, e, index: nestedIndex });

                  return (
                    <MenuItems
                      data={content}
                      hover={nestedHover}
                      isItemClicked={isItemClicked}
                      isOpen={nestedIndex === hoveredIndex}
                      key={content.label}
                      onMouseEnter={mouseEnterContent}
                    />
                  );
                })
              ) : (
                <MenuItems
                  data={item}
                  hover={hover}
                  isItemClicked={isItemClicked}
                  isOpen={index === hoveredIndex}
                  onMouseEnter={mouseEnterItem}
                />
              )}

              {isCollapseWithSubheader ? (
                <CollapsibleItems
                  isSubHeader
                  collapseMenu={collapseMenu}
                  collapseScrollMaxHeight={nestedScrollCollapsMaxHeight}
                  collapseScrollMaxWidth={nestedScrollCollapsMaxWidth}
                  currentTop={itemTop}
                  currentWidth={itemWidth}
                  data={item.groups}
                  isCollapsed={index === hoveredIndex}
                  level={level + 1}
                  setCollapseScrollMaxHeight={setNestedScrollCollapsMaxHeight}
                  setCollapseScrollMaxWidth={setNestedScrollCollapsMaxWidth}
                />
              ) : (
                isSimpleCollapse &&
                item?.groups?.map(
                  (itemGroup) =>
                    isArrayItem(itemGroup?.children) && (
                      <div key={itemGroup.label}>
                        <CollapsibleItems
                          collapseMenu={collapseMenu}
                          collapseScrollMaxHeight={nestedScrollCollapsMaxHeight}
                          collapseScrollMaxWidth={nestedScrollCollapsMaxWidth}
                          currentTop={itemTop}
                          currentWidth={itemWidth}
                          data={itemGroup.children}
                          isCollapsed={index === hoveredIndex}
                          level={level + 1}
                          setCollapseScrollMaxHeight={
                            setNestedScrollCollapsMaxHeight
                          }
                          setCollapseScrollMaxWidth={
                            setNestedScrollCollapsMaxWidth
                          }
                        />
                      </div>
                    )
                )
              )}
            </List>
          );
        })}
      </Collapse>
    ),
    memoProps: [
      isCollapsed,
      collapseScrollMaxHeight,
      collapseScrollMaxWidth,
      nestedScrollCollapsMaxHeight,
      nestedScrollCollapsMaxWidth,
      hoveredIndex,
      selectedNavigationItems,
      hoveredNavigationItems
    ]
  });
};

export default CollapsibleItems;
