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

import type { PaginatedDotsApi } from '../lib/PaginatedDots';
import type { PageSectionComponent } from '../types';
import type { VideosSectionProps } from './types';

import Box from '~/src/components/Box';
import Clickable from '~/src/components/Clickable';
import SoundOffIcon from '~/src/components/Icon/SoundOffIcon';
import SoundOnIcon from '~/src/components/Icon/SoundOnIcon';
import HorizontalScroller from '~/src/components/Scroller2/HorizontalScroller';
import YouTubeVideo, {
  canUnmuteIframeBeforeInteraction,
} from '~/src/components/YouTubeVideo';
import { useGlobalMediaState } from '~/src/components/YouTubeVideo/useGlobalMediaState';
import {
  blockViewportListenerCallbacks,
  unblockViewportListenerCallbacks,
} from '~/src/components/YouTubeVideo/viewportController';
import useIsLargeScreen from '~/src/hooks/useIsLargeScreen';
import { useI18n } from '~/src/lib/i18n';
import isTruthy from '~/src/lib/utils/isTruthy';
import { parseYoutubeVideoId } from '~/src/lib/youtube/utils';
import { useRegisterNavItem } from '../../components/ItemPageNav';
import PaginatedDots from '../lib/PaginatedDots';
import SectionTitle from '../lib/SectionTitle';

const SPACE_BETWEEN_ITEMS_REM = 1;

const VideosSection: PageSectionComponent<VideosSectionProps> = ({
  title,
  items,
  sectionId,
  sectionIndex,
  navTitle = 'Videos',
  autoplay,
}) => {
  const isLargeScreen = useIsLargeScreen();
  const paginatedDotsApiRef = useRef<PaginatedDotsApi>(null);
  const [showToggleMuteButton, setShowToggleMuteButton] = useState(!!autoplay);
  const scrollerRef = useRef<HTMLDivElement>(null);
  const [activeItemIndex, setActiveItemIndex] = useState<number | null>(0);
  const { t } = useI18n();

  const { globalVideoMuted, dispatchGlobalVideoMutedChange } =
    useGlobalMediaState();

  useEffect(() => {
    // COMPLEX: on android webview/in-app-browser we can't unmute the video
    // until the user has tapped the youtube iframe, if we attempt to the video
    // is paused. So we cannot show our mute button in this case. Potentially once
    // the user has engaged with the iframe we could show it, but it's a little late then.
    if (!canUnmuteIframeBeforeInteraction()) {
      setShowToggleMuteButton(false);
    }
  }, []);

  // Defining the default title here means we don't need to always use a preset.
  // It makes sense to use a preset if we want to define default content (eg. on artist page).
  // This means the component can be added, the layout customized and the props still feed
  // from the preset. Without the preset the props would have to be hard-coded into the custom
  // layout meaning the default videos will never update.
  if (title === undefined) {
    title = t('item.defaultVideosTitle');
  }

  const itemsFiltered = items
    ?.map((item) => {
      const youtubeVideoId = parseYoutubeVideoId(item.link);

      if (!youtubeVideoId) {
        return;
      }

      if (item.isEmbeddable === false) {
        return;
      }

      return {
        ...item,
        youtubeVideoId,
      };
    })
    .filter(isTruthy);

  // Calculate the width for each item based on the number of items
  const totalItems = itemsFiltered?.length || 0;
  const itemWidth = totalItems > 1 ? `${100 / totalItems}%` : '100%';
  const containerWidth = `${totalItems * 100}%`;

  useRegisterNavItem({
    id: sectionId,
    text: navTitle,
    index: sectionIndex,
    skip: !items?.length,
  });

  // don't render anything if there are no items
  if (!itemsFiltered?.length) {
    return null;
  }

  return (
    <div data-testid="videos" className="videos">
      {title && <SectionTitle padding="0 0 2.3rem" text={title} />}
      <HorizontalScroller
        overflowStyle={isLargeScreen ? 'outside' : undefined}
        gradientColor="mask"
        margin={`0 -${SPACE_BETWEEN_ITEMS_REM / 2}rem`}
        scrollerRef={scrollerRef}
        centerContent
        withSnapping
        rightMaskGradientWidth="2rem"
        onScroll={() => {
          blockViewportListenerCallbacks();
        }}
        onScrollEnd={({ x, maxX }) => {
          unblockViewportListenerCallbacks();

          const itemWidthPx = maxX / (totalItems - 1);
          const itemIndex = Math.round(x / itemWidthPx);

          paginatedDotsApiRef.current?.setIndex(itemIndex);
          setActiveItemIndex(itemIndex);
        }}
        arrowSize="2.8rem"
        contentStyle={{
          display: 'flex',
          flexDirection: 'row',
          justifyContent: 'flex-start',
          width: containerWidth,
        }}
        renderContent={({ snapItemStyle }) => {
          return itemsFiltered.map((item, index) => {
            return (
              <Box
                className="videoItem"
                testId="videoItem"
                key={item.link}
                style={{
                  ...snapItemStyle,
                  flex: '0 0 auto',
                  padding: `0 ${SPACE_BETWEEN_ITEMS_REM / 2}rem`,
                  width: itemWidth,
                  maxWidth: '100%',
                }}
              >
                <Box
                  positionRelative
                  style={{
                    border: 'solid 1px #333',
                    borderRadius: '1.6rem',
                    overflow: 'hidden',
                  }}
                >
                  <YouTubeVideo
                    withOfficialControls
                    loop
                    isVisible={activeItemIndex === index}
                    image={item.image}
                    title={item.title}
                    // Prioritise the preload of the first most visible video
                    // We currently preload ALL the videos in the section, this is because of the way muting and autoplay
                    // works. We do stagger the loading in batches to spread out the load. It would be ideal if we could
                    // preload only the first few videos and then load in the rest as they get closer to the visible viewport.
                    // This needs more investigation but I think that if the user has chosen to globally unmute videos then
                    // the lazy loaded videos would have to be played muted (ios only).
                    priorityPreload={index === 0}
                    // We don't always have the aspect ratio of the video, songwhip-lookup doesn't seem to
                    // return it anymore (I believe it used to?). The only way I know to get the aspect ratio
                    // is from the official youtube data api by inspecting the `player` object. We do have the
                    // aspect when videos are manually added to the section though. For now we're always using
                    // a 16:9 aspect player container and fitting the video inside. it would be ideal to be
                    // able to size the player container based on videos inside. If they're all portrait shorts
                    // then it would make sense to use a 16:9 aspect player, if they're a combination of aspects
                    // then we'd probably stick to 16:9. We could improve the 16:9 fit experience as when we hide
                    // controls the video can be sized wrong.
                    aspect={9 / 16}
                    containerAspect={9 / 16}
                    videoId={item.youtubeVideoId}
                    autoplay={autoplay}
                  />
                </Box>
              </Box>
            );
          });
        }}
      />
      <Box margin="1.8rem 0 0" positionRelative>
        {itemsFiltered.length > 1 && (
          <PaginatedDots
            apiRef={paginatedDotsApiRef}
            total={totalItems}
            onItemClick={(index) => {
              const videoItems =
                scrollerRef.current?.querySelectorAll('.videoItem');

              const videoItem = videoItems?.[index];
              const itemWidth = videoItem?.clientWidth;

              if (videoItem && itemWidth) {
                scrollerRef.current?.scrollTo({
                  left: itemWidth * index,
                  behavior: 'smooth',
                });
              }
            }}
          />
        )}
        {showToggleMuteButton && (
          <Clickable
            positionAbsolute
            right={0}
            top={0}
            bottom={0}
            centerContent
            height="1rem"
            padding="0 1.5rem"
            isInline
            withHoverOpacityFrom={0.7}
            onClick={() => {
              dispatchGlobalVideoMutedChange(!globalVideoMuted);
            }}
          >
            {globalVideoMuted ? (
              <SoundOffIcon size="1.9rem" />
            ) : (
              <SoundOnIcon size="1.9rem" />
            )}
          </Clickable>
        )}
      </Box>
    </div>
  );
};

export default VideosSection;
