import { useCallback, useEffect, useMemo, useRef, useState } from 'react';

import Button from '@design-system/Buttons/Button';
import Menu from '@design-system/Menu';
import Tabs from '@design-system/Tabs';
import { IconButton, Stack, useTheme } from '@mui/material';
import { composeSx } from '@utils/components';

import {
  CONTENT_HEIGHT,
  getTabsForMode,
  MAX_WIDTH,
  MAX_WIDTH_EMOJI_ONLY,
} from './constants';
import EmojiPickerOption from './options/Emoji';
import GifPicker from './options/Gif';
import {
  type IconSelectorMode,
  type IconSelectorProps,
  type IconSelectorSlotProps,
  IconSelectorTab,
  type IconSelectorTabProps,
} from './types';
import { getScrollableAncestors } from './utils';

const DEFAULT_MENU_SLOT: NonNullable<IconSelectorSlotProps['menu']> = {
  position: 'center',
};

const TRIGGER_BASE_SX = {
  height: 'fit-content',
  width: 'fit-content',
  margin: 'auto',
} as const;

const TabsComponent = ({
  tabs,
  value,
  onTabChange,
}: {
  tabs: IconSelectorTabProps[];
  value: IconSelectorTab;
  onTabChange: (value: IconSelectorTab) => void;
}) => {
  return (
    <Tabs
      tabs={tabs}
      value={value}
      sx={{
        pt: 2,
        px: 2,
        width: 'fit-content',
      }}
      onTabChange={(_value, index) => onTabChange(tabs[index].value)}
    />
  );
};

const TabContent = ({
  tab,
  onEmojiSelect,
  onGifSelect,
  onClose,
  closeOnEmojiSelect,
  textSlot,
  showTitle,
}: {
  tab: IconSelectorTab;
  onEmojiSelect?: IconSelectorProps['onEmojiSelect'];
  onGifSelect?: IconSelectorProps['onGifSelect'];
  onClose?: () => void;
  closeOnEmojiSelect?: boolean;
  textSlot?: IconSelectorSlotProps['text'];
  showTitle?: boolean;
}) => {
  if (tab === IconSelectorTab.EMOJI) {
    return (
      <EmojiPickerOption
        onEmojiSelect={onEmojiSelect}
        onClose={onClose}
        closeOnEmojiSelect={closeOnEmojiSelect}
      />
    );
  }

  if (tab === IconSelectorTab.PERSONALIZED) {
    return (
      <Stack
        sx={{
          height: CONTENT_HEIGHT,
          bgcolor: theme => theme.palette.new.background.elements.grey,
          alignItems: 'center',
          justifyContent: 'center',
          color: theme => theme.palette.new.text.neutral.lighter,
        }}
      >
        Custom icons (coming soon)
      </Stack>
    );
  }

  if (tab === IconSelectorTab.GIF) {
    return (
      <GifPicker
        title={showTitle ? textSlot?.gifTitle : undefined}
        noResultsMessage={textSlot?.gifNoResultsMessage}
        searchPlaceholder={textSlot?.gifSearchPlaceholder}
        onGifSelect={onGifSelect}
        onClose={onClose}
      />
    );
  }

  return null;
};

export const IconSelector = ({
  mode = 'all',
  slotProps = {},
  icon,
  text,
  anchorEl: externalAnchorEl,
  onEmojiSelect,
  onGifSelect,
  closeOnEmojiSelect = true,
  closeOnEnter = true,
  closeOnScroll = false,
  open,
  onOpenChange,
}: IconSelectorProps) => {
  const menuProps = { ...DEFAULT_MENU_SLOT, ...slotProps.menu };
  const buttonProps = slotProps.button;
  const iconButtonProps = slotProps.iconButton;
  const theme = useTheme();
  const anchorRef = useRef<HTMLButtonElement | null>(null);
  const isControlled = open !== undefined;
  const [uncontrolledOpen, setUncontrolledOpen] = useState(false);
  const pickerOpen = isControlled ? open : uncontrolledOpen;
  const hasExternalAnchor = externalAnchorEl != null;
  const resolvedAnchorEl = hasExternalAnchor
    ? externalAnchorEl
    : anchorRef.current;

  const tabs = useMemo(() => getTabsForMode(mode), [mode]);
  const [tab, setTab] = useState(tabs[0]?.value);

  const setOpen = useCallback(
    (next: boolean) => {
      if (next === pickerOpen) return;
      if (!isControlled) setUncontrolledOpen(next);
      onOpenChange?.(next);
    },
    [isControlled, onOpenChange, pickerOpen],
  );

  const onTogglePicker = useCallback(
    () => setOpen(!pickerOpen),
    [pickerOpen, setOpen],
  );
  const onCloseMenu = useCallback(() => {
    setOpen(false);
    setTab(prev => tabs[0]?.value ?? prev);
  }, [tabs, setOpen]);

  const onCloseMenuRef = useRef(onCloseMenu);
  onCloseMenuRef.current = onCloseMenu;

  useEffect(() => {
    if (!pickerOpen || !closeOnEnter) return;
    const handleKeyDown = (e: KeyboardEvent) => {
      if (e.key === 'Enter') onCloseMenuRef.current();
    };
    document.addEventListener('keydown', handleKeyDown);
    return () => document.removeEventListener('keydown', handleKeyDown);
  }, [pickerOpen, closeOnEnter]);

  useEffect(() => {
    if (!pickerOpen || !closeOnScroll) return;
    const handleScroll = () => onCloseMenuRef.current();
    const targets = getScrollableAncestors(resolvedAnchorEl);
    for (const target of targets) {
      target.addEventListener('scroll', handleScroll, { passive: true });
    }
    return () => {
      for (const target of targets) {
        target.removeEventListener('scroll', handleScroll);
      }
    };
  }, [pickerOpen, closeOnScroll, resolvedAnchorEl]);

  const maxWidth = useMemo(() => {
    return tabs.length <= 1 && tab === IconSelectorTab.EMOJI
      ? MAX_WIDTH_EMOJI_ONLY
      : MAX_WIDTH;
  }, [tabs, tab]);

  const hideTabs = useMemo(() => tabs.length <= 1, [tabs]);

  const isIconOnlyTrigger = icon != null && text == null;
  const triggerCommonProps = {
    ref: anchorRef,
    onClick: onTogglePicker,
  };

  const trigger = isIconOnlyTrigger ? (
    <IconButton
      {...iconButtonProps}
      {...triggerCommonProps}
      sx={composeSx(TRIGGER_BASE_SX, iconButtonProps?.sx)}
    >
      {icon}
    </IconButton>
  ) : (
    <Button
      {...buttonProps}
      {...triggerCommonProps}
      startIcon={icon}
      sx={composeSx(TRIGGER_BASE_SX, buttonProps?.sx)}
    >
      {text ?? ''}
    </Button>
  );

  return (
    <>
      {!hasExternalAnchor && trigger}
      <Menu
        open={pickerOpen}
        anchorEl={resolvedAnchorEl}
        onClose={onCloseMenu}
        fixedDimensions={false}
        {...menuProps}
      >
        <Stack
          sx={{
            width: maxWidth,
            bgcolor: theme.palette.new.background.layout.tertiary,
            border: 1,
            borderColor: theme.palette.new.border.neutral.default,
            borderRadius: 2,
            overflow: 'hidden',
          }}
        >
          {!hideTabs && (
            <TabsComponent
              tabs={tabs}
              value={tab}
              onTabChange={setTab}
            />
          )}
          <Stack
            sx={{
              height: CONTENT_HEIGHT,
              overflow: 'hidden',
            }}
          >
            <TabContent
              tab={tab}
              onEmojiSelect={onEmojiSelect}
              onGifSelect={onGifSelect}
              onClose={onCloseMenu}
              closeOnEmojiSelect={closeOnEmojiSelect}
              textSlot={slotProps.text}
              showTitle={hideTabs}
            />
          </Stack>
        </Stack>
      </Menu>
    </>
  );
};

export type {
  IconSelectorMode,
  IconSelectorProps,
  IconSelectorSlotProps,
  IconSelectorTabProps,
};
export { IconSelectorTab };

export { getTabsForMode } from './constants';
export {
  GifPickerProvider,
  useGifPicker,
} from './options/Gif/GifPickerContext';

export default IconSelector;
