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

import type {
  ApiDisallowedGifts,
  ApiPeer,
  ApiPremiumGiftCodeOption,
  ApiStarGift,
  ApiStarGiftRegular,
  ApiStarsAmount,
} from '../../../api/types';
import type { TabState } from '../../../global/types';
import type { StarGiftCategory } from '../../../types';

import { STARS_CURRENCY_CODE } from '../../../config';
import { getUserFullName } from '../../../global/helpers';
import { getPeerTitle, isApiPeerChat, isApiPeerUser } from '../../../global/helpers/peers';
import { selectTabState } from '../../../global/selectors';
import { selectPeer, selectUserFullInfo } from '../../../global/selectors';
import buildClassName from '../../../util/buildClassName';
import { throttle } from '../../../util/schedulers';

import useCurrentOrPrev from '../../../hooks/useCurrentOrPrev';
import { useIntersectionObserver } from '../../../hooks/useIntersectionObserver';
import useLang from '../../../hooks/useLang';
import useLastCallback from '../../../hooks/useLastCallback';
import useOldLang from '../../../hooks/useOldLang';

import Avatar from '../../common/Avatar';
import InteractiveSparkles from '../../common/InteractiveSparkles';
import SafeLink from '../../common/SafeLink';
import Button from '../../ui/Button';
import Modal from '../../ui/Modal';
import Transition from '../../ui/Transition';
import BalanceBlock from '../stars/BalanceBlock';
import GiftSendingOptions from './GiftComposer';
import GiftItemPremium from './GiftItemPremium';
import GiftItemStar from './GiftItemStar';
import GiftModalResaleScreen from './GiftModalResaleScreen';
import GiftResaleFilters from './GiftResaleFilters';
import StarGiftCategoryList from './StarGiftCategoryList';

import styles from './GiftModal.module.scss';

export type OwnProps = {
  modal: TabState['giftModal'];
};

export type GiftOption = ApiPremiumGiftCodeOption | ApiStarGift;

type StateProps = {
  boostPerSentGift?: number;
  starGiftsById?: Record<string, ApiStarGiftRegular>;
  starGiftIdsByCategory?: Record<StarGiftCategory, string[]>;
  starBalance?: ApiStarsAmount;
  peer?: ApiPeer;
  isSelf?: boolean;
  disallowedGifts?: ApiDisallowedGifts;
  resaleGiftsCount?: number;
  areResaleGiftsLoading?: boolean;
  selectedResaleGift?: ApiStarGift;
  tabId: number;
};

const AVATAR_SIZE = 100;
const INTERSECTION_THROTTLE = 200;
const SCROLL_THROTTLE = 200;
const AVATAR_SPARKLES_CENTER_SHIFT = [0, -50] as const;

const runThrottledForScroll = throttle((cb) => cb(), SCROLL_THROTTLE, true);

const GiftModal: FC<OwnProps & StateProps> = ({
  modal,
  starGiftsById,
  starGiftIdsByCategory,
  starBalance,
  peer,
  isSelf,
  disallowedGifts,
  resaleGiftsCount,
  areResaleGiftsLoading,
  selectedResaleGift,
  tabId,
}) => {
  const {
    closeGiftModal, openGiftInfoModal, resetResaleGifts, loadResaleGifts, openGiftInMarket, closeResaleGiftsMarket,
  } = getActions();
  const dialogRef = useRef<HTMLDivElement>();
  const transitionRef = useRef<HTMLDivElement>();
  const giftHeaderRef = useRef<HTMLHeadingElement>();

  const scrollerRef = useRef<HTMLDivElement>();

  const isOpen = Boolean(modal);
  const renderingModal = useCurrentOrPrev(modal);

  const user = peer && isApiPeerUser(peer) ? peer : undefined;
  const chat = peer && isApiPeerChat(peer) ? peer : undefined;

  const [selectedGift, setSelectedGift] = useState<GiftOption | undefined>();
  const [shouldShowMainScreenHeader, setShouldShowMainScreenHeader] = useState(false);
  const [isMainScreenHeaderForStarGifts, setIsMainScreenHeaderForStarGifts] = useState(false);
  const [isGiftScreenHeaderForStarGifts, setIsGiftScreenHeaderForStarGifts] = useState(false);

  const [selectedCategory, setSelectedCategory] = useState<StarGiftCategory>('all');
  const triggerSparklesRef = useRef<(() => void) | undefined>();

  const areAllGiftsDisallowed = useMemo(() => {
    if (!disallowedGifts) {
      return undefined;
    }
    const {
      shouldDisallowPremiumGifts,
      ...disallowedGiftTypes
    } = disallowedGifts;
    return !isSelf && Object.values(disallowedGiftTypes).every(Boolean);
  }, [isSelf, disallowedGifts]);

  const areUnlimitedStarGiftsDisallowed = !isSelf && disallowedGifts?.shouldDisallowUnlimitedStarGifts;
  const areLimitedStarGiftsDisallowed = !isSelf && disallowedGifts?.shouldDisallowLimitedStarGifts;

  const oldLang = useOldLang();
  const lang = useLang();
  const allGifts = renderingModal?.gifts;
  const filteredGifts = useMemo(() => {
    return allGifts?.sort((prevGift, gift) => prevGift.months - gift.months)
      .filter((gift) => gift.users === 1 && gift.currency !== STARS_CURRENCY_CODE);
  }, [allGifts]);

  const giftsByStars = useMemo(() => {
    const mapGifts = new Map();

    if (!filteredGifts) return mapGifts;

    filteredGifts.forEach((gift) => {
      const giftByStars = allGifts?.find(
        (starsGift) => starsGift.currency === STARS_CURRENCY_CODE
          && starsGift.months === gift.months,
      );
      if (giftByStars) {
        mapGifts.set(gift, giftByStars);
      }
    });

    return mapGifts;
  }, [allGifts, filteredGifts]);

  const baseGift = useMemo(() => {
    return filteredGifts?.reduce((prev, gift) => (prev.amount < gift.amount ? prev : gift));
  }, [filteredGifts]);

  const {
    observe: observeIntersection,
  } = useIntersectionObserver({ rootRef: scrollerRef, throttleMs: INTERSECTION_THROTTLE, isDisabled: !isOpen });

  const isResaleScreen = Boolean(selectedResaleGift) && !selectedGift;
  const isGiftScreen = Boolean(selectedGift);
  const shouldShowHeader = isResaleScreen || isGiftScreen || shouldShowMainScreenHeader;
  const isHeaderForStarGifts = isGiftScreen ? isGiftScreenHeaderForStarGifts : isMainScreenHeaderForStarGifts;

  useEffect(() => {
    if (selectedResaleGift) {
      const giftId = 'regularGiftId' in selectedResaleGift
        ? selectedResaleGift.regularGiftId
        : selectedResaleGift.id;
      loadResaleGifts({ giftId });
    }
  }, [selectedResaleGift]);

  useEffect(() => {
    if (!isOpen) {
      setShouldShowMainScreenHeader(false);
      setSelectedGift(undefined);
      setSelectedCategory('all');
    }
  }, [isOpen, tabId, closeResaleGiftsMarket]);

  const handleScroll = useLastCallback((e: React.UIEvent<HTMLDivElement>) => {
    if (isGiftScreen) return;
    const currentTarget = e.currentTarget;

    runThrottledForScroll(() => {
      const { scrollTop } = currentTarget;

      setShouldShowMainScreenHeader(scrollTop > 150);

      if (transitionRef.current && giftHeaderRef.current) {
        const { top: headerTop } = giftHeaderRef.current.getBoundingClientRect();
        const { top: transitionTop } = transitionRef.current.getBoundingClientRect();
        setIsMainScreenHeaderForStarGifts(headerTop - transitionTop <= 0);
      }
    });
  });

  const giftPremiumDescription = lang('GiftPremiumDescription', {
    user: getUserFullName(user)!,
    link: (
      <SafeLink
        text={lang('GiftPremiumDescriptionLinkCaption')}
        url={lang('GiftPremiumDescriptionLink')}
      />
    ),
  }, { withNodes: true });

  const starGiftDescription = chat
    ? lang('StarGiftDescriptionChannel', { peer: getPeerTitle(lang, chat) }, {
      withNodes: true,
      withMarkdown: true,
    })
    : isSelf
      ? lang('StarGiftDescriptionSelf', undefined, {
        withNodes: true,
        renderTextFilters: ['br'],
      })
      : lang('StarGiftDescription', {
        user: getUserFullName(user)!,
      }, { withNodes: true, withMarkdown: true });

  function renderGiftPremiumHeader() {
    return (
      <h2 className={buildClassName(styles.headerText, styles.center)}>
        {lang('GiftPremiumHeader')}
      </h2>
    );
  }

  function renderGiftPremiumDescription() {
    return (
      <p className={buildClassName(styles.description, styles.center)}>
        {giftPremiumDescription}
      </p>
    );
  }

  function renderStarGiftsHeader() {
    return (
      <h2 ref={giftHeaderRef} className={buildClassName(styles.headerText, styles.center)}>
        {lang(isSelf ? 'StarsGiftHeaderSelf' : 'StarsGiftHeader')}
      </h2>
    );
  }

  function renderStarGiftsDescription() {
    return (
      <p className={buildClassName(styles.description, styles.starGiftsDescription, styles.center)}>
        {starGiftDescription}
      </p>
    );
  }

  const handleGiftClick = useLastCallback((gift: GiftOption, target?: 'resell' | 'original') => {
    if (target === 'resell') {
      if (!('id' in gift)) {
        return;
      }
      if (isResaleScreen) {
        openGiftInfoModal({ gift, recipientId: renderingModal?.forPeerId });
        return;
      }
      openGiftInMarket({ gift, tabId });
      return;
    }
    setSelectedGift(gift);
    setIsGiftScreenHeaderForStarGifts('id' in gift);
  });

  function renderStarGifts() {
    const filteredGiftIds = starGiftIdsByCategory?.[selectedCategory]?.filter((giftId) => {
      const gift = starGiftsById?.[giftId];
      if (!gift) return false;

      const { isLimited, isSoldOut, upgradeStars } = gift;
      if (areUnlimitedStarGiftsDisallowed && !areLimitedStarGiftsDisallowed) {
        return isLimited;
      }
      if (areLimitedStarGiftsDisallowed && !areUnlimitedStarGiftsDisallowed) {
        return !isLimited && !isSoldOut;
      }
      if (areUnlimitedStarGiftsDisallowed && areLimitedStarGiftsDisallowed) {
        return Boolean(isLimited && Boolean(upgradeStars));
      }

      return true;
    });

    return (
      <div className={styles.starGiftsContainer}>
        {starGiftsById && filteredGiftIds?.flatMap((giftId) => {
          const gift = starGiftsById[giftId];
          const shouldShowResale = selectedCategory !== 'stock' && Boolean(gift.availabilityResale);
          const shouldDuplicateAsResale = selectedCategory !== 'resale' && shouldShowResale && !gift.isSoldOut;

          const elements = [
            <GiftItemStar
              key={giftId}
              gift={gift}
              observeIntersection={observeIntersection}
              isResale={shouldShowResale && !shouldDuplicateAsResale}
              onClick={handleGiftClick}
            />,
          ];

          if (shouldDuplicateAsResale) {
            elements.push(
              <GiftItemStar
                key={`resale_${giftId}`}
                isResale
                gift={gift}
                observeIntersection={observeIntersection}
                onClick={handleGiftClick}
              />,
            );
          }

          return elements;
        })}
      </div>
    );
  }

  function renderPremiumGifts() {
    return (
      <div className={styles.premiumGiftsGallery}>
        {filteredGifts?.map((gift) => {
          return (
            <GiftItemPremium
              option={gift}
              optionByStars={giftsByStars.get(gift)}
              baseMonthAmount={baseGift ? Math.floor(baseGift.amount / baseGift.months) : undefined}
              onClick={handleGiftClick}
            />
          );
        })}
      </div>
    );
  }

  const onCategoryChanged = useLastCallback((category: StarGiftCategory) => {
    setSelectedCategory(category);
  });

  const handleCloseModal = useLastCallback(() => {
    setSelectedGift(undefined);
    resetResaleGifts();
    closeGiftModal();
  });

  const handleCloseButtonClick = useLastCallback(() => {
    if (isResaleScreen) {
      closeResaleGiftsMarket({ tabId });
      return;
    }
    if (isGiftScreen) {
      setSelectedGift(undefined);
      return;
    }
    handleCloseModal();
  });

  const handleAvatarMouseMove = useLastCallback(() => {
    triggerSparklesRef.current?.();
  });

  const handleRequestAnimation = useLastCallback((animate: NoneToVoidFunction) => {
    triggerSparklesRef.current = animate;
  });

  function renderMainScreen() {
    return (
      <div ref={scrollerRef} className={buildClassName(styles.main, 'custom-scroll')} onScroll={handleScroll}>
        <div className={styles.avatars}>
          <Avatar
            className={styles.avatar}
            size={AVATAR_SIZE}
            peer={peer}
            onMouseMove={handleAvatarMouseMove}
          />
          <InteractiveSparkles
            className={styles.logoBackground}
            color="gold"
            centerShift={AVATAR_SPARKLES_CENTER_SHIFT}
            onRequestAnimation={handleRequestAnimation}
          />
        </div>
        {!isSelf && !chat && !disallowedGifts?.shouldDisallowPremiumGifts && (
          <>
            {renderGiftPremiumHeader()}
            {renderGiftPremiumDescription()}
            {renderPremiumGifts()}
          </>
        )}

        {!areAllGiftsDisallowed && (
          <>
            {renderStarGiftsHeader()}
            {renderStarGiftsDescription()}
            <StarGiftCategoryList
              areLimitedStarGiftsDisallowed={areLimitedStarGiftsDisallowed}
              onCategoryChanged={onCategoryChanged}
            />
            <Transition
              name="zoomFade"
              activeKey={getCategoryKey(selectedCategory)}
              className={styles.starGiftsTransition}
            >
              {renderStarGifts()}
            </Transition>
          </>
        )}
      </div>
    );
  }

  const isBackButton = isGiftScreen || isResaleScreen;

  const buttonClassName = buildClassName(
    'animated-close-icon',
    isBackButton && 'state-back',
  );

  function renderHeader() {
    if (!shouldShowHeader) return undefined;
    if (isResaleScreen) {
      const isFirstLoading = areResaleGiftsLoading && !resaleGiftsCount;
      return (
        <div className={styles.resaleHeaderContentContainer}>
          <h2 className={styles.resaleHeaderText}>
            {selectedResaleGift.title}
          </h2>
          {isFirstLoading
            && (
              <div className={styles.resaleHeaderDescription}>
                {lang('Loading')}
              </div>
            )}
          {!isFirstLoading && resaleGiftsCount !== undefined
            && (
              <div className={styles.resaleHeaderDescription}>
                {lang('HeaderDescriptionResaleGifts', {
                  count: resaleGiftsCount,
                }, { withNodes: true, withMarkdown: true, pluralValue: resaleGiftsCount })}
              </div>
            )}
          <GiftResaleFilters dialogRef={dialogRef} />
        </div>
      );
    }
    return (
      <h2 className={styles.commonHeaderText}>
        {lang(isHeaderForStarGifts ? (isSelf ? 'StarsGiftHeaderSelf' : 'StarsGiftHeader') : 'GiftPremiumHeader')}
      </h2>
    );
  }

  return (
    <Modal
      dialogRef={dialogRef}
      onClose={handleCloseModal}
      isOpen={isOpen}
      isSlim
      contentClassName={styles.content}
      className={buildClassName(styles.modalDialog, styles.root)}
      isLowStackPriority
    >
      <Button
        className={styles.closeButton}
        round
        color="translucent"
        size="smaller"
        onClick={handleCloseButtonClick}
        ariaLabel={isBackButton ? oldLang('Common.Back') : oldLang('Common.Close')}
      >
        <div className={buttonClassName} />
      </Button>
      <BalanceBlock className={styles.balance} balance={starBalance} withAddButton />
      <div className={buildClassName(
        styles.header,
        isResaleScreen && styles.resaleHeader,
        !shouldShowHeader && styles.hiddenHeader)}
      >
        <Transition
          name="slideVerticalFade"
          activeKey={!shouldShowHeader ? 0 : isResaleScreen ? 1 : isHeaderForStarGifts ? 2 : 3}
          slideClassName={styles.headerSlide}
        >
          {renderHeader()}
        </Transition>
      </div>
      <Transition
        ref={transitionRef}
        className={styles.transition}
        name="pushSlide"
        activeKey={isGiftScreen ? 1 : isResaleScreen ? 2 : 0}
      >
        {!isGiftScreen && !isResaleScreen && renderMainScreen()}
        {isResaleScreen && selectedResaleGift
          && (
            <GiftModalResaleScreen
              onGiftClick={handleGiftClick}
            />
          )}
        {isGiftScreen && renderingModal?.forPeerId && (
          <GiftSendingOptions
            gift={selectedGift}
            giftByStars={giftsByStars.get(selectedGift)}
            peerId={renderingModal.forPeerId}
          />
        )}
      </Transition>
    </Modal>
  );
};

export default memo(withGlobal<OwnProps>((global, { modal }): Complete<StateProps> => {
  const {
    starGifts,
    stars,
    currentUserId,
  } = global;

  const peer = modal?.forPeerId ? selectPeer(global, modal.forPeerId) : undefined;
  const isSelf = Boolean(currentUserId && modal?.forPeerId === currentUserId);
  const userFullInfo = peer ? selectUserFullInfo(global, peer?.id) : undefined;

  const { resaleGifts } = selectTabState(global);
  const resaleGiftsCount = resaleGifts.count;
  const areResaleGiftsLoading = resaleGifts.isLoading !== false;
  const selectedResaleGift = modal?.selectedResaleGift;

  return {
    boostPerSentGift: global.appConfig.boostsPerSentGift,
    starGiftsById: starGifts?.byId,
    starGiftIdsByCategory: starGifts?.idsByCategory,
    starBalance: stars?.balance,
    peer,
    isSelf,
    disallowedGifts: userFullInfo?.disallowedGifts,
    resaleGiftsCount,
    areResaleGiftsLoading,
    selectedResaleGift,
    tabId: selectTabState(global).id,
  };
})(GiftModal));

function getCategoryKey(category: StarGiftCategory) {
  if (category === 'all') return 0;
  if (category === 'limited') return 1;
  if (category === 'resale') return 2;
  if (category === 'stock') return 3;
  return category + 3;
}
