import type { FC } from '../../lib/teact/teact';
import React, {
  memo, useEffect, useMemo, useRef, useState,
} from '../../lib/teact/teact';
import { getActions, getGlobal } from '../../global';

import type { ApiAvailableReaction, ApiReaction, ApiSticker } from '../../api/types';
import type { ObserveFn } from '../../hooks/useIntersectionObserver';
import type { StickerSetOrReactionsSetOrRecent } from '../../types';

import {
  DEFAULT_STATUS_ICON_ID,
  DEFAULT_TOPIC_ICON_STICKER_ID,
  EMOJI_SIZE_PICKER,
  FAVORITE_SYMBOL_SET_ID,
  POPULAR_SYMBOL_SET_ID,
  RECENT_SYMBOL_SET_ID,
  STICKER_SIZE_PICKER,
} from '../../config';
import { getReactionKey } from '../../global/helpers';
import { selectIsAlwaysHighPriorityEmoji, selectIsSetPremium } from '../../global/selectors';
import buildClassName from '../../util/buildClassName';

import useAppLayout from '../../hooks/useAppLayout';
import useFlag from '../../hooks/useFlag';
import { useIsIntersecting } from '../../hooks/useIntersectionObserver';
import useLang from '../../hooks/useLang';
import useLastCallback from '../../hooks/useLastCallback';
import useMediaTransition from '../../hooks/useMediaTransition';
import useResizeObserver from '../../hooks/useResizeObserver';
import useWindowSize from '../../hooks/window/useWindowSize';

import Button from '../ui/Button';
import ConfirmDialog from '../ui/ConfirmDialog';
import ReactionEmoji from './ReactionEmoji';
import StickerButton from './StickerButton';

import grey from '../../assets/icons/forumTopic/grey.svg';

type OwnProps = {
  stickerSet: StickerSetOrReactionsSetOrRecent;
  loadAndPlay: boolean;
  index: number;
  idPrefix: string;
  isNearActive: boolean;
  favoriteStickers?: ApiSticker[];
  isSavedMessages?: boolean;
  isStatusPicker?: boolean;
  isReactionPicker?: boolean;
  isCurrentUserPremium?: boolean;
  shouldHideHeader?: boolean;
  selectedReactionIds?: string[];
  withDefaultTopicIcon?: boolean;
  withDefaultStatusIcon?: boolean;
  isChatEmojiSet?: boolean;
  isChatStickerSet?: boolean;
  isTranslucent?: boolean;
  noContextMenus?: boolean;
  forcePlayback?: boolean;
  observeIntersection?: ObserveFn;
  observeIntersectionForPlayingItems: ObserveFn;
  observeIntersectionForShowingItems: ObserveFn;
  availableReactions?: ApiAvailableReaction[];
  onStickerSelect?: (sticker: ApiSticker, isSilent?: boolean, shouldSchedule?: boolean) => void;
  onReactionSelect?: (reaction: ApiReaction) => void;
  onStickerUnfave?: (sticker: ApiSticker) => void;
  onStickerFave?: (sticker: ApiSticker) => void;
  onStickerRemoveRecent?: (sticker: ApiSticker) => void;
  onContextMenuOpen?: NoneToVoidFunction;
  onContextMenuClose?: NoneToVoidFunction;
  onContextMenuClick?: NoneToVoidFunction;
};

const ITEMS_PER_ROW_FALLBACK = 8;
const ITEMS_MOBILE_PER_ROW_FALLBACK = 7;
const ITEMS_MINI_MOBILE_PER_ROW_FALLBACK = 6;
const MOBILE_WIDTH_THRESHOLD_PX = 440;
const MINI_MOBILE_WIDTH_THRESHOLD_PX = 362;

const StickerSet: FC<OwnProps> = ({
  stickerSet,
  loadAndPlay,
  index,
  idPrefix,
  isNearActive,
  favoriteStickers,
  availableReactions,
  isSavedMessages,
  isStatusPicker,
  isReactionPicker,
  isCurrentUserPremium,
  shouldHideHeader,
  withDefaultTopicIcon,
  selectedReactionIds,
  withDefaultStatusIcon,
  isChatEmojiSet,
  isChatStickerSet,
  isTranslucent,
  noContextMenus,
  forcePlayback,
  observeIntersection,
  observeIntersectionForPlayingItems,
  observeIntersectionForShowingItems,
  onReactionSelect,
  onStickerSelect,
  onStickerUnfave,
  onStickerFave,
  onStickerRemoveRecent,
  onContextMenuOpen,
  onContextMenuClose,
  onContextMenuClick,
}) => {
  const {
    clearRecentStickers,
    clearRecentCustomEmoji,
    clearRecentReactions,
    openPremiumModal,
    toggleStickerSet,
    loadStickers,
  } = getActions();

  // eslint-disable-next-line no-null/no-null
  const ref = useRef<HTMLDivElement>(null);

  // eslint-disable-next-line no-null/no-null
  const sharedCanvasRef = useRef<HTMLCanvasElement>(null);
  // eslint-disable-next-line no-null/no-null
  const sharedCanvasHqRef = useRef<HTMLCanvasElement>(null);

  const lang = useLang();
  const { width: windowWidth } = useWindowSize();
  const [isConfirmModalOpen, openConfirmModal, closeConfirmModal] = useFlag();
  const { isMobile } = useAppLayout();

  const [itemsPerRow, setItemsPerRow] = useState(getItemsPerRowFallback(windowWidth));

  const isIntersecting = useIsIntersecting(ref, observeIntersection ?? observeIntersectionForShowingItems);
  const transitionClassNames = useMediaTransition(isIntersecting);

  // `isNearActive` is set in advance during animation, but it is not reliable for short sets
  const shouldRender = isNearActive || isIntersecting;

  const stickerMarginPx = isMobile ? 8 : 4;
  const emojiMarginPx = isMobile ? 8 : 10;
  const emojiVerticalMarginPx = isMobile ? 8 : 4;
  const isRecent = stickerSet.id === RECENT_SYMBOL_SET_ID;
  const isFavorite = stickerSet.id === FAVORITE_SYMBOL_SET_ID;
  const isPopular = stickerSet.id === POPULAR_SYMBOL_SET_ID;
  const isEmoji = stickerSet.isEmoji;
  const isPremiumSet = !isRecent && selectIsSetPremium(stickerSet);

  const handleClearRecent = useLastCallback(() => {
    if (isReactionPicker) {
      clearRecentReactions();
    } else if (isEmoji) {
      clearRecentCustomEmoji();
    } else {
      clearRecentStickers();
    }
    closeConfirmModal();
  });

  const handleAddClick = useLastCallback(() => {
    if (isPremiumSet && !isCurrentUserPremium) {
      openPremiumModal({
        initialSection: 'animated_emoji',
      });
    } else {
      toggleStickerSet({
        stickerSetId: stickerSet.id,
      });
    }
  });

  const handleDefaultTopicIconClick = useLastCallback(() => {
    onStickerSelect?.({
      id: DEFAULT_TOPIC_ICON_STICKER_ID,
      isLottie: false,
      isVideo: false,
      stickerSetInfo: {
        shortName: 'dummy',
      },
    } satisfies ApiSticker);
  });

  const handleDefaultStatusIconClick = useLastCallback(() => {
    onStickerSelect?.({
      id: DEFAULT_STATUS_ICON_ID,
      isLottie: false,
      isVideo: false,
      stickerSetInfo: {
        shortName: 'dummy',
      },
    } satisfies ApiSticker);
  });

  const itemSize = isEmoji ? EMOJI_SIZE_PICKER : STICKER_SIZE_PICKER;
  const margin = isEmoji ? emojiMarginPx : stickerMarginPx;
  const verticalMargin = isEmoji ? emojiVerticalMarginPx : stickerMarginPx;

  const calculateItemsPerRow = useLastCallback((width: number) => {
    if (!width) {
      return getItemsPerRowFallback(windowWidth);
    }

    return Math.floor((width + margin) / (itemSize + margin));
  });

  const handleResize = useLastCallback((entry: ResizeObserverEntry) => {
    setItemsPerRow(calculateItemsPerRow(entry.contentRect.width));
  });

  useResizeObserver(ref, handleResize);

  useEffect(() => {
    if (!ref.current) return;
    setItemsPerRow(calculateItemsPerRow(ref.current.clientWidth));
  }, [calculateItemsPerRow]);

  useEffect(() => {
    if (shouldRender && !stickerSet.stickers?.length && !stickerSet.reactions?.length && stickerSet.accessHash) {
      loadStickers({
        stickerSetInfo: {
          id: stickerSet.id,
          accessHash: stickerSet.accessHash,
        },
      });
    }
  }, [shouldRender, loadStickers, stickerSet]);

  const isLocked = !isSavedMessages && !isCurrentUserPremium && isPremiumSet && !isChatEmojiSet;

  const isInstalled = stickerSet.installedDate && !stickerSet.isArchived;
  const canCut = !isInstalled && stickerSet.id !== RECENT_SYMBOL_SET_ID && stickerSet.id !== POPULAR_SYMBOL_SET_ID
    && !isChatEmojiSet && !isChatStickerSet;
  const [isCut, , expand] = useFlag(canCut);
  const itemsBeforeCutout = itemsPerRow * 3 - 1;
  const totalItemsCount = withDefaultTopicIcon ? stickerSet.count + 1 : stickerSet.count;

  const itemHeight = itemSize + verticalMargin;
  const heightWhenCut = Math.ceil(Math.min(itemsBeforeCutout, totalItemsCount) / itemsPerRow)
    * itemHeight - verticalMargin;
  const height = isCut ? heightWhenCut : Math.ceil(totalItemsCount / itemsPerRow) * itemHeight - verticalMargin;

  const favoriteStickerIdsSet = useMemo(() => (
    favoriteStickers ? new Set(favoriteStickers.map(({ id }) => id)) : undefined
  ), [favoriteStickers]);
  const withAddSetButton = !shouldHideHeader && !isRecent && isEmoji && !isPopular && !isChatEmojiSet
    && (!isInstalled || (!isCurrentUserPremium && !isSavedMessages));
  const addSetButtonText = useMemo(() => {
    if (isLocked) {
      if (isInstalled) return lang('lng_emoji_premium_restore');
      return lang('Unlock');
    }

    return lang('Add');
  }, [isLocked, lang, isInstalled]);

  return (
    <div
      ref={ref}
      key={stickerSet.id}
      id={`${idPrefix}-${index}`}
      className={
        buildClassName('symbol-set', isLocked && 'symbol-set-locked')
      }
    >
      {!shouldHideHeader && (
        <div className="symbol-set-header">
          <p className={buildClassName('symbol-set-title', withAddSetButton && 'symbol-set-title-external')}>
            {isLocked && <i className="symbol-set-locked-icon icon icon-lock-badge" />}
            <span className="symbol-set-name">{stickerSet.title}</span>
            {(isChatEmojiSet || isChatStickerSet) && (
              <span className="symbol-set-chat">{lang(isChatEmojiSet ? 'GroupEmoji' : 'GroupStickers')}</span>
            )}
            {withAddSetButton && Boolean(stickerSet.stickers) && (
              <span className="symbol-set-amount">
                {lang(isEmoji ? 'EmojiCount' : 'Stickers', stickerSet.stickers.length, 'i')}
              </span>
            )}
          </p>
          {isRecent && (
            <i className="symbol-set-remove icon icon-close" onClick={openConfirmModal} />
          )}
          {withAddSetButton && (
            <Button
              className="symbol-set-add-button"
              withPremiumGradient={isPremiumSet && !isCurrentUserPremium}
              onClick={handleAddClick}
              pill
              size="tiny"
              fluid
            >
              {addSetButtonText}
            </Button>
          )}
        </div>
      )}
      <div
        className={buildClassName('symbol-set-container shared-canvas-container', transitionClassNames)}
        style={`height: ${height}px;`}
      >
        <canvas
          ref={sharedCanvasRef}
          className="shared-canvas"
          style={canCut ? `height: ${heightWhenCut}px;` : undefined}
        />
        {(isRecent || isFavorite || canCut) && <canvas ref={sharedCanvasHqRef} className="shared-canvas" />}
        {withDefaultTopicIcon && (
          <Button
            className="StickerButton custom-emoji"
            color="translucent"
            onClick={handleDefaultTopicIconClick}
            key="default-topic-icon"
          >
            <img src={grey} alt="Reset" className="sticker-media" draggable={false} />
          </Button>
        )}
        {withDefaultStatusIcon && (
          <Button
            className="StickerButton custom-emoji status-default"
            color="translucent"
            onClick={handleDefaultStatusIconClick}
            key="default-status-icon"
          >
            <i className="icon icon-premium" />
          </Button>
        )}
        {shouldRender && stickerSet.reactions?.map((reaction) => {
          const reactionId = getReactionKey(reaction);
          const isSelected = reactionId ? selectedReactionIds?.includes(reactionId) : undefined;

          return (
            <ReactionEmoji
              key={`${stickerSet.id}_${reactionId}`}
              reaction={reaction}
              isSelected={isSelected}
              loadAndPlay={loadAndPlay}
              availableReactions={availableReactions}
              observeIntersection={observeIntersectionForPlayingItems}
              onClick={onReactionSelect!}
              sharedCanvasRef={sharedCanvasRef}
              sharedCanvasHqRef={sharedCanvasHqRef}
              forcePlayback={forcePlayback}
            />
          );
        })}
        {shouldRender && stickerSet.stickers?.slice(0, isCut ? itemsBeforeCutout : stickerSet.stickers.length)
          .map((sticker, i) => {
            const isHqEmoji = (isRecent || isFavorite)
              && selectIsAlwaysHighPriorityEmoji(getGlobal(), sticker.stickerSetInfo);
            const canvasRef = (canCut && i >= itemsBeforeCutout) || isHqEmoji
              ? sharedCanvasHqRef
              : sharedCanvasRef;
            const reactionId = sticker.isCustomEmoji ? sticker.id : sticker.emoji;
            const isSelected = reactionId ? selectedReactionIds?.includes(reactionId) : undefined;

            return (
              <StickerButton
                key={sticker.id}
                sticker={sticker}
                size={itemSize}
                observeIntersection={observeIntersectionForPlayingItems}
                observeIntersectionForShowing={observeIntersectionForShowingItems}
                noPlay={!loadAndPlay}
                isSavedMessages={isSavedMessages}
                isStatusPicker={isStatusPicker}
                canViewSet
                noContextMenu={noContextMenus}
                isCurrentUserPremium={isCurrentUserPremium}
                shouldIgnorePremium={isChatEmojiSet}
                sharedCanvasRef={canvasRef}
                withTranslucentThumb={isTranslucent}
                onClick={onStickerSelect}
                clickArg={sticker}
                isSelected={isSelected}
                onUnfaveClick={isFavorite && favoriteStickerIdsSet?.has(sticker.id) ? onStickerUnfave : undefined}
                onFaveClick={!favoriteStickerIdsSet?.has(sticker.id) ? onStickerFave : undefined}
                onRemoveRecentClick={isRecent ? onStickerRemoveRecent : undefined}
                onContextMenuOpen={onContextMenuOpen}
                onContextMenuClose={onContextMenuClose}
                onContextMenuClick={onContextMenuClick}
                forcePlayback={forcePlayback}
              />
            );
          })}
        {isCut && totalItemsCount > itemsBeforeCutout && (
          <Button
            className="StickerButton custom-emoji set-expand"
            round
            color="translucent"
            onClick={expand}
            key="more"
          >
            +{totalItemsCount - itemsBeforeCutout}
          </Button>
        )}
      </div>

      {isRecent && (
        <ConfirmDialog
          text={lang(isReactionPicker ? 'ClearRecentReactionsAlertMessage' : 'ClearRecentStickersAlertMessage')}
          isOpen={isConfirmModalOpen}
          onClose={closeConfirmModal}
          confirmHandler={handleClearRecent}
          confirmIsDestructive
        />
      )}
    </div>
  );
};

export default memo(StickerSet);

function getItemsPerRowFallback(windowWidth: number): number {
  return windowWidth > MOBILE_WIDTH_THRESHOLD_PX
    ? ITEMS_PER_ROW_FALLBACK
    : (windowWidth < MINI_MOBILE_WIDTH_THRESHOLD_PX
      ? ITEMS_MINI_MOBILE_PER_ROW_FALLBACK
      : ITEMS_MOBILE_PER_ROW_FALLBACK);
}
