import { memo, useLayoutEffect, useRef } from 'react';

import type { SelectOption, SelectOptionsProps } from './types';

import useIsLargeScreen from '~/src/hooks/useIsLargeScreen';
import { useI18nDynamic } from '~/src/lib/i18n';
import Box from '../Box';
import Clickable from '../Clickable';
import { useAppToast } from '../NextApp/lib/CoreUi';
import { NotificationType } from '../Notification';
import Scroller2, { useScroller } from '../Scroller2';
import Text from '../Text';
import { BACKGROUND_COLOR } from './constants';

const SelectOptions = <T extends SelectOption>({
  query,
  close,
  testId = 'select',
  options,
  inputRef,
  onChange,
  renderOption,
  optionHeight: baseOptionHeight,
  searchFeature,
  selectedOption,
  visibleOptionsCount,
}: SelectOptionsProps<T>) => {
  const trimmedQuery = query.trim();

  const { t } = useI18nDynamic('select');
  const isLargeScreen = useIsLargeScreen();
  const appToast = useAppToast();

  const optionHeight = isLargeScreen
    ? baseOptionHeight
    : `calc(${baseOptionHeight} + .8rem)`;

  // show extra half of the option height to identify options are scrollable
  const VISIBLE_OPTIONS_HEIGHT = `calc(${optionHeight} * ${visibleOptionsCount} + ${optionHeight} / 2)`;
  const CONTENT_HEIGHT = isLargeScreen ? VISIBLE_OPTIONS_HEIGHT : '100%';

  const Content = () => {
    const scroller = useScroller();
    const firstRenderRef = useRef(true);

    const isCreateOptionVisible =
      searchFeature?.type === 'create' &&
      trimmedQuery &&
      options.every(({ text }) => text !== trimmedQuery);

    useLayoutEffect(() => {
      if (firstRenderRef.current) {
        firstRenderRef.current = false;
        return;
      }

      // Reset scroll only if options scrolled
      if (scroller?.getScrollTop()) {
        // scroll to the top when options gets updated
        // it fix stucking in the middle of the list during search
        scroller?.setScrollTop(0);

        // setTimeout ensures focus is called after the render pass
        setTimeout(() => {
          inputRef.current?.focus();
        });
      }
    }, [trimmedQuery]);

    return (
      <Box flexColumn minHeight={searchFeature ? CONTENT_HEIGHT : '100%'}>
        {isCreateOptionVisible && (
          <Clickable
            testId={`${testId}Create`}
            noFlexShrink
            padding={isLargeScreen ? '1.6rem 1rem' : '2rem'}
            alignCenter
            onClick={() => {
              searchFeature
                .action({ query: trimmedQuery, close })
                .then((id) => {
                  // NOTE: Set new value after create action
                  onChange(id);
                })
                .catch((error: Error) => {
                  appToast({
                    type: NotificationType.ERROR,
                    text: error.message,
                  });
                });
            }}
          >
            <Text
              isBold
              color="#fff"
              size={isLargeScreen ? '1.4rem' : '1.7rem'}
            >
              {searchFeature.text?.(trimmedQuery) ??
                t('addOption', { name: trimmedQuery })}
            </Text>
          </Clickable>
        )}
        {options.length ? (
          <Box
            flexColumn
            tag="ul"
            // PERF: use single click handler on parent element reduce
            // number of event listeners for large lists
            onClick={(e) => {
              if (!(e.target instanceof HTMLElement)) {
                return;
              }

              const el = e.target.closest('[data-id]');

              if (el instanceof HTMLElement && el?.dataset.id) {
                onChange(el.dataset.id);
                close();
              }
            }}
          >
            {options.map((option) => {
              const isSelected = option.id === selectedOption?.id;

              return (
                renderOption?.({
                  testId: `${testId}Option`,
                  height: optionHeight,
                  isSelected,
                  isLargeScreen,
                  close,
                  onChange,
                  ...option,
                }) ?? (
                  <Box
                    tag="li"
                    positionRelative
                    testId={`${testId}Option`}
                    data-id={option.id}
                    key={option.id}
                    height={optionHeight}
                    padding={`0 ${isLargeScreen ? '1rem' : '1.6rem'}`}
                    flexRow
                    alignCenter
                    style={{
                      cursor: 'pointer',
                      backgroundColor: isSelected ? '#303030' : '#1a1a1a',
                    }}
                  >
                    {option.Icon && (
                      <option.Icon
                        noFlexShrink
                        color="#fff"
                        margin="0 1rem 0 0"
                      />
                    )}
                    <Box
                      flexColumn
                      style={{ overflow: 'hidden', gap: '.4rem' }}
                    >
                      <Text
                        color="#fff"
                        size={isLargeScreen ? '1.4rem' : '1.7rem'}
                        isBold={isSelected}
                        withEllipsis
                      >
                        {option.text}
                      </Text>
                      {option.caption && (
                        <Text
                          color="#999"
                          size={isLargeScreen ? '1.1rem' : '1.3rem'}
                          withEllipsis
                        >
                          {option.caption}
                        </Text>
                      )}
                    </Box>
                  </Box>
                )
              );
            })}
          </Box>
        ) : (
          <Box
            flexColumn
            centerContent
            padding={isLargeScreen ? '2rem' : '8rem 2rem'}
            style={{ flexGrow: 1 }}
          >
            <Text
              color="#999"
              size={isLargeScreen ? '1.2rem' : '1.5rem'}
              centered
            >
              {searchFeature ? t('noResults') : t('noOptions')}
            </Text>
          </Box>
        )}
      </Box>
    );
  };

  return (
    <Scroller2
      testId={`${testId || 'select'}Scroller`}
      className="selectOptions"
      flexGrow
      fullHeight={false}
      maxHeight={CONTENT_HEIGHT}
      style={{ overflow: 'hidden' }}
      withScrollBar
      withOverflowGradients
      gradientColor={BACKGROUND_COLOR}
      // scroll to the selected option after mount
      onMount={async ({ scrollerEl }) => {
        if (selectedOption?.id) {
          scrollerEl
            .querySelector(`[data-id="${selectedOption.id}"]`)
            ?.scrollIntoView({ behavior: 'instant' });
        }
      }}
      contentStyle={{
        display: 'flex',
        flexDirection: 'column',
        minHeight: searchFeature ? CONTENT_HEIGHT : '100%',
      }}
      onScrollStart={() => {
        // NOTE: unfocus input hides keyboard on mobile devices
        inputRef.current?.blur();
      }}
    >
      <Content />
      <style jsx>{`
        :global(.selectOptions li:hover),
        :global(.selectOptions button:hover) {
          background-color: #252525 !important;
        }
      `}</style>
    </Scroller2>
  );
};

export default memo(SelectOptions);
