'use client';

import { motion } from 'framer-motion';
import { observer } from 'mobx-react-lite';
import { usePathname } from 'next/navigation';
import { useRouter } from 'next/navigation';
import React, {
  forwardRef,
  useCallback,
  useEffect,
  useMemo,
  useRef,
  useState,
} from 'react';
import { useTranslation } from 'react-i18next';
import AutoSizer from 'react-virtualized-auto-sizer';
import { VariableSizeList as List } from 'react-window';
import { twMerge } from 'tailwind-merge';

import { useStores } from '@/app/(root)/AppProviders';
import ImpressionLogger, {
  ImpressionLoggerConfig,
} from '@/components/ImpressionLogger';
import Button, {
  ButtonShape,
  ButtonSize,
  ButtonVariant,
} from '@/components/button/Button';
import Link from '@/components/link/Link';
import { usePreviewContext } from '@/context/PreviewContext';
import {
  CaretRightIcon,
  ChevronLeftIcon,
  ChevronRightIcon,
  CreateIcon,
} from '@/icons';
import { ContextType } from '@/logging/contextTypes';
import { ClipsStore } from '@/state/clipStore';
import { TYPOGRAPHY_DISCOVER_SECTION_TITLE_CLASSNAME } from '@/utils/constants';
import { eventLogger } from '@/utils/event-logger';
import { EventNames } from '@/utils/event-names';

import Selector from '../../select/Selector';
import { CategoryCardItem } from '../carouselCards/CategoryCardItem';
import { PersonaCardItem } from '../carouselCards/PersonaCardItem';
import { PlaylistCardItem } from '../carouselCards/PlaylistCardItem';
import { SongCardItem } from '../carouselCards/SongCardWrapper';
import { UserCardItem } from '../carouselCards/UserCardItem';
import { RowSkeleton } from './SkeletonCarousel';

interface ContentCarouselProps {
  sectionTitle: string;
  elements: any[];
  sectionType: string;
  styleType?: string;
  carouselType?: string;
  sectionLink?: string;
  sectionId?: string;
  options?: string[];
  sectionName: string;
  selectedOption?: string;
  secondaryOptions?: string[];
  selectedSecondaryOption?: string;
  index: number;
}

const ContentTypeToElemWidget: {
  [key: string]: {
    widget: React.FC<any>;
    width: number;
    height: number;
    cardHeight: number;
    backgroundColor?: string;
  };
} = {
  playlist: { widget: SongCardItem, width: 177, height: 24, cardHeight: 18 },
  user_list: {
    widget: UserCardItem,
    width: 254,
    height: 20.5,
    cardHeight: 16.5,
  },
  playlist_list: {
    widget: PlaylistCardItem,
    width: 166,
    height: 17.5,
    cardHeight: 13,
  },
  style_list: {
    widget: CategoryCardItem,
    width: 230,
    height: 10,
    cardHeight: 10,
  },
  persona_list: {
    widget: PersonaCardItem,
    width: 230,
    height: 24,
    cardHeight: 16,
    backgroundColor: 'bg-background-primary',
  },
};
const CardContainer = forwardRef((props: any, ref: any) => (
  <section
    ref={ref}
    {...props}
    className='flex h-auto w-full overflow-x-auto scroll-smooth [&::-webkit-overflow-scrolling]:touch-auto [&::-webkit-scrollbar]:hidden'
  />
));

CardContainer.displayName = 'CardContainer';
const ContentCarousel: React.FC<ContentCarouselProps> = observer((props) => {
  const { t } = useTranslation();
  const {
    playbar,
    discover,
    session,
    clips: clipStore,
    createV2,
  } = useStores();
  const { setPreviewClip } = usePreviewContext();
  const containerRef = useRef<any>(null);
  const scrollRef = useRef<any>(null);
  const [showLeftPaddle, setShowLeftPaddle] = useState(false);
  const [showRightPaddle, setShowRightPaddle] = useState(false);
  const [contentElements, setContentElements] = useState(props.elements);
  const [fetchingElements, isFetchingElements] = useState(false);
  const [sectionLink, setSectionLink] = useState(props.sectionLink);
  const [sectionTitle, setSectionTitle] = useState(props.sectionTitle);
  const pathname = usePathname();
  const [selectedContentOption, setSelectedContentOption] = useState(
    props.options?.[0] ?? ''
  );
  const [selectedSecondaryOption, setSelectedSecondaryOption] = useState(
    props.secondaryOptions?.[0] ?? ''
  );
  const router = useRouter();

  const updatePaddles = useCallback(() => {
    if (scrollRef.current) {
      const canScroll =
        scrollRef.current.scrollWidth !== scrollRef.current.clientWidth;
      if (canScroll) {
        setShowLeftPaddle(scrollRef.current.scrollLeft > 0);
        const scrollMax =
          scrollRef.current.scrollWidth - scrollRef.current.clientWidth;
        setShowRightPaddle(scrollRef.current.scrollLeft < scrollMax - 1);
      } else {
        setShowLeftPaddle(false);
        setShowRightPaddle(false);
      }
    }
  }, []);

  useEffect(() => {
    const handleScroll = () => {
      if (scrollRef.current) {
        const scrollMax =
          scrollRef.current.scrollWidth - scrollRef.current.clientWidth;
        setShowLeftPaddle(scrollRef.current.scrollLeft > 0);
        setShowRightPaddle(scrollRef.current.scrollLeft < scrollMax);
      }
    };

    scrollRef.current?.addEventListener('scroll', handleScroll);
    window.addEventListener('resize', updatePaddles);
    return () => {
      scrollRef.current?.removeEventListener('scroll', handleScroll);
      window.removeEventListener('resize', updatePaddles);
    };
  }, [updatePaddles]);

  const leftMaskGradient = `mask-[linear-gradient(to_right,transparent,black_20%,black)] mask-size-[100%_100%]`;
  const rightMaskGradient = `mask-[linear-gradient(to_right,black,black_80%,transparent)] mask-size-[100%_100%]`;
  const bothMaskGradient = `mask-[linear-gradient(to_right,transparent,black_20%,black_80%,transparent)] mask-size-[100%_100%]`;
  const maskGradient =
    showLeftPaddle && showRightPaddle
      ? bothMaskGradient
      : showLeftPaddle
        ? leftMaskGradient
        : rightMaskGradient;

  const impressionLoggerConfigs = useMemo<ImpressionLoggerConfig[]>(() => {
    const baseEvent = {
      principalObjectType: props.sectionType,
      principalObjectValue: props.sectionName,
      context: {
        sectionLink: sectionLink,
        selectedContentOption: selectedContentOption,
        selectedSecondaryOption: selectedSecondaryOption,
        carouselIndex: props.index,
        carouselTitle: sectionTitle,
      },
    };
    return [
      {
        event: {
          ...baseEvent,
          actionName: 'CarouselSeen',
        },
        threshold: 0.95,
      },
      {
        event: {
          ...baseEvent,
          actionName: 'CarouselSeenPartially',
        },
        threshold: 0.25,
      },
    ];
  }, [
    sectionTitle,
    props.sectionName,
    props.sectionType,
    sectionLink,
    selectedContentOption,
    selectedSecondaryOption,
    props.index,
  ]);

  const listRef = useRef<List>(null);
  // for "playlist" sections, ie. carousels with songs, filter out hidden clips
  const filteredElements = useMemo(() => {
    return props.sectionType === 'playlist'
      ? contentElements.filter(
          (element) => !clipStore.isNotInterested(element.id)
        )
      : contentElements;
  }, [contentElements, clipStore, props.sectionType]);

  // refresh the item sizes when the elements are removed
  useEffect(() => {
    if (listRef.current) {
      listRef.current.resetAfterIndex(0);
    }
  }, [filteredElements]);

  const showPrimaryFilter = useMemo(
    () => props.options !== null && (props.options?.length ?? 0) > 0,
    [props.options]
  );
  const showSecondaryFilter = useMemo(
    () =>
      props.secondaryOptions !== null &&
      (props.secondaryOptions?.length ?? 0) > 0,
    [props.secondaryOptions]
  );

  const getItemSize = useCallback(() => {
    return ContentTypeToElemWidget[props.sectionType]?.width;
  }, [props.sectionType]);

  const getItemKey = useCallback(
    (index: number) => {
      const element = filteredElements[index];
      return `${element.id || element.external_user_id}-${props.sectionName}`;
    },
    [filteredElements, props.sectionName]
  );

  return (
    <motion.div
      className={`w-full py-4 ${
        ContentTypeToElemWidget[props.sectionType]?.backgroundColor
          ? ContentTypeToElemWidget[props.sectionType]?.backgroundColor
          : 'bg-background-primary'
      } mb-0 md:mb-2`}
      aria-label={`section-${sectionTitle}`}
      onViewportEnter={updatePaddles}
      onLoad={updatePaddles}
    >
      <ImpressionLogger
        configs={impressionLoggerConfigs}
        className={`h-full w-full overflow-hidden`}
      >
        <div className='mb-2 flex w-full flex-row justify-between pb-2'>
          {sectionTitle && (
            <div className='flex items-center gap-4'>
              {sectionLink ? (
                <Link href={sectionLink} className='w-auto'>
                  <h1
                    className={twMerge(
                      TYPOGRAPHY_DISCOVER_SECTION_TITLE_CLASSNAME,
                      'pb-2 text-foreground-primary'
                    )}
                  >
                    {sectionTitle}
                  </h1>
                </Link>
              ) : (
                <h1
                  className={twMerge(
                    TYPOGRAPHY_DISCOVER_SECTION_TITLE_CLASSNAME,
                    'pb-2 text-foreground-primary'
                  )}
                >
                  {sectionTitle}
                </h1>
              )}
            </div>
          )}
          {showPrimaryFilter ||
            (showSecondaryFilter && (
              <>
                <div className='flex flex-row px-2 md:px-4'>
                  <div className='flex items-center'>
                    {showPrimaryFilter && (
                      <Selector
                        selections={
                          props.options?.map((option: string) => ({
                            key: option,
                            name: option,
                          })) ?? []
                        }
                        onChange={async (key: string) => {
                          isFetchingElements(true);
                          setSelectedContentOption(key);
                          const data = await discover.sectionUpdate({
                            sectionName: props.sectionName,
                            sectionContent: key,
                            secondarySectionContent: selectedSecondaryOption,
                          });

                          isFetchingElements(false);
                          if (!data || data.sections?.length == 0) {
                            return;
                          }
                          setContentElements(data?.sections?.[0]?.items ?? []);
                          setSectionLink(
                            data?.sections?.[0]?.link || undefined
                          );
                          setSectionTitle(data?.sections?.[0]?.title || '');
                        }}
                      />
                    )}
                  </div>
                </div>

                {showSecondaryFilter && (
                  <div className='flex items-center px-1'>
                    {props.secondaryOptions !== null &&
                      (props.secondaryOptions?.length ?? 0) > 0 && (
                        <Selector
                          selections={
                            props.secondaryOptions?.map((option: string) => ({
                              key: option,
                              name: option,
                            })) ?? []
                          }
                          onChange={async (key: string) => {
                            isFetchingElements(true);
                            setSelectedSecondaryOption(key);
                            const data = await discover.sectionUpdate({
                              sectionName: props.sectionName,
                              sectionContent: selectedContentOption,
                              secondarySectionContent: key,
                            });
                            isFetchingElements(false);
                            if (!data || data.sections?.length == 0) {
                              return;
                            }
                            setContentElements(
                              data?.sections?.[0]?.items ?? []
                            );
                            setSectionLink(
                              data?.sections?.[0]?.link || undefined
                            );
                            setSectionTitle(data?.sections?.[0]?.title || '');
                          }}
                        />
                      )}
                  </div>
                )}
              </>
            ))}
          {props.sectionName === 'recent_creations' && (
            <div className='flex items-center'>
              <Button
                className='shadow-[inset_0_0_16px_rgba(255,255,255,0.3)] [--button-background-image:linear-gradient(to_bottom,#2215B0,#1D118D)]'
                contentClassName='whitespace-nowrap text-sm'
                onClick={() => {
                  const style =
                    clipStore.clipById[props.elements[0].id].metadata?.tags ||
                    '';
                  createV2.setTagInput(style);
                  router.push('/create');
                }}
                variant={ButtonVariant.Aura}
                size={ButtonSize.Small}
                shape={ButtonShape.Pill}
                iconEnd={CreateIcon}
              >
                Create More
              </Button>
            </div>
          )}
          {sectionLink && (
            <Link
              href={sectionLink || ''}
              onClick={() => {
                eventLogger.segmentTrack(EventNames.webPageEvent, {
                  userId: session?.userId,
                  element: 'homepage_show_more',
                  eventType: 'click',
                  entityType: 'page',
                  entityId: sectionLink,
                  pageUrl: pathname,
                });
              }}
              className='flex flex-1 items-center justify-end'
            >
              <div className='line-clamp-1 cursor-pointer gap-2 font-sans text-sm hover:underline'>
                {'Show More'}
              </div>
              <CaretRightIcon className='h-4 w-4' />
            </Link>
          )}
        </div>

        <div
          ref={containerRef}
          className={`relative w-full overflow-hidden`}
          style={{
            height: `${ContentTypeToElemWidget[props.sectionType]?.height + (props.styleType === 'contest' ? 1.3 : 0)}rem`,
          }}
        >
          <button
            className={`absolute top-0 left-0 z-2 hidden h-full w-16 items-center justify-center transition ease-linear sm:flex ${
              showLeftPaddle
                ? 'pointer-events-auto opacity-100'
                : 'pointer-events-none opacity-0'
            }`}
            aria-label='Scroll left'
            onClick={() => {
              if (scrollRef.current) {
                const step = Math.ceil(scrollRef.current.clientWidth * 0.5);
                const newScrollPos = scrollRef.current.scrollLeft - step;
                // if the new scroll position is within the bounds, set it to the max
                if (newScrollPos > 0 && newScrollPos < step) {
                  scrollRef.current.scrollLeft = 0;
                } else {
                  scrollRef.current.scrollLeft = newScrollPos;
                }
              }
              eventLogger.segmentTrack(EventNames.webPageEvent, {
                userId: session?.userId,
                element: 'carousel_left_tab',
                eventType: 'click',
                entityId: sectionTitle,
                pageUrl: pathname,
              });
            }}
          >
            <Button
              className='absolute left-0 -translate-y-1/2'
              style={{
                top: `${(ContentTypeToElemWidget[props.sectionType]?.cardHeight || 0) / 2}rem`,
              }}
              variant={ButtonVariant.Standard}
              shape={ButtonShape.Pill}
              size={ButtonSize.Small}
              icon={ChevronLeftIcon}
              aria-label={t('cta.prev')}
              enableHoverState
            />
          </button>

          <div
            className={`h-full w-full overflow-hidden ${maskGradient} transition-[mask-image] duration-500`}
          >
            {fetchingElements && <RowSkeleton />}
            {!fetchingElements && (
              <AutoSizer>
                {({ height, width }) => {
                  return (
                    <List
                      ref={listRef}
                      height={height}
                      itemCount={filteredElements.length}
                      // index is available as an arg but not used here
                      itemSize={getItemSize}
                      layout='horizontal'
                      width={width}
                      itemKey={getItemKey}
                      itemData={{
                        elements: filteredElements,
                        playbar,
                        setPreviewClip,
                        contextId:
                          props.sectionId ||
                          'content_carousel_missing_section_id',
                        contextType: ContextType.DiscoverCarousel,
                        clips: ClipsStore,
                        sectionName: props.sectionName,
                        styleType: props.styleType,
                        carouselIndex: props.index,
                      }}
                      outerElementType={CardContainer}
                      outerRef={scrollRef}
                      style={{
                        scrollbarWidth: 'none',
                      }}
                    >
                      {ContentTypeToElemWidget[props.sectionType]?.widget}
                    </List>
                  );
                }}
              </AutoSizer>
            )}
          </div>

          <button
            className={`absolute top-0 right-0 z-2 hidden h-full w-16 items-center justify-center transition ease-linear sm:flex ${
              showRightPaddle
                ? 'pointer-events-auto opacity-100'
                : 'pointer-events-none opacity-0'
            }`}
            aria-label='Scroll right'
            onClick={() => {
              if (scrollRef.current) {
                const step = Math.ceil(scrollRef.current.clientWidth * 0.5);
                const newScrollPos = scrollRef.current.scrollLeft + step;
                const scrollMax =
                  scrollRef.current.scrollWidth - scrollRef.current.clientWidth;
                const lowerBound = scrollMax - step;
                // if the new scroll position is within the bounds, set it to the max
                if (newScrollPos > lowerBound && newScrollPos < scrollMax) {
                  scrollRef.current.scrollLeft = scrollMax;
                } else {
                  scrollRef.current.scrollLeft = newScrollPos;
                }
              }
              eventLogger.segmentTrack(EventNames.webPageEvent, {
                userId: session?.userId,
                element: 'carousel_right_tab',
                eventType: 'click',
                entityId: sectionTitle,
                pageUrl: pathname,
              });
            }}
          >
            <Button
              className='absolute left-0 -translate-y-1/2'
              style={{
                top: `${(ContentTypeToElemWidget[props.sectionType]?.cardHeight || 0) / 2}rem`,
              }}
              variant={ButtonVariant.Standard}
              shape={ButtonShape.Pill}
              size={ButtonSize.Small}
              icon={ChevronRightIcon}
              aria-label={t('cta.next')}
              enableHoverState
            />
          </button>
        </div>
      </ImpressionLogger>
    </motion.div>
  );
});

export default ContentCarousel;
