import React, {
  ComponentProps,
  CSSProperties,
  forwardRef,
  HTMLAttributes,
  useEffect,
  useLayoutEffect,
  useRef,
  useState,
} from 'react';
import { Autocomplete as MuiAutocomplete, autocompleteClasses, inputAdornmentClasses } from '@mui/material';

import { SPACING } from '@/mui/constants';

import { autoSuggestHighlight, omitUndefinedProps } from '../../helpers';

import Checkbox from '../Checkbox/Checkbox';
import ListItem from '../ListItem/ListItem';
import ListItemIcon from '../ListItemIcon/ListItemIcon';
import ListItemText from '../ListItemText/ListItemText';
import Stack from '../Stack/Stack';

const useIsomorphicLayoutEffect = typeof window !== 'undefined' ? useLayoutEffect : useEffect;

type Props = ComponentProps<typeof MuiAutocomplete> & {
  optionLabelKey?: string;
  optionValueKey?: string;
  autoSuggest?: boolean;
  checkboxes?: boolean;
};

type OptionType = {
  [key: string]: string;
};

const Autocomplete = forwardRef<HTMLElement, Props>((innerProps, ref) => {
  const {
    checkboxes = false,
    autoSuggest = true,
    optionLabelKey = 'label',
    optionValueKey = 'value',
    ...props
  } = innerProps;

  const autocompleteRef = useRef<HTMLElement>(null);
  const [adornmentWidth, setAdornmentWidth] = useState(0);

  const getOptionLabel = (option: unknown) => {
    if (props.getOptionLabel) {
      return props.getOptionLabel?.(option as string) || '';
    }

    const option$ = option as OptionType;

    if (typeof option$ === 'object') {
      return `${option$?.[optionLabelKey] || ''}`;
    }

    return `${option ?? ''}`;
  };

  const autocompleteOptions = {
    getLimitTagsText: (mode: number) => `+${mode}`,
    renderOption: (
      props$: HTMLAttributes<HTMLLIElement>,
      option: unknown,
      { inputValue, selected }: { inputValue: string; selected: boolean },
    ) => {
      const option$ = option as OptionType;

      const key = `${option$?.[optionLabelKey]}:${option$?.[optionValueKey]}:${option$}`;

      const label = getOptionLabel(option$);

      return (
        <ListItem {...props$} key={key}>
          <Stack direction="row" alignItems="center" spacing={0} fullWidth>
            {checkboxes && (
              <ListItemIcon>
                <Checkbox checked={selected} />
              </ListItemIcon>
            )}
            <ListItemText
              primary={
                autoSuggest
                  ? autoSuggestHighlight(inputValue, label as string).map((part) => {
                      const Tag = part.highlight ? 'b' : React.Fragment;

                      return <Tag key={part.id}>{part.text}</Tag>;
                    })
                  : label
              }
              slotProps={{
                primary: {
                  variant: 'body2',
                },
              }}
            />
          </Stack>
        </ListItem>
      );
    },
    getOptionLabel,
    isOptionEqualToValue: (sourceOption: unknown, option: unknown) => {
      if (Object.prototype.toString.call(sourceOption) === '[object Object]') {
        const sourceOption$ = sourceOption as OptionType;
        const option$ = option as OptionType;
        return (
          Object.prototype.hasOwnProperty.call(sourceOption, optionValueKey) &&
          sourceOption$[optionValueKey] === option$[optionValueKey]
        );
      }

      return sourceOption === option;
    },
    ...omitUndefinedProps(props),
  };

  useIsomorphicLayoutEffect(() => {
    const checkWidth = () => {
      const aWidth =
        (
          autocompleteRef.current?.querySelector(`.${inputAdornmentClasses.positionEnd}`) ||
          autocompleteRef.current?.querySelector(`.${autocompleteClasses.endAdornment}`)
        )?.clientWidth || 0;

      setAdornmentWidth(aWidth + SPACING * 2);
    };

    checkWidth();

    const timer = window.setInterval(() => checkWidth(), 1000);

    return () => {
      clearInterval(timer);
    };
  }, [autocompleteRef.current]);

  return (
    <MuiAutocomplete
      {...autocompleteOptions}
      options={props.options}
      renderInput={props.renderInput}
      style={
        {
          '--internal-adornment-width': `${adornmentWidth}px`,
        } as CSSProperties
      }
      ref={(node) => {
        if (typeof ref === 'function') {
          ref(node as HTMLElement);
        } else if (ref) {
          ref.current = node as HTMLElement;
        }
        autocompleteRef.current = node as HTMLElement;
      }}
    />
  );
});

Autocomplete.displayName = 'Autocomplete';

export default Autocomplete;
