import clsx from 'clsx';
import { observer } from 'mobx-react-lite';
import { memo, useCallback, useMemo, useState } from 'react';
import React from 'react';
import { useTranslation } from 'react-i18next';
import { twMerge } from 'tailwind-merge';

import { useStores } from '@/app/(root)/AppProviders';
import ImpressionLogger, {
  ImpressionLoggerConfig,
} from '@/components/ImpressionLogger';
import {
  ButtonShape,
  ButtonSize,
  ButtonVariant,
} from '@/components/button/Button';
import { FeedListContextProps } from '@/components/feed/FeedItemList';
import { SongFeedItemData } from '@/components/feed/SongFeedItemData';
import ImageWithFallback from '@/components/image/ImageWithFallback';
import MobileSongActions from '@/components/song/MobileSongActions';
import { SongMenuWithContext } from '@/components/song/newActions/SongMenuWithContext';
import { tagsToArray } from '@/components/song/songUtils';
import { ModelNameTag } from '@/components/tag/ModelNameTag';
import Tag, { TagVariant } from '@/components/tag/Tag';
import { ThemeMode, useThemeContext } from '@/context/ThemeContext';
import useDeviceAttributes from '@/hooks/useDeviceAttributes';
import { MoreHorizontalIcon, StatefulPlayPauseIcon } from '@/icons';
import { components } from '@/lib/gen';
import { ContextType } from '@/logging/contextTypes';
import logWebUserEvent from '@/logging/logWebUserEvent';
import { Clip } from '@/state/clipStore';
import { getClipDisplayTags } from '@/utils/clip';
import { NO_STYLE_FALLBACK, SMALL_IMAGE } from '@/utils/constants';

export const getPropsForSongFeedItem = ({
  item,
}: {
  item: components['schemas']['GeneratedClipSchema'];
}): SongFeedItemProps & { key: string } => {
  return {
    key: `${item.title}-${item.id}`,
    imgUrl: item.image_url ?? '',
    title: item.title ?? '',
    clipId: item.id ?? '',
    tags: getClipDisplayTags(item),
    displayName: item.display_name ?? '',
    handle: item.handle ?? '',
    avatarImageUrl: item.avatar_image_url ?? '',
    personaId: item.persona?.id ?? '',
    personaName: item.persona?.name ?? '',
    personaImageUrl: item.persona?.image_s3_id ?? '',
    playCount: item.play_count ?? 0,
    upvoteCount: item.upvote_count ?? 0,
    commentCount: item.comment_count ?? 0,
  };
};

export type SongFeedItemProps = React.HTMLAttributes<HTMLDivElement> & {
  imgUrl: string;
  title: string;
  clipId: string;
  tags: string;
  displayName: string;
  handle: string;
  avatarImageUrl: string;
  playCount: number;
  upvoteCount: number;
  commentCount: number;
  personaId?: string;
  personaName?: string;
  personaImageUrl?: string;
  hideAvatar?: boolean;
};

export type Props = Omit<
  React.HTMLAttributes<HTMLDivElement>,
  keyof FeedListContextProps | keyof SongFeedItemProps
> &
  SongFeedItemProps &
  FeedListContextProps & { index: number };

const SongFeedItemActions = memo(function SongFeedItemActions(props: {
  isNotInterested: boolean;
  isHighlighted: boolean;
  isMobile: boolean;
  clip: Clip;
  listId: string;
  handleMenuOpen: () => void;
  handleMenuClose: () => void;
}) {
  const {
    isNotInterested,
    isHighlighted,
    isMobile,
    clip,
    listId,
    handleMenuOpen,
    handleMenuClose,
  } = props;

  const handleOpenChange = useCallback(
    (open: boolean) => {
      if (open) {
        handleMenuOpen();
      } else {
        handleMenuClose();
      }
    },
    [handleMenuOpen, handleMenuClose]
  );

  return isNotInterested ? null : (
    <div
      className={clsx(
        '-mt-1 -mr-1',
        'transition-opacity duration-150',
        'group-hover:opacity-100 focus-within:opacity-100',
        {
          'opacity-0': !(isHighlighted || isMobile),
          'opacity-100': isHighlighted || isMobile,
        }
      )}
    >
      {isMobile ? (
        <MobileSongActions clip={clip} isSongPage={true} />
      ) : (
        <SongMenuWithContext
          clip={clip}
          sectionName={listId}
          className='rounded-md'
          variant={ButtonVariant.Tertiary}
          shape={ButtonShape.Rounded}
          size={ButtonSize.Mini}
          active
          icon={MoreHorizontalIcon}
          onOpenChange={handleOpenChange}
        />
      )}
    </div>
  );
});

const SongFeedItem: React.FC<Props> = observer((props) => {
  const {
    imgUrl,
    title,
    clipId,
    tags: rawTags,
    displayName,
    handle,
    avatarImageUrl,
    personaId,
    personaName,
    personaImageUrl,
    playCount,
    upvoteCount,
    commentCount,
    listId,
    listTitle,
    listClips,
    listContextId,
    className,
    index,
    hideAvatar,
    ...restProps
  } = props;

  const {
    clips: clipStore,
    playbar: playbarStore,
    queue: queueStore,
  } = useStores();

  const { t } = useTranslation();

  const { isMobile } = useDeviceAttributes();

  const clip = useMemo(() => clipStore.clipById[clipId], [clipStore, clipId]);
  const isNotInterested = clipStore.notInterestedById[clipId];
  const contextId = listContextId || listId || clipId;
  const contextType = ContextType.FeaturedFeed;
  const tags = useMemo(
    () => (rawTags.length ? tagsToArray(rawTags) : [NO_STYLE_FALLBACK]),
    [rawTags]
  );
  const { effectiveTheme } = useThemeContext();

  const handlePlayClip = useCallback(() => {
    logWebUserEvent({
      actionName: 'SongFeedItemPlayClicked',
      principalObjectType: 'song',
      principalObjectValue: clipId,
      context: {
        index: index,
        playCount: playCount,
        likeCount: upvoteCount,
        commentCount: commentCount,
        sectionId: listId,
        sectionTitle: listTitle,
        sectionContextId: listContextId,
      },
    });

    // Bootstrap the play context
    if (
      queueStore.contextId === contextId &&
      queueStore.contextType === ContextType.FeaturedFeed
    ) {
      queueStore.setClips(listClips || []);
      queueStore.setPendingClips([]);
    } else {
      queueStore.setPendingClips(listClips || []);
    }
    // Figure out where this clip is in the list
    const currentIndex =
      listClips?.findIndex((clip) => clip.id === clipId) || 0;

    // Play the queue
    queueStore.setPlayContext({
      currentIndex: Math.max(0, currentIndex),
      clips: queueStore.getClipsForContext(contextType, contextId),
      contextType: contextType,
      contextId: contextId,
    });
    playbarStore.playClip(clipStore.clipById[clipId]);
  }, [
    clipStore,
    playbarStore,
    queueStore,
    clipId,
    listId,
    listContextId,
    listClips,
    contextId,
    contextType,
    index,
    playCount,
    upvoteCount,
    commentCount,
    listTitle,
  ]);

  const handlePlayClick = useCallback(() => {
    if (playbarStore.clip?.id === clipId && !playbarStore.isClipPreloaded) {
      playbarStore.togglePlay();
      return;
    }
    handlePlayClip();
  }, [playbarStore, clipId, handlePlayClip]);

  const handleDoubleClick = useCallback(() => {
    // Always play from beginning
    handlePlayClip();
  }, [handlePlayClip]);

  const isCurrentSong = useMemo(() => {
    return (
      queueStore.contextType === contextType &&
      queueStore.contextId === contextId &&
      playbarStore.clip?.id === clipId
    );
  }, [
    clipId,
    contextId,
    contextType,
    queueStore.contextType,
    queueStore.contextId,
    playbarStore.clip?.id,
  ]);
  const isPlaying = isCurrentSong && playbarStore.isPlaying;

  const [isHighlighted, setIsHighlighted] = useState(false);
  const handleMenuOpen = useCallback(() => {
    setIsHighlighted(true);
  }, []);
  const handleMenuClose = useCallback(() => {
    setIsHighlighted(false);
  }, []);

  const impressionLoggerConfig = useMemo<ImpressionLoggerConfig[]>(() => {
    return [
      {
        event: {
          actionName: 'SongFeedItemSeen',
          principalObjectType: 'song',
          principalObjectValue: clipId,
          context: {
            index: index,
            playCount: playCount,
            likeCount: upvoteCount,
            commentCount: commentCount,
            sectionId: listId,
            sectionTitle: listTitle,
            sectionContextId: listContextId,
          },
        },
        threshold: 0.9,
      },
    ];
  }, [
    clipId,
    index,
    playCount,
    upvoteCount,
    commentCount,
    listId,
    listTitle,
    listContextId,
  ]);

  const modelTag = useMemo(() => {
    const {
      metadata: { type: clipType },
      major_model_version: modelMajorVersion,
      model_name: modelName,
    } = clip || {};

    if (clipType === 'studio_export') {
      return <Tag variant={TagVariant.Studio}>Made with Studio</Tag>;
    }

    return clip.metadata?.uses_latest_model ? (
      <ModelNameTag
        variant={TagVariant.Inherit}
        clipType={clipType}
        displayName={clip.metadata?.model_badges?.songrow?.display_name}
        textColor={
          effectiveTheme === ThemeMode.Dark
            ? clip.metadata?.model_badges?.songrow?.dark?.text_color
              ? `#${clip.metadata.model_badges.songrow.dark.text_color}`
              : undefined
            : clip.metadata?.model_badges?.songrow?.light?.text_color
              ? `#${clip.metadata.model_badges.songrow.light.text_color}`
              : undefined
        }
        backgroundColor={
          effectiveTheme === ThemeMode.Dark
            ? clip.metadata?.model_badges?.songrow?.dark?.background_color
              ? `#${clip.metadata.model_badges.songrow.dark.background_color}`
              : undefined
            : clip.metadata?.model_badges?.songrow?.light?.background_color
              ? `#${clip.metadata.model_badges.songrow.light.background_color}`
              : undefined
        }
        borderColor={
          effectiveTheme === ThemeMode.Dark
            ? clip.metadata?.model_badges?.songrow?.dark?.border_color
              ? `#${clip.metadata.model_badges.songrow.dark.border_color}`
              : undefined
            : clip.metadata?.model_badges?.songrow?.light?.border_color
              ? `#${clip.metadata.model_badges.songrow.light.border_color}`
              : undefined
        }
        modelMajorVersion={modelMajorVersion}
        modelName={modelName}
      />
    ) : null;
  }, [clip]);

  return (
    <ImpressionLogger configs={impressionLoggerConfig}>
      <div
        className={twMerge(
          clsx(
            'group flex w-full flex-row items-center justify-between rounded-lg p-2',
            'transition-colors duration-150',
            'focus-within:bg-background-secondary-glass hover:bg-background-secondary-glass',
            {
              'pointer-events-none cursor-not-allowed opacity-50':
                isNotInterested,
              'bg-background-secondary-glass': isHighlighted,
            }
          ),
          className
        )}
        onDoubleClick={handleDoubleClick}
        data-media-playing={isPlaying}
        aria-disabled={isNotInterested}
        inert={isNotInterested}
        {...restProps}
      >
        <div className='flex w-full min-w-0 flex-row items-center gap-2'>
          <button
            className='group relative shrink-0'
            onClick={handlePlayClick}
            aria-label={isPlaying ? t('media.pause') : t('media.play')}
          >
            <ImageWithFallback
              className='h-16 w-12 rounded-lg object-cover'
              src={imgUrl}
              alt={title}
              imageSize={SMALL_IMAGE}
            />
            <div
              className={clsx(
                'absolute inset-0 flex items-center justify-center rounded-lg',
                'transition-colors duration-150',
                'group-hover:bg-opacity-black-30 group-hover:text-foreground-primary-glass',
                'group-focus-within:bg-opacity-black-30 group-focus-within:text-foreground-primary-glass',
                {
                  'bg-transparent text-transparent': !(
                    isPlaying || isCurrentSong
                  ),
                  'bg-opacity-black-30 text-foreground-primary-glass':
                    isPlaying || isCurrentSong,
                }
              )}
            >
              <StatefulPlayPauseIcon className='h-6 w-6' />
            </div>
          </button>
          <SongFeedItemData
            className='min-w-0 flex-1'
            title={title}
            titleExtra={modelTag}
            clipId={clipId}
            tags={tags}
            displayName={displayName}
            handle={handle}
            imageUrl={avatarImageUrl}
            personaId={personaId}
            personaName={personaName}
            personaImageUrl={personaImageUrl}
            playCount={playCount}
            likeCount={upvoteCount}
            commentCount={commentCount}
            onPlayClick={handlePlayClick}
            index={index}
            listId={listId}
            listTitle={listTitle}
            listContextId={listContextId}
            hideAvatar={hideAvatar}
            artistId={clip?.user_id || ''}
          >
            <SongFeedItemActions
              isNotInterested={isNotInterested}
              isHighlighted={isHighlighted}
              isMobile={isMobile}
              clip={clip}
              listId={listId}
              handleMenuOpen={handleMenuOpen}
              handleMenuClose={handleMenuClose}
            />
          </SongFeedItemData>
        </div>
      </div>
    </ImpressionLogger>
  );
});

export default SongFeedItem;
