import { forwardRef, memo, useCallback, useMemo, useState } from 'react';
import clsx from 'clsx';

import Grid from '@mui/material/Grid';
import Typography from '@mui/material/Typography';

import type { DropDownMenuProps } from '../dropdown-menu';
import { Select } from '../@forms/select';
import { TextWithTooltip } from '../text-with-tooltip';

import createClasses from './styles';
import type { ClientImageProps, ClientImageMenuItemProps } from './types';
import { useEffect } from 'react';

// TODO: This could be written much cleaner..
// Should combine update functions to one, and have a single state

const ClientImageMenuItemContent = (
  props: ClientImageMenuItemProps['content'] & {
    labelId?: string;
    classes: Partial<ReturnType<typeof createClasses>>;
  }
) => {
  const { src, title, subtitle, labelId, classes } = props;

  return (
    <Grid container wrap="nowrap">
      {src && (
        <img
          loading="lazy"
          width="16"
          height="16"
          src={src}
          alt=""
          style={{ marginTop: 3, marginRight: 8 }}
        />
      )}
      <Grid id={labelId} item zeroMinWidth>
        {typeof title === 'string' && (
          <TextWithTooltip
            numOfLines={1}
            text={title}
            classes={{ text: clsx(['itemTitle', classes?.itemTitle]) }}
          />
        )}
        <Typography variant="caption" className={classes.itemSubtitle}>
          {subtitle}
        </Typography>
      </Grid>
    </Grid>
  );
};

const ClientImage = forwardRef((props: ClientImageProps, ref) => {
  const { width, parentChildrenSpace, controlled = false, ...otherProps } = props;
  const { classes: menuContentClasses, listItems: menuContentListItems } = props?.menuContentProps;
  const styles = createClasses({ width });
  const defaultItemValue = useMemo(
    () => menuContentListItems.find(item => item.default)?.value,
    [menuContentListItems]
  );
  const [selectValue, setSelectValue] = useState(
    props.value || props.defaultValue || defaultItemValue || ''
  );

  const defaultListItemValue = useMemo(
    () =>
      menuContentListItems?.map(({ content, ...itemProps }) => {
        const labelId = `lid-${itemProps.name}`;
        return {
          hidden: itemProps.default,
          selected: controlled && props.value === itemProps.value,
          textNode: (
            <ClientImageMenuItemContent
              labelId={labelId}
              src={content?.src}
              title={content?.title}
              subtitle={content?.subtitle}
              classes={styles}
            />
          ),
          ariaLabelledby: labelId,
          ...itemProps
        };
      }),
    [controlled, menuContentListItems, props.value, styles]
  );

  const [listItemsDisplay, setListItemsDisplay] = useState(defaultListItemValue);
  const [secondaryListItems, setSecondaryListItems] = useState(defaultListItemValue);

  const handleSelectChange: NonNullable<DropDownMenuProps['handleClick']> =
    (name, value) => event => {
      const selected = selectValue === name ? selectValue : name.toString();
      setSelectValue(selected);

      const newListItems = listItemsDisplay.map((item, index) => ({
        ...item,
        selected: controlled ? item.value === props.value : item.name === selected,
        hidden: index === 0 && selected === listItemsDisplay[0].name ? true : undefined
      }));
      setListItemsDisplay(newListItems);
      setSecondaryListItems(newListItems);
      props.menuContentProps?.handleClick?.(name, value)(event);
    };

  useEffect(() => {
    const updateListItems = (prev: typeof listItemsDisplay) =>
      !props.value
        ? defaultListItemValue
        : prev.map((item, index) => ({
            ...item,
            selected: props.value === item.value,
            hidden: index === 0 && props.value === prev[0].value
          }));

    if (controlled) {
      setListItemsDisplay(prev => updateListItems(prev));
      setSecondaryListItems(prev => updateListItems(prev));
    }
  }, [controlled, defaultListItemValue, props.value]);

  const handleTextChange = useCallback(
    e => {
      const value = (e?.target ? e.target.value : e).toString();
      const lowerValue = value.toLowerCase();
      const newListItems = listItemsDisplay.map(item => {
        const contentProps = (item.textNode as { props: ClientImageMenuItemProps['content'] })
          ?.props;
        const keywords = [item.name, contentProps?.title, contentProps?.subtitle];
        const isMatch = keywords.join(' ').toLowerCase().includes(lowerValue);

        return {
          ...item,
          hidden: !isMatch || item.default
        };
      });
      if (value !== '') {
        setListItemsDisplay(newListItems);
      } else {
        setListItemsDisplay(secondaryListItems);
      }
    },
    [listItemsDisplay, secondaryListItems]
  );

  const getValue = () => {
    if (controlled) return props.value || defaultItemValue;
    else return selectValue;
  };

  return (
    <Select
      ref={ref}
      {...otherProps}
      classes={{
        ...props.classes,
        inputBaseRoot: clsx([
          styles.inputBaseRoot,
          props.classes?.inputBaseRoot,
          selectValue !== defaultItemValue && parentChildrenSpace && styles.subClientPadding
        ]),
        select: styles.select,
        selectMenu: styles.selectMenu
      }}
      value={getValue()}
      menuContentProps={{
        search: true,
        searchPlaceholder: 'Search',
        ...props.menuContentProps,
        listItems: listItemsDisplay,
        handleClick: handleSelectChange,
        handleTextChange,
        classes: {
          ...menuContentClasses,
          menuItem: clsx([styles.menuItem, menuContentClasses?.menuItem]),
          menuList: clsx([styles.menuList, menuContentClasses?.menuList]),
          menuListSubheader: clsx([styles.menuListSubheader, menuContentClasses?.menuListSubheader])
        },
        defaultListItems: secondaryListItems
      }}
    />
  );
});

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