import { noop } from 'lodash-es';
import { observer } from 'mobx-react-lite';
import { useCallback, useMemo, useState } from 'react';

import { useStores } from '@/app/(root)/AppProviders';
import { FocusedObjectContext } from '@/app/(root)/create/v2/useFocusedObject';
import Draggable, {
  useClipDraggedObject,
} from '@/app/(root)/dragAndDrop/Draggable';
import { PublishButton } from '@/components/song/PublishButton';
import useClip from '@/hooks/useClip';
import { useContestClips } from '@/hooks/useContestClip';
import { useContextSelector } from '@/hooks/useContextSelector';
import useDeviceAttributes from '@/hooks/useDeviceAttributes';
import { MoreHorizontalIcon, TriangleDownIcon } from '@/icons';
import logWebUserEvent from '@/logging/logWebUserEvent';
import { Clip } from '@/state/clipStore';
import { isComplete, isValidModelVersion } from '@/utils/clip';
import { isProjectsFeatureEnabled } from '@/utils/session';

import Button, {
  ButtonShape,
  ButtonSize,
  ButtonVariant,
} from '../button/Button';
import {
  ContextMenuTrigger,
  RightClickMenuTrigger,
} from '../contextMenu/ContextMenu';
import {
  ClipContextProvider,
  NullableClipContextProvider,
} from './ClipContext';
import {
  ClipDetailsWrapper,
  ClipDotAndCheckbox,
  ClipExactTags,
  ClipImage,
  ClipRowGroup,
  ClipRowWrapper,
  ClipTitle,
  WorkspaceClipInteractions,
} from './ClipElements';
import { MoreMenuContents, RemixEditMenuContents } from './ClipMenus';
import MultiSelectContext from './MultiSelectContext';
import { ClipBrowserContext } from './useClipBrowser';
import { ClipBrowserRegistryContext } from './useClipBrowserRegistry';
import useClipPlayback, { ClipPlaybackProvider } from './useClipPlayback';
import { PreviewClipType, useClipPreviewUpsell } from './useClipPreviewUpsell';
import virtualizeClipRow from './virtualizeClipRow';

const WorkspaceClipRowInner = observer(function WorkspaceClipRowInner({
  keepHovering,
  handleRowMouseDown,
  isPinned,
  clip,
  isMultiSelecting,
  setIsRemixEditMenuOpen,
  setIsMoreMenuOpen,
  previewClipType,
  previewUnlocking,
  manualUnlock,
  enableManualUnlock,
  isUnlocking,
  handleToggle,
  handleUnlockPreview,
  handleGetFullSong,
  onboardingRef,
  likeOnboardingRef,
  shareOnboardingRef,
}: {
  keepHovering: boolean;
  handleRowMouseDown: (e: React.MouseEvent) => void;
  isPinned: boolean;
  clip: Clip | null;
  isMultiSelecting: boolean;
  setIsRemixEditMenuOpen: (open: boolean) => void;
  setIsMoreMenuOpen: (open: boolean) => void;
  previewClipType: PreviewClipType;
  previewUnlocking: boolean;
  manualUnlock: boolean;
  enableManualUnlock: boolean;
  isUnlocking: boolean;
  handleToggle: (e: React.MouseEvent) => void;
  handleUnlockPreview: (e: React.MouseEvent) => void;
  handleGetFullSong: (e: React.MouseEvent) => void;
  onboardingRef?: React.RefObject<HTMLDivElement | null>;
  likeOnboardingRef?: React.RefObject<HTMLDivElement | null>;
  shareOnboardingRef?: React.RefObject<HTMLDivElement | null>;
}) {
  const { isContestEligible } = useContestClips();
  const { isMobile } = useDeviceAttributes();
  const isEligibleForContest = clip ? isContestEligible({ clip }) : false;
  const shouldShowSubmitRemix =
    clip &&
    !clip.is_public &&
    isEligibleForContest &&
    clip.is_liked &&
    !isMobile;

  const shouldShowGetFullSong =
    clip &&
    (clip.metadata?.task === 'extend' ||
      clip.metadata?.task === 'upload_extend' ||
      clip.metadata?.task === 'artist_extend') &&
    isComplete(clip) &&
    isValidModelVersion(clip) &&
    !clip.is_trashed;

  return (
    <ClipRowWrapper
      data-testid='clip-row'
      className='clip-row'
      keepHovering={keepHovering}
      onClick={handleRowMouseDown}
      ref={onboardingRef}
      role='row'
    >
      <ClipRowGroup>
        <ClipDotAndCheckbox />
        <ClipImage />
        <ClipDetailsWrapper>
          <ClipTitle />
          <ClipExactTags />
          <WorkspaceClipInteractions
            isPinned={isPinned}
            likeOnboardingRef={likeOnboardingRef}
            shareOnboardingRef={shareOnboardingRef}
          />
        </ClipDetailsWrapper>
      </ClipRowGroup>
      <ClipRowGroup className='shrink-0'>
        {clip && (
          <ClipContextProvider clip={clip}>
            {shouldShowSubmitRemix && (
              <div className='mr-2'>
                <PublishButton clip={clip} />
              </div>
            )}
            {/* Show upgrade/unlock buttons instead of context menus for preview clips */}
            {previewUnlocking ? (
              manualUnlock && enableManualUnlock ? (
                <Button
                  onClick={handleUnlockPreview}
                  variant={ButtonVariant.Primary}
                  size={ButtonSize.Small}
                  shape={ButtonShape.Rounded}
                  className='rounded-full text-xs lg:text-sm'
                  disabled={isUnlocking}
                >
                  {isUnlocking ? 'Unlocking...' : 'Unlock song'}
                </Button>
              ) : (
                <span className='animate-pulse text-xs text-foreground-primary lg:text-sm'>
                  Unlocking song...
                </span>
              )
            ) : previewClipType ? (
              <Button
                onClick={handleToggle}
                variant={ButtonVariant.Primary}
                size={ButtonSize.Small}
                shape={ButtonShape.Rounded}
                className='rounded-full text-xs lg:text-sm'
              >
                Upgrade for full song
              </Button>
            ) : (
              <>
                {shouldShowGetFullSong && (
                  <Button
                    onClick={handleGetFullSong}
                    className='hover-only remix-menu-trigger context-menu-button px-4 py-3 text-xs'
                    shape={ButtonShape.Pill}
                  >
                    Get Full Song
                  </Button>
                )}
                {!clip.is_trashed && !isMultiSelecting && (
                  <ContextMenuTrigger
                    onOpenChange={setIsRemixEditMenuOpen}
                    ButtonComponent={(props) => (
                      <Button
                        className='hover-only remix-menu-trigger context-menu-button px-4 py-2.5 text-xs'
                        shape={ButtonShape.Pill}
                        {...props}
                        aria-label='Remix/Edit clip'
                      >
                        Remix/Edit <TriangleDownIcon className='h-5 w-5' />
                      </Button>
                    )}
                    ContentsComponent={RemixEditMenuContents}
                  />
                )}
                <ContextMenuTrigger
                  onOpenChange={setIsMoreMenuOpen}
                  ButtonComponent={(props) => (
                    <Button
                      className='hover-fade-in context-menu-button p-3'
                      shape={ButtonShape.Pill}
                      icon={<MoreHorizontalIcon className='h-4 w-4' />}
                      {...props}
                      aria-label='More menu contents'
                    />
                  )}
                  ContentsComponent={MoreMenuContents}
                />
              </>
            )}
          </ClipContextProvider>
        )}
      </ClipRowGroup>
    </ClipRowWrapper>
  );
});

export const WorkspaceClipRow = observer(function WorkspaceClipRow({
  clipId,
  onboardingRef,
  likeOnboardingRef,
  shareOnboardingRef,
}: {
  clipId: string;
  onboardingRef?: React.RefObject<HTMLDivElement | null>;
  likeOnboardingRef?: React.RefObject<HTMLDivElement | null>;
  shareOnboardingRef?: React.RefObject<HTMLDivElement | null>;
}) {
  const { clip } = useClip(clipId);
  const { isMobile } = useDeviceAttributes();
  const { session, clips, project } = useStores();
  const clipPlayback = useClipPlayback(clip);

  const [isRemixEditMenuOpen, setIsRemixEditMenuOpen] = useState(false);
  const [isMoreMenuOpen, setIsMoreMenuOpen] = useState(false);
  const {
    previewClipType,
    previewUnlocking,
    manualUnlock,
    enableManualUnlock,
    isUnlocking,
    handleToggle,
    handleUnlockPreview,
  } = useClipPreviewUpsell(clip);

  const clipCreated = useContextSelector(
    ClipBrowserRegistryContext,
    (context) => context?.clipCreated || noop
  );
  const workspacePinnedClips = useContextSelector(
    ClipBrowserContext,
    (context) => context.workspacePinnedClips
  );
  const isPinned = useMemo(
    () => workspacePinnedClips.map((c) => c.id).includes(clipId),
    [workspacePinnedClips, clipId]
  );

  const isMultiSelected = useContextSelector(MultiSelectContext, (ctx) =>
    ctx.selectedClipIds.includes(clip?.id || '')
  );
  const toggleMultiSelectClip = useContextSelector(
    MultiSelectContext,
    (ctx) => ctx.toggleClip
  );
  const selectRangeMultiSelect = useContextSelector(
    MultiSelectContext,
    (ctx) => ctx.selectRange
  );
  const clearMultiSelect = useContextSelector(
    MultiSelectContext,
    (ctx) => ctx.clear
  );
  const setLastSelectedClip = useContextSelector(
    MultiSelectContext,
    (ctx) => ctx.setLastSelected
  );
  const isMultiSelecting = useContextSelector(
    MultiSelectContext,
    (ctx) => ctx.selectedClipIds.length > 0
  );
  const setFocusedObject = useContextSelector(
    FocusedObjectContext,
    ({ setFocusedObject }) => setFocusedObject
  );
  const regularClipIds = useContextSelector(
    ClipBrowserContext,
    (context) => context.clips
  );
  const pinnedClipIds = useMemo(
    () => workspacePinnedClips.map((c) => c.id),
    [workspacePinnedClips]
  );
  const allClipIds = useMemo(
    () => [...pinnedClipIds, ...regularClipIds],
    [pinnedClipIds, regularClipIds]
  );

  const keepHovering = isMultiSelected || isRemixEditMenuOpen || isMoreMenuOpen;

  const handleGetFullSong = useCallback(
    async (e: React.MouseEvent) => {
      if (!clip || !isComplete(clip)) return;

      e.stopPropagation();
      e.preventDefault();

      try {
        if (isProjectsFeatureEnabled(session)) {
          await project.setCurrentProjectToClipProject(clip);
        }

        const concatClip = await clips.runConcat(
          clip.id,
          !!clip.metadata?.infill,
          {
            editSessionId: clip.metadata?.edit_session_id,
          }
        );
        if (concatClip) {
          clipCreated(concatClip);
        }

        logWebUserEvent({
          actionName: 'SongRowGetFullSongClicked',
          principalObjectType: 'song',
          principalObjectValue: clip.id,
        });
      } catch (error) {
        console.error('Failed to get full song:', error);
      }
    },
    [clip, clips, project, session, clipCreated]
  );

  const handleRowClick = useCallback(
    (e: React.MouseEvent) => {
      if (clip?.metadata.type === 'preview') {
        e.stopPropagation();
        e.preventDefault();
        return;
      }
      if (previewClipType === 'lockedPreview') {
        e.stopPropagation();
        e.preventDefault();
        handleToggle(e);
        return;
      }

      const isInteractiveElement =
        !isMultiSelected &&
        (e.target instanceof HTMLElement || e.target instanceof SVGElement) &&
        e.target.matches(
          '.multi-select-button, .multi-select-button *, .clip-interaction-button, .clip-interaction-button *, .context-menu-button, .context-menu-button *'
        );

      if (isInteractiveElement) {
        return;
      }

      if (e.shiftKey) {
        selectRangeMultiSelect(clipId, allClipIds);
      } else if (e.ctrlKey || e.metaKey) {
        toggleMultiSelectClip(clipId);
      } else {
        // Only clicking on the image area should play/pause the song
        if (clipPlayback.onPlayClick) {
          const shouldPlay =
            (e.target instanceof HTMLElement ||
              e.target instanceof SVGElement) &&
            e.target.matches(
              '.clip-image, .clip-image *, .image-container, .image-container *'
            );

          if (shouldPlay) {
            clipPlayback.onPlayClick();
          }
        }
        setFocusedObject({ type: 'clip', clipId });
        setLastSelectedClip(clipId);
        if (!isMultiSelected) clearMultiSelect();
      }
    },
    [
      clipId,
      clearMultiSelect,
      setFocusedObject,
      setLastSelectedClip,
      isMultiSelected,
      toggleMultiSelectClip,
      selectRangeMultiSelect,
      allClipIds,
      clipPlayback,
      isMobile,
      clip?.metadata.type,
      previewClipType,
      handleToggle,
    ]
  );

  const validForInteraction = ['complete', 'streaming'].includes(
    clip?.status ?? ''
  );

  const draggedObject = useClipDraggedObject(clipId);

  return (
    <ClipPlaybackProvider value={clipPlayback}>
      <NullableClipContextProvider clip={clip}>
        <Draggable draggedObject={validForInteraction ? draggedObject : null}>
          <RightClickMenuTrigger ContentsComponent={MoreMenuContents}>
            <WorkspaceClipRowInner
              keepHovering={keepHovering}
              handleRowMouseDown={handleRowClick}
              isPinned={isPinned}
              clip={clip}
              isMultiSelecting={isMultiSelecting}
              setIsRemixEditMenuOpen={setIsRemixEditMenuOpen}
              setIsMoreMenuOpen={setIsMoreMenuOpen}
              previewClipType={previewClipType}
              previewUnlocking={previewUnlocking}
              manualUnlock={manualUnlock}
              enableManualUnlock={enableManualUnlock}
              isUnlocking={isUnlocking}
              handleToggle={handleToggle}
              handleUnlockPreview={handleUnlockPreview}
              handleGetFullSong={handleGetFullSong}
              onboardingRef={onboardingRef}
              likeOnboardingRef={likeOnboardingRef}
              shareOnboardingRef={shareOnboardingRef}
            />
          </RightClickMenuTrigger>
        </Draggable>
      </NullableClipContextProvider>
    </ClipPlaybackProvider>
  );
});

export const VirtualizedWorkspaceClipRow = virtualizeClipRow(WorkspaceClipRow);
