import React, { memo, useCallback, useMemo } from 'react';
import { twMerge } from 'tailwind-merge';

import ImpressionLogger, {
  ImpressionLoggerConfig,
} from '@/components/ImpressionLogger';
import { FeedSkeleton } from '@/components/carousel/carousels/SkeletonCarousel';
import SongFeedItem, {
  getPropsForSongFeedItem,
} from '@/components/feed/SongFeedItem';
import SuggestedCreatorFeedItem, {
  getPropsForSuggestedCreatorFeedItem,
} from '@/components/feed/SuggestedCreatorFeedItem';
import ResultFilters, {
  FilterConfig,
} from '@/components/filters/ResultFilters';
import Link from '@/components/link/Link';
import { CaretRightIcon } from '@/icons';
import { components } from '@/lib/gen';
import logWebUserEvent from '@/logging/logWebUserEvent';
import { Clip } from '@/state/clipStore';
import { TYPOGRAPHY_DISCOVER_SECTION_TITLE_CLASSNAME } from '@/utils/constants';

export type FeedItem =
  | components['schemas']['GeneratedClipSchema']
  | components['schemas']['PlaylistMetadataSchema']
  | components['schemas']['StyleItemSchema']
  | components['schemas']['PersonaSchema']
  | components['schemas']['SimpleProfileInfoSchema']
  | components['schemas']['FollowingFeedItemSchema'];

export type FeedItemListProps = {
  id: string;
  titleClassName?: string;
  feedItems: Array<FeedItem>;
  title: string;
  link?: string;
  filterConfig?: FilterConfig;
  isLoading?: boolean;
  index: number;
  contextId?: string;
  hideAvatar?: boolean;
};

export type Props = Omit<
  React.HTMLAttributes<HTMLDivElement>,
  keyof FeedItemListProps
> &
  FeedItemListProps;

type FeedItemType = {
  Component?: React.ComponentType<any>;
  propGenerator?: (item: any) => { key: string } & Record<string, any>;
  entityField?: keyof components['schemas']['FollowingFeedItemSchema'];
};

export const FOLLOWING_FEED_ITEM_TYPE_TO_COMPONENT: Record<
  string,
  FeedItemType
> = {
  publish_song: {
    Component: SongFeedItem,
    propGenerator: getPropsForSongFeedItem,
    entityField: 'clip_schema',
  },
  comment_song: {},
  like_song: {},
  suggest_creator: {
    Component: SuggestedCreatorFeedItem,
    propGenerator: getPropsForSuggestedCreatorFeedItem,
    entityField: 'profile_schema',
  },
};

export const ENTITY_TYPE_TO_FEED_ITEM_COMPONENT: Record<string, FeedItemType> =
  {
    song_schema: {
      Component: SongFeedItem,
      propGenerator: getPropsForSongFeedItem,
    },
    playlist_metadata_schema: {},
    style_item_schema: {},
    persona_schema: {},
    simple_profile_info_schema: {},
  };

export type FeedListContextProps = {
  listId: string;
  listTitle: string;
  listClips?: components['schemas']['GeneratedClipSchema'][];
  listContextId?: string;
};

export const renderFollowingComponent = ({
  item,
  context,
  index,
  hideAvatar,
}: {
  item: components['schemas']['FollowingFeedItemSchema'];
  context: FeedListContextProps;
  index: number;
  hideAvatar?: boolean;
}) => {
  const { Component, propGenerator, entityField } =
    FOLLOWING_FEED_ITEM_TYPE_TO_COMPONENT[item.activity_type];
  if (!Component || !propGenerator || !entityField) {
    return null;
  }

  const { key, ...props } = propGenerator({
    item: item[entityField],
  });
  return (
    <Component
      key={key}
      {...props}
      {...context}
      index={index}
      hideAvatar={hideAvatar}
    />
  );
};

export const renderFeedItemComponent = ({
  item,
  context,
  index,
  hideAvatar,
}: {
  item: FeedItem;
  context: FeedListContextProps;
  index: number;
  hideAvatar?: boolean;
}) => {
  if (!item.entity_type) {
    return null;
  }
  const { Component, propGenerator } =
    ENTITY_TYPE_TO_FEED_ITEM_COMPONENT[item.entity_type];
  if (!Component || !propGenerator) {
    return null;
  }
  const { key, ...props } = propGenerator({
    item,
  });
  return (
    <Component
      key={key}
      {...props}
      {...context}
      index={index}
      hideAvatar={hideAvatar}
    />
  );
};

export function getFeedListClips(feedItems: FeedItem[]): Clip[] {
  return feedItems
    .map((feedItem) => {
      switch (feedItem.entity_type) {
        case 'song_schema':
          return feedItem;
        case 'following_feed_item_schema':
          return feedItem.clip_schema;
        case 'playlist_schema':
          return feedItem.playlist_clips.map(
            (playlistClip) => playlistClip.clip
          );
        default:
          return undefined;
      }
    })
    .flatMap((clip) => clip)
    .filter((clip) => clip != null);
}

const FeedItemTitle = memo(function FeedItemTitle({
  title,
  link,
  index,
  contextId,
  id,
  titleClassName,
}: {
  title: string;
  link?: string;
  index: number;
  contextId?: string;
  id: string;
  titleClassName?: string;
}) {
  const handleTitleClick = useCallback(() => {
    logWebUserEvent({
      actionName: 'FeaturedFeedListTitleClicked',
      principalObjectType: 'featured_feed_list',
      principalObjectValue: id,
      context: {
        index,
        sectionTitle: title,
        sectionContextId: contextId,
      },
    });
  }, [id, index, title, contextId]);

  return link ? (
    <Link
      href={link}
      className='flex cursor-pointer flex-row items-center hover:underline'
      onClick={handleTitleClick}
    >
      <div
        className={twMerge(
          TYPOGRAPHY_DISCOVER_SECTION_TITLE_CLASSNAME,
          'line-clamp-1',
          titleClassName
        )}
      >
        {title}
      </div>
      <CaretRightIcon className='h-6 w-6 pt-0.5 text-foreground-tertiary' />
    </Link>
  ) : (
    <div
      className={twMerge(
        TYPOGRAPHY_DISCOVER_SECTION_TITLE_CLASSNAME,
        'line-clamp-1',
        titleClassName
      )}
    >
      {title}
    </div>
  );
});

const FeedItemList: React.FC<Props> = memo(function FeedItemList(props) {
  const {
    className,
    titleClassName,
    feedItems,
    id,
    title,
    link,
    index,
    filterConfig,
    isLoading,
    contextId,
    hideAvatar,
    ...restProps
  } = props;

  const listClips = useMemo(
    () => getFeedListClips(feedItems || []),
    [feedItems]
  );
  const context = useMemo(
    () => ({
      listId: id,
      listTitle: title,
      listClips,
      listContextId: contextId,
    }),
    [id, title, listClips, contextId]
  );

  const impressionLoggerConfigs = useMemo<ImpressionLoggerConfig[]>(() => {
    const baseEvent = {
      principalObjectType: 'featured_feed_list' as const,
      principalObjectValue: id,
      context: {
        index,
        sectionTitle: title,
        sectionContextId: contextId,
      },
    };
    return [
      {
        event: {
          ...baseEvent,
          actionName: 'FeaturedFeedListSeen',
        },
        threshold: 0.9,
      },
      {
        event: {
          ...baseEvent,
          actionName: 'FeaturedFeedListSeenPartially',
        },
        threshold: 0.25,
      },
    ];
  }, [title, id, index, contextId]);

  return (
    <ImpressionLogger configs={impressionLoggerConfigs}>
      <div
        className={twMerge(
          'flex min-h-96 flex-col gap-3 text-foreground-primary',
          className
        )}
        {...restProps}
      >
        <div className='flex flex-row justify-between'>
          <FeedItemTitle
            title={title}
            link={link}
            index={index}
            contextId={contextId}
            id={id}
            titleClassName={titleClassName}
          />
          {filterConfig && (
            <ResultFilters
              filters={filterConfig}
              className='md:pr-10 md:pl-2'
            />
          )}
        </div>
        <div className='flex flex-col gap-1'>
          {isLoading || !feedItems ? (
            <FeedSkeleton columnCount={1} itemCount={5} showTitles={false} />
          ) : feedItems.length > 0 ? (
            <>
              {feedItems.map((item, index) => {
                if (!item) {
                  return null;
                }
                if (item.entity_type === 'following_feed_item_schema') {
                  return renderFollowingComponent({
                    item,
                    context,
                    index,
                    hideAvatar,
                  });
                } else {
                  return renderFeedItemComponent({
                    item,
                    context,
                    index,
                    hideAvatar,
                  });
                }
              })}
            </>
          ) : (
            <div className='text-md mt-10 flex items-center justify-center text-foreground-tertiary'>
              No Results Found
            </div>
          )}
        </div>
      </div>
    </ImpressionLogger>
  );
});

export default FeedItemList;
