import React, { isValidElement, memo, useCallback, useEffect, useMemo, useState } from 'react';
import type { ReactNode, Dispatch, SetStateAction } from 'react';

import ClickAwayListener from '@mui/base/ClickAwayListener';
import Drawer from '@mui/material/Drawer';
import List from '@mui/material/List';
import Box from '@mui/material/Box';

import { ASSETS_URL } from '../../consts/common';
import noop from 'lodash/noop';
import values from 'lodash/values';
import map from 'lodash/map';
import clsx from 'clsx';
import type { MainNavItem } from '../@navigation/breadcrumbs';
import { DrawerButton } from '../@navigation/drawer-button';
import type { DrawerButtonProps } from '../@navigation/drawer-button';
import { ClientImage } from '../client-image';
import type { ClientImageMenuItemProps } from '../client-image';
import type { DropDownMenuProps } from '../dropdown-menu';
import createClasses from './styles';
import type { ClassesProps } from './styles';
import { CustomIcon } from '../custom-icon';
import { SidebarItem } from './sidebarItems';
import type { DrawerStates } from './sidebarItems';

export interface SidebarProps extends ClassesProps {
  /**
   * Logo which will be displayed at the top of the component when open and when `closedLogo` ins't provided.
   */
  logo: ReactNode;
  /**
   * Logo which will be displayed at the top of the component when closed.
   */
  closedLogo?: ReactNode;
  /**
   * Menu items to display.
   */
  sidebarItemsMap: Record<string, MainNavItem>;
  /**
   * The open state of the drawer.
   * @default 'closed'
   */
  open?: DrawerStates;
  /**
   * Set the open state of the drawer.
   */
  setOpen?: Dispatch<SetStateAction<DrawerStates>>;
  /**
   * Whether to show sidebar button or not.
   */
  showDrawerButton?: boolean;
  /**
   * Control the drawer from layout
   */
  handleDrawerChange?: (event?: Event) => void;
  /**
   * Provide to override classes.
   */
  classes?: ReturnType<typeof createClasses>;
  /**
   * mspClientPicker will be displayed below logo and on top of sidebar elements when the tenant type is MSP.
   */
  mspClientPicker?: {
    isMSP?: boolean;
    props?: {
      createNewOrg?: () => void;
      onClientClick?: NonNullable<DropDownMenuProps['handleClick']>;
      bottomBtnText?: string;
      orgList?: ClientImageMenuItemProps[];
      noResultsText?: string;
      selectValue?: string;
      controlled?: boolean;
    };
  };
  /**
   * If true, the client is running inside CP's infinity portal and header and logo will not be visible.
   */
  hideHeader?: boolean;
}
const Sidebar = (props: SidebarProps) => {
  const {
    logo,
    closedLogo,
    sidebarItemsMap,
    drawerWidthClose,
    drawerWidthOpen,
    open = 'closed',
    setOpen,
    showDrawerButton = true,
    classes,
    mspClientPicker,
    handleDrawerChange,
    hideHeader
  } = props;

  const MINIMUM_SCREEN_WIDTH_FOR_LOCK = 1366;

  const styles = createClasses({ drawerWidthClose, drawerWidthOpen, open, hideHeader });
  const listItems = useMemo(() => values(sidebarItemsMap), [sidebarItemsMap]);

  const handleDrawerStateChange = useCallback(
    (currentState: DrawerStates): DrawerButtonProps['onClick'] =>
      e => {
        e.stopPropagation();
        switch (true) {
          case currentState === 'locked':
            setOpen?.('closed');
            break;
          case currentState === 'open' && window.innerWidth >= MINIMUM_SCREEN_WIDTH_FOR_LOCK:
            setOpen?.('locked');
            break;
          case currentState === 'open':
            setOpen?.('closed');
            break;
          case currentState === 'closed' && window.innerWidth <= MINIMUM_SCREEN_WIDTH_FOR_LOCK:
            setOpen?.('open');
            break;
          case currentState === 'closed':
            setOpen?.('locked');
            break;
        }
      },
    [setOpen]
  );

  const handleMouseEvents = useCallback(
    (state: 'open' | 'closed' | 'drawer'): React.MouseEventHandler<HTMLDivElement> =>
      () => {
        if (open === 'locked' && handleDrawerChange) {
          handleDrawerChange();
        }
        if (state === 'drawer') {
          setOpen?.(open);
        } else {
          if (open !== 'locked') {
            setOpen?.(state);
          }
        }
      },
    [handleDrawerChange, open, setOpen]
  );

  const handleClickAway = useCallback(() => {
    if (open === 'open') {
      return setOpen?.('closed');
    }
  }, [open, setOpen]);

  return (
    <ClickAwayListener onClickAway={handleClickAway}>
      <Box
        onMouseEnter={handleMouseEvents('open')}
        onMouseLeave={handleMouseEvents('closed')}
        position="relative"
      >
        <Drawer
          data-testid="drawer"
          variant="permanent"
          anchor="left"
          classes={{
            paper: clsx(
              styles.drawerRoot,
              classes?.drawerRoot,
              (open === 'locked' || open === 'open') && styles.drawerOpen,
              open === 'closed' && styles.drawerClose,
              mspClientPicker?.isMSP && styles.mspClientPicker
            ),
            docked: styles.docked
          }}
        >
          {showDrawerButton && (
            <Box
              onMouseEnter={handleMouseEvents('drawer')}
              onMouseLeave={handleMouseEvents('open')}
            >
              <DrawerButton
                open={open === 'open'}
                locked={open === 'locked'}
                side="left"
                onClick={handleDrawerStateChange(open)}
                title="Open / Close Navigation"
                hideHeader={hideHeader}
              />
            </Box>
          )}
          {isValidElement(logo) && !hideHeader && (
            <Box className={clsx(styles.logoContainer, classes?.logoContainer)}>
              {closedLogo && open === 'closed' ? closedLogo : logo}
            </Box>
          )}
          {mspClientPicker?.isMSP && (open === 'open' || open === 'locked') ? (
            <ClientImage
              placeholder={''}
              value={mspClientPicker.props?.selectValue}
              parentChildrenSpace={true}
              controlled={mspClientPicker.props?.controlled}
              menuContentProps={{
                ariaLabel: 'List of Organizations',
                bottomButtonText: mspClientPicker.props?.bottomBtnText,
                bottomButtonProps: mspClientPicker.props?.bottomBtnText
                  ? {
                      startIconUrl: `${ASSETS_URL}/icons/icon_plus.svg`,
                      onClick: () => {
                        mspClientPicker.props?.createNewOrg?.();
                        open !== 'locked' && setOpen?.('closed');
                      }
                    }
                  : undefined,
                searchPlaceholder: 'Search',
                noResultsText: mspClientPicker.props?.noResultsText,
                listItems: map(mspClientPicker.props?.orgList, item => {
                  return {
                    ...item,
                    value: item.value || item.name
                  };
                }),
                handleClick: mspClientPicker.props?.onClientClick || (() => noop)
              }}
            />
          ) : mspClientPicker?.isMSP && open === 'closed' ? (
            <Box className={styles.iconContainer}>
              <CustomIcon src={`${ASSETS_URL}/icons2/icon_plan.svg`} className={styles.mspIcon} />
            </Box>
          ) : null}
          <Box className={clsx(styles.listWrap, classes?.listWrap)}>
            <List className={styles.listNavigation}>
              {listItems.map(item => (
                <SidebarItem
                  {...item}
                  key={`item_${item.name}`}
                  sidebarState={open}
                  classes={classes}
                  styles={styles}
                />
              ))}
            </List>
          </Box>
        </Drawer>
      </Box>
    </ClickAwayListener>
  );
};

const m = memo(Sidebar);
export { m as Sidebar };
