import { useGateValue } from '@statsig/react-bindings';
import { noop } from 'lodash-es';
import { observer } from 'mobx-react-lite';
import { useCallback, useMemo, useState } from 'react';

import { useStores } from '@/app/(root)/AppProviders';
import { ReferenceType } from '@/app/(root)/chat/components/input/ReferenceTypes';
import { useChatContext } from '@/app/(root)/chat/useChat';
import { FocusedObjectContext } from '@/app/(root)/create/v2/useFocusedObject';
import { ModalTypes } from '@/components/modal/constants/ModalTypes';
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 { ContextType } from '@/logging/contextTypes';
import logWebUserEvent from '@/logging/logWebUserEvent';
import { Clip } from '@/state/clipStore';
import { FeatureKey } from '@/state/sessionStore';
import { isComplete, isValidModelVersion } from '@/utils/clip';
import { isProjectsFeatureEnabled } from '@/utils/session';
import { isSubscriber } from '@/utils/session';

import Button, {
  ButtonShape,
  ButtonSize,
  ButtonVariant,
} from '../button/Button';
import { ContextMenuTrigger } from '../contextMenu/ContextMenu';
import {
  ClipContextProvider,
  NullableClipContextProvider,
} from './ClipContext';
import {
  ClipDetailsWrapper,
  ClipDisplayTags,
  ClipImage,
  ClipRowGroup,
  ClipRowWrapper,
  ClipTitle,
  OrpheusClipInteractions,
} from './ClipElements';
import { MoreMenuContents, RemixEditMenuContents } from './ClipMenus';
import MultiSelectContext from './MultiSelectContext';
import { ClipBrowserRegistryContext } from './useClipBrowserRegistry';
import useClipPlayback, { ClipPlaybackProvider } from './useClipPlayback';

type PreviewClipType = 'preview' | 'lockedPreview' | undefined;

const OrpheusClipRowInner = observer(function OrpheusClipRowInner({
  keepHovering,
  clip,
  setIsRemixEditMenuOpen,
  setIsMoreMenuOpen,
  previewClipType,
  previewUnlocking,
  manualUnlock,
  enableManualUnlock,
  isUnlocking,
  handleToggle,
  handleUnlockPreview,
  handleGetFullSong,
  handleRowMouseDown,
  showInteractions = true,
  hideEditTitleIcon = false,
  showShareLabel = false,
  hideRemixEditButton = false,
}: {
  keepHovering: boolean;
  clip: Clip | null;
  handleRowMouseDown: (e: React.MouseEvent) => void;
  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;
  showInteractions?: boolean;
  hideEditTitleIcon?: boolean;
  showShareLabel?: boolean;
  hideRemixEditButton?: boolean;
}) {
  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 cursor-pointer'
      keepHovering={keepHovering}
      onClick={handleRowMouseDown}
      role='row'
    >
      <ClipRowGroup>
        <ClipImage />
        <ClipDetailsWrapper>
          <ClipTitle
            suffix={
              typeof clip?.batch_index === 'number'
                ? ` (${clip.batch_index + (clip.metadata?.batch_offset ?? 0) + 1})`
                : undefined
            }
            showNewIndicator={true}
            editableIfOwned={!hideEditTitleIcon}
          />
          <ClipDisplayTags />
          {showInteractions ? (
            <OrpheusClipInteractions showShareLabel={showShareLabel} />
          ) : null}
        </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 && !hideRemixEditButton && (
                  <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 OrpheusClipRow = observer(function OrpheusClipRow({
  clipId,
  playContext,
  onClick,
  showInteractions,
  hideEditTitleIcon = false,
  showShareLabel = false,
  hideRemixEditButton = false,
}: {
  clipId: string;
  playContext?: {
    contextId: string;
    contextType: ContextType;
    clips: any[];
    currentIndex: number;
  };
  onClick?: () => void;
  showInteractions?: boolean;
  hideEditTitleIcon?: boolean;
  showShareLabel?: boolean;
  hideRemixEditButton?: boolean;
}) {
  const { clip } = useClip(clipId);
  const { session, clips, menus, project } = useStores();

  const [isRemixEditMenuOpen, setIsRemixEditMenuOpen] = useState(false);
  const [isMoreMenuOpen, setIsMoreMenuOpen] = useState(false);
  const [isUnlocking, setIsUnlocking] = useState(false);
  const clipPlayback = useClipPlayback(clip, playContext);
  const isSelected = useContextSelector(MultiSelectContext, (ctx) =>
    ctx.selectedClipIds.includes(clip?.id || '')
  );
  const { addReference, clearReferenceType } = useChatContext();

  // Preview functionality
  const previewClipType: PreviewClipType = useMemo(() => {
    if (!clip) return undefined;
    return clip.preview_seconds == null
      ? undefined
      : clip.preview_seconds === 0
        ? 'lockedPreview'
        : 'preview';
  }, [clip?.preview_seconds]);

  const clipCreated = useContextSelector(
    ClipBrowserRegistryContext,
    (context) => context?.clipCreated || noop
  );

  const previewUnlocking = useMemo(() => {
    return Boolean(clip?.metadata.type === 'preview' && isSubscriber(session));
  }, [clip?.metadata.type, session]);

  const manualUnlock = useMemo(() => {
    return Boolean(clips.shouldShowManualUnlock(clip?.id || ''));
  }, [clips, clip?.id]);

  const enableManualUnlock = useGateValue('manual-unlock') ?? false;

  const keepHovering = isSelected || isRemixEditMenuOpen || isMoreMenuOpen;
  const setFocusedObject = useContextSelector(
    FocusedObjectContext,
    (context) => context?.setFocusedObject
  );
  const toggleMultiSelectClip = useContextSelector(
    MultiSelectContext,
    (ctx) => ctx.toggleClip
  );
  const clearMultiSelect = useContextSelector(
    MultiSelectContext,
    (ctx) => ctx.clear
  );

  // Upgrade and unlock handlers
  const handleToggle = useCallback(
    (e: React.MouseEvent) => {
      if (!clip || clip.preview_seconds == null) return;
      menus.setCurrentUpsellFeature(FeatureKey.UPGRADE_LATEST_MODEL);
      e.stopPropagation();
      e.preventDefault();
      logWebUserEvent({
        actionName: 'UpgradeClipPreviewClicked',
        principalObjectType: 'song',
        principalObjectValue: clip.id,
        context: {
          songId: clip.id,
          previewSeconds: clip.preview_seconds,
        },
      });
      menus.openModal(ModalTypes.UPSELL_MODAL);
    },
    [clip, menus]
  );

  const handleUnlockPreview = useCallback(
    async (e: React.MouseEvent) => {
      if (!clip) return;
      const currentClip = clips.clipById[clip.id];
      if (currentClip?.metadata.type === 'gen' || isUnlocking) return;

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

      try {
        setIsUnlocking(true);
        await clips.unlockPreview(clip.id);
        clips.addToPreviewPollingQueue(clip.id);
      } catch (error) {
        console.error('Failed to unlock preview:', error);
      } finally {
        setIsUnlocking(false);
      }
    },
    [clip, clips, isUnlocking]
  );

  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 =
        (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 (!clip) return;

      clearMultiSelect();

      if (isSelected) {
        setFocusedObject?.(undefined);
        clearReferenceType(ReferenceType.CLIP);
      } else {
        toggleMultiSelectClip(clipId);
        setFocusedObject?.({ type: 'clip', clipId });
        clearReferenceType(ReferenceType.CLIP);
        addReference({ clipId, type: ReferenceType.CLIP });
      }
      onClick?.();
    },
    [
      clipId,
      setFocusedObject,
      clip?.metadata.type,
      previewClipType,
      handleToggle,
      isSelected,
      clearMultiSelect,
      toggleMultiSelectClip,
      addReference,
      clearReferenceType,
      onClick,
    ]
  );

  return (
    <ClipPlaybackProvider value={clipPlayback}>
      <NullableClipContextProvider clip={clip}>
        <OrpheusClipRowInner
          keepHovering={keepHovering}
          handleRowMouseDown={handleRowClick}
          clip={clip}
          setIsRemixEditMenuOpen={setIsRemixEditMenuOpen}
          setIsMoreMenuOpen={setIsMoreMenuOpen}
          previewClipType={previewClipType}
          previewUnlocking={previewUnlocking}
          manualUnlock={manualUnlock}
          enableManualUnlock={enableManualUnlock}
          isUnlocking={isUnlocking}
          handleToggle={handleToggle}
          handleUnlockPreview={handleUnlockPreview}
          handleGetFullSong={handleGetFullSong}
          showInteractions={showInteractions}
          hideEditTitleIcon={hideEditTitleIcon}
          showShareLabel={showShareLabel}
          hideRemixEditButton={hideRemixEditButton}
        />
      </NullableClipContextProvider>
    </ClipPlaybackProvider>
  );
});
