import styled from '@emotion/styled';
import { useStatsigClient } from '@statsig/react-bindings';
import { uniq } from 'lodash-es';
import { useRouter } from 'next/navigation';
import { useCallback, useState } from 'react';

import { useStores } from '@/app/(root)/AppProviders';
import { toast } from '@/components/toast/Toast';
import { fetchClip } from '@/hooks/useClip';
import { useContextSelector } from '@/hooks/useContextSelector';
import {
  CopyIcon,
  DiscardIcon,
  DuplicateIcon,
  EmbedIcon,
  ExtendRightIcon,
  MicrophoneIcon,
  PasteIcon,
  RecentPromptsIcon,
  ScissorsIcon,
  SectionIcon,
  VolumeOnIcon,
} from '@/icons';
import { useApiClient } from '@/lib/apiClient';
import logWebUserEvent from '@/logging/logWebUserEvent';
import { PlanFeature } from '@/state/sessionStore';
import ctrlStr from '@/utils/ctrlStr';
import { initDownloadClipWav } from '@/utils/download';
import { isFeatureEnabledForPlan } from '@/utils/session';

import Button, {
  ButtonShape,
  ButtonSize,
  ButtonVariant,
} from '../button/Button';
import { openDownloadConfirmModal } from '../clipBrowser/ClipMenuItems';
import {
  ContextMenuGroup,
  ContextMenuItem,
  ContextMenuItemMainText,
  ContextMenuItemSubtext,
} from '../contextMenu/ContextMenu';
import { songSectionColors } from '../edit2025/getSections';
import { getDownbeatsFromTiming } from '../edit2025/getTimingFromDownbeats';
import { getPlaintextLyrics } from '../edit2025/lyrics/getPlaintextLyrics';
import { ModalTypes } from '../modal/constants/ModalTypes';
import SpinnerSVG from '../svg/SpinnerSVG';
import { Tooltip } from '../tooltip/Tooltip';
import StudioContext from './StudioContext';
import StudioHorizontalFader, {
  dbToZeroOne,
  multiplierToScaledZeroOne,
  scaledZeroOneToMultiplier,
} from './StudioHorizontalFader';
import { StudioProjectManagementContext } from './StudioProjectManagementContext';
import StudioSelectionActions from './StudioSelectionActions';
import assignMetadata from './actions/assignMetadata';
import { combineActions } from './actions/combineActions';
import {
  copy,
  duplicate,
  magneticDuplicate,
  magneticPaste,
  paste,
} from './actions/copyPaste';
import deleteRanges from './actions/deleteRanges';
import deleteTimelineSelection from './actions/deleteTimelineSelection';
import healEdits, { canHealSelection } from './actions/healEdits';
import magneticDeleteTimelineSelection from './actions/magneticDeleteTimelineSelection';
import makeBeat1 from './actions/makeBeat1';
import moveSelection from './actions/moveSelection';
import setGapSize from './actions/setGapSize';
import splitTimelineSelection from './actions/splitTimelineSelection';
import updateClipAmplitude from './actions/updateClipAmplitude';
import updateClipColor from './actions/updateClipColor';
import updateClipName from './actions/updateClipName';
import renderState, { getRenderStateKey } from './apiWrappers/renderState';
import DEFAULT_STATE from './defaultState';
import downloadAsMIDI from './downloadAsMIDI';
import getStateAlignedLyrics from './getStateAlignedLyrics';
import { getTrimmedUnmutedTracks } from './helpers/getTrimmedUnmutedTracks';
import { inFlightDragKeys } from './hooks/useInFlightDrags';
import {
  getDerivedTiming,
  getFocusedClip,
  getFocusedStudioClip,
  getSelectedClips,
  getSelectionDurationBeats,
  getSelectionEndBeats,
  getSelectionStartBeats,
  getSelectionStartSeconds,
  getTrackIds,
} from './selectors';
import { StudioProjectState } from './types';
import useCanPaste from './useCanPaste';
import { formatDecibelValue } from './valueFormatting';

const colors = uniq(Object.values(songSectionColors));

const TextInput = styled.input`
  background-color: var(--color-background-smoke-thick);
  padding: 10px;
  margin: 2px;
  border: none;
  font-size: 14px;
  font-weight: 500;
  letter-spacing: 0;
  font-family: 'PP Neue Montreal', sans-serif;
  border-radius: 4px;
`;

export default function StudioContextMenuContents({
  onClose,
  eventTrigger,
}: {
  onClose: () => void;
  eventTrigger: 'right-click' | 'three-dot-menu';
}) {
  const clipsById = useContextSelector(
    StudioContext,
    (context) => context.clipsById
  );
  const focusedClip = useContextSelector(StudioContext, (context) =>
    getFocusedClip({ state: context.state, clipsById: context.clipsById })
  );
  const focusedStudioClip = useContextSelector(StudioContext, (context) =>
    getFocusedStudioClip(context.state)
  );
  const focusedTrackId = useContextSelector(
    StudioContext,
    (context) => context.state.selection.focusedTrackId
  );
  const playbackController = useContextSelector(
    StudioContext,
    (context) => context.playbackController
  );
  const inFlightDrags = useContextSelector(
    StudioContext,
    (context) => context.inFlightDrags
  );

  const hasZeroLengthSelection = useContextSelector(
    StudioContext,
    (context) => getSelectionDurationBeats(context.state) <= 0
  );
  const canSplit = useContextSelector(StudioContext, (context) => {
    const selectedClips = getSelectedClips(context.state);
    return selectedClips.some(
      (clip) =>
        (clip.startBeats < context.state.selection.anchorBeats &&
          clip.endBeats > context.state.selection.anchorBeats) ||
        (clip.startBeats < context.state.selection.focusBeats &&
          clip.endBeats > context.state.selection.focusBeats)
    );
  });
  const hasSelectedContent = useContextSelector(StudioContext, (context) => {
    const selectedClips = getSelectedClips(context.state);
    return !hasZeroLengthSelection && selectedClips.length > 0;
  });
  const canHeal = useContextSelector(StudioContext, (context) =>
    canHealSelection(context.state)
  );

  const setState = useContextSelector(
    StudioContext,
    (context) => context.setState
  );
  const isReadOnlyMode = useContextSelector(
    StudioContext,
    (context) => context.isReadOnlyMode
  );

  const magnetMode = useContextSelector(
    StudioContext,
    (context) => context.magnetMode
  );
  const reloadClipLyrics = useContextSelector(
    StudioContext,
    (context) => context.reloadClipLyrics
  );
  const editSessionId = useContextSelector(
    StudioContext,
    (context) => context.editSessionId
  );
  const editClipId = useContextSelector(
    StudioContext,
    (context) => context.state.editClipId
  );
  const anchorBeats = useContextSelector(
    StudioContext,
    (context) => context.state.selection.anchorBeats
  );
  const stateRef = useContextSelector(
    StudioContext,
    (context) => context.stateRef
  );
  const selectionStartBeats = useContextSelector(StudioContext, (context) =>
    getSelectionStartBeats(context.state)
  );
  const selectionStartSeconds = useContextSelector(StudioContext, (context) =>
    getSelectionStartSeconds(context.state)
  );
  const selectionEndBeats = useContextSelector(StudioContext, (context) =>
    getSelectionEndBeats(context.state)
  );
  const trackIds = useContextSelector(StudioContext, (context) =>
    getTrackIds(context.state)
  );
  const editMode = useContextSelector(
    StudioContext,
    (context) => context.mode === 'edit'
  );

  const canPaste = useCanPaste();
  const [clipName, setClipName] = useState(focusedStudioClip?.name || '');

  const [reloadingLyrics, setReloadingLyrics] = useState(false);
  const canCrop = hasSelectedContent && editMode;

  const alignedLyricsByClipId = useContextSelector(
    StudioContext,
    (context) => context.alignedLyricsByClipId
  );
  const studioProjectId = useContextSelector(
    StudioContext,
    (context) => context.studioProjectId
  );

  const currentProjectTitle = useContextSelector(
    StudioProjectManagementContext,
    (context) => context.loadedProject?.title || 'Untitled Project'
  );

  const apiClient = useApiClient();
  const { clips: clipsStore, session, menus } = useStores();
  const statsigClient = useStatsigClient();
  const router = useRouter();
  const enableMIDI = !!isFeatureEnabledForPlan(session, PlanFeature.Studio);
  const [gettingMIDI, setGettingMIDI] = useState(false);
  const handleGetMIDI = useCallback(async () => {
    setGettingMIDI(true);
    try {
      const selectionStartBeats = getSelectionStartBeats(stateRef.current);
      const selectionEndBeats = getSelectionEndBeats(stateRef.current);
      const tracks = getTrimmedUnmutedTracks(stateRef.current);
      const timing = getDerivedTiming(stateRef.current);
      const state: StudioProjectState = {
        ...DEFAULT_STATE,
        tracks,
        timing: {
          type: 'manual',
          ...timing,
        },
      };
      const downbeats = getDownbeatsFromTiming(
        timing,
        selectionStartBeats,
        selectionEndBeats
      );
      const clip = await renderState({
        apiClient: apiClient,
        state: state,
        startBeats: selectionStartBeats,
        endBeats: selectionEndBeats,
        fromStudioProjectId: studioProjectId,
        lyrics: getPlaintextLyrics(
          getStateAlignedLyrics(state, alignedLyricsByClipId, {
            startBeats: selectionStartBeats,
            endBeats: selectionEndBeats,
          })
        ),
        downbeats,
        cacheKey: getRenderStateKey(
          state,
          selectionStartBeats,
          selectionEndBeats
        ),
      });
      const success = await downloadAsMIDI(
        clipsStore,
        clip,
        currentProjectTitle || 'Suno Studio MIDI',
        downbeats
      );
      if (!success) {
        toast({
          title: 'Error',
          description: 'Something went wrong. Please try on a different clip.',
          status: 'error',
          duration: 5000,
          isClosable: true,
        });
      }
    } finally {
      setGettingMIDI(false);
    }
  }, [
    apiClient,
    stateRef,
    alignedLyricsByClipId,
    studioProjectId,
    currentProjectTitle,
    clipsStore,
  ]);

  const [downloadingWAV, setDownloadingWAV] = useState(false);
  const handleDownloadAudio = useCallback(async () => {
    setDownloadingWAV(true);
    try {
      const selectionStartBeats = getSelectionStartBeats(stateRef.current);
      const selectionEndBeats = getSelectionEndBeats(stateRef.current);
      const tracks = getTrimmedUnmutedTracks(stateRef.current);
      const timing = getDerivedTiming(stateRef.current);
      const state: StudioProjectState = {
        ...DEFAULT_STATE,
        tracks,
        timing: {
          type: 'manual',
          ...timing,
        },
      };
      const downbeats = getDownbeatsFromTiming(
        timing,
        selectionStartBeats,
        selectionEndBeats
      );
      const clip = await renderState({
        apiClient: apiClient,
        state: state,
        startBeats: selectionStartBeats,
        endBeats: selectionEndBeats,
        fromStudioProjectId: studioProjectId,
        lyrics: getPlaintextLyrics(
          getStateAlignedLyrics(state, alignedLyricsByClipId, {
            startBeats: selectionStartBeats,
            endBeats: selectionEndBeats,
          })
        ),
        downbeats,
        cacheKey: getRenderStateKey(
          state,
          selectionStartBeats,
          selectionEndBeats
        ),
      });
      await fetchClip(clipsStore, clip.id, true);
      await openDownloadConfirmModal(
        clip,
        menus,
        session,
        clipsStore,
        router,
        statsigClient,
        apiClient,
        () => {
          menus.openModal(ModalTypes.DOWNLOAD_FILE);
          initDownloadClipWav(clipsStore.apiClient, clip.id);
          clipsStore.pendingWavDownloadClip = clip;
          clipsStore.pendingWavDownloadAtTime = Date.now();
          clipsStore.setPendingWavPolling();
        }
      );
    } finally {
      setDownloadingWAV(false);
    }
  }, [
    apiClient,
    stateRef,
    alignedLyricsByClipId,
    studioProjectId,
    setDownloadingWAV,
    menus,
    session,
    clipsStore,
    router,
    statsigClient,
  ]);

  const mode = useContextSelector(StudioContext, (context) => context.mode);

  return (
    <>
      {focusedStudioClip && mode === 'edit' && (
        <ContextMenuGroup>
          <Tooltip label='Clip Name' placement='right'>
            <TextInput
              value={clipName}
              onChange={(e) => {
                setClipName(e.target.value);
              }}
              onBlur={() => {
                setClipName(focusedStudioClip.name);
              }}
              onKeyDown={(e) => {
                if (e.key === 'Enter') {
                  setState(updateClipName(focusedStudioClip.id, clipName));
                  e.currentTarget.blur();
                  setClipName(clipName);
                  e.stopPropagation();
                } else if (e.key === 'Escape') {
                  e.currentTarget.blur();
                  setClipName(focusedStudioClip.name);
                  e.stopPropagation();
                }
              }}
            />
          </Tooltip>
          <Tooltip
            label='Clip Volume (double-click to reset)'
            placement='right'
          >
            <ContextMenuItem icon={<VolumeOnIcon className='h-4 w-4' />}>
              <StudioHorizontalFader
                color={focusedStudioClip.color}
                key={focusedTrackId}
                value={multiplierToScaledZeroOne(focusedStudioClip.amplitude)}
                defaultValue={dbToZeroOne(0)}
                anchorValue={dbToZeroOne(0)}
                formatValue={(x) =>
                  formatDecibelValue(scaledZeroOneToMultiplier(x))
                }
                getMeterValue={(index: number) => {
                  const currentBeats = playbackController.getCurrentBeats();
                  if (currentBeats < focusedStudioClip.startBeats) {
                    return {
                      ppm: -Infinity,
                      ppmHold: -Infinity,
                      vu: -Infinity,
                    };
                  } else if (currentBeats > focusedStudioClip.endBeats) {
                    return {
                      ppm: -Infinity,
                      ppmHold: -Infinity,
                      vu: -Infinity,
                    };
                  }
                  return playbackController.getTrackMeter(
                    focusedTrackId,
                    index
                  );
                }}
                onChange={(value) => {
                  inFlightDrags.update(
                    inFlightDragKeys.clipGain(focusedStudioClip.id),
                    scaledZeroOneToMultiplier(value) -
                      focusedStudioClip.amplitude
                  );
                  playbackController.setClipAmplitude(
                    focusedStudioClip.id,
                    scaledZeroOneToMultiplier(value)
                  );
                }}
                onCommit={(value) => {
                  setState(
                    updateClipAmplitude(
                      focusedStudioClip.id,
                      scaledZeroOneToMultiplier(value)
                    )
                  );
                  inFlightDrags.finishAll();
                }}
              />
            </ContextMenuItem>
          </Tooltip>
          <Tooltip label='Clip Color' placement='right'>
            <div className='flex min-w-40 justify-evenly px-2 pb-2'>
              {colors.map((c) => (
                <Button
                  key={c}
                  shape={ButtonShape.Rectangle}
                  variant={ButtonVariant.Tertiary}
                  size={ButtonSize.Mini}
                  icon={
                    <div
                      className='h-5 w-5'
                      style={{ backgroundColor: c, borderRadius: 2 }}
                    />
                  }
                  onClick={() => {
                    setState(updateClipColor(focusedStudioClip.id, c));
                  }}
                />
              ))}
            </div>
          </Tooltip>
        </ContextMenuGroup>
      )}
      {hasSelectedContent && mode === 'studio' && (
        <StudioSelectionActions
          downloadingWAV={downloadingWAV}
          handleDownloadAudio={handleDownloadAudio}
          enableMIDI={enableMIDI}
          gettingMIDI={gettingMIDI}
          handleGetMIDI={handleGetMIDI}
          onClose={onClose}
        />
      )}
      <ContextMenuGroup>
        {!hasZeroLengthSelection && (
          <ContextMenuItem
            className='bg-transparent'
            icon={<CopyIcon className='h-4 w-4' />}
            onClick={() => {
              copy(stateRef.current);
              onClose();
              logWebUserEvent({
                actionName: 'EditV3Copy',
                context: {
                  editSessionId: editSessionId,
                  editingClipId:
                    stateRef.current.editClipId || 'MISSING_EDIT_CLIP_ID',
                  trigger: eventTrigger,
                },
              });
            }}
          >
            <ContextMenuItemMainText>Copy</ContextMenuItemMainText>
            <ContextMenuItemSubtext>{ctrlStr}+C</ContextMenuItemSubtext>
          </ContextMenuItem>
        )}
        {canPaste && !isReadOnlyMode && (
          <ContextMenuItem
            className='bg-transparent'
            icon={<PasteIcon className='h-4 w-4' />}
            onClick={() => {
              setState(magnetMode ? magneticPaste : paste);
              onClose();
              logWebUserEvent({
                actionName: 'EditV3Paste',
                context: {
                  editSessionId: editSessionId,
                  editingClipId: editClipId || 'MISSING_EDIT_CLIP_ID',
                  trigger: eventTrigger,
                },
              });
            }}
          >
            <ContextMenuItemMainText>Paste</ContextMenuItemMainText>
            <ContextMenuItemSubtext>{ctrlStr}+V</ContextMenuItemSubtext>
          </ContextMenuItem>
        )}
        {!hasZeroLengthSelection && !isReadOnlyMode && (
          <ContextMenuItem
            className='bg-transparent'
            icon={<DuplicateIcon className='h-4 w-4' />}
            onClick={() => {
              setState(magnetMode ? magneticDuplicate : duplicate);
              onClose();
              logWebUserEvent({
                actionName: 'EditV3Duplicate',
                context: {
                  editSessionId: editSessionId,
                  editingClipId: editClipId || 'MISSING_EDIT_CLIP_ID',
                  trigger: eventTrigger,
                },
              });
            }}
          >
            <ContextMenuItemMainText>Duplicate</ContextMenuItemMainText>
            <ContextMenuItemSubtext>{ctrlStr}+D</ContextMenuItemSubtext>
          </ContextMenuItem>
        )}
        {!hasZeroLengthSelection && !isReadOnlyMode && (
          <ContextMenuItem
            className='bg-transparent'
            icon={DiscardIcon}
            onClick={() => {
              setState(
                magnetMode
                  ? magneticDeleteTimelineSelection
                  : deleteTimelineSelection
              );
              onClose();
              logWebUserEvent({
                actionName: 'EditV3Delete',
                context: {
                  editSessionId: editSessionId,
                  editingClipId: editClipId || 'MISSING_EDIT_CLIP_ID',
                  trigger: eventTrigger,
                },
              });
            }}
          >
            <ContextMenuItemMainText>Delete</ContextMenuItemMainText>
            <ContextMenuItemSubtext>Backspace</ContextMenuItemSubtext>
          </ContextMenuItem>
        )}
      </ContextMenuGroup>

      <ContextMenuGroup>
        {hasZeroLengthSelection && anchorBeats < 4 && anchorBeats !== 0 && (
          <ContextMenuItem
            className='bg-transparent'
            icon={<ExtendRightIcon className='h-4 w-4' />}
            onClick={() => {
              setState(makeBeat1(anchorBeats));
              onClose();
            }}
          >
            <ContextMenuItemMainText>Make Beat 1</ContextMenuItemMainText>
          </ContextMenuItem>
        )}
        {canHeal && (
          <ContextMenuItem
            className='bg-transparent'
            icon={<RecentPromptsIcon className='h-4 w-4' />}
            onClick={() => {
              setState(healEdits(clipsById));
              onClose();
            }}
          >
            <ContextMenuItemMainText>Heal Edits</ContextMenuItemMainText>
          </ContextMenuItem>
        )}
        {canCrop && (
          <ContextMenuItem
            className='bg-transparent'
            icon={<ScissorsIcon className='h-4 w-4' />}
            onClick={() => {
              setState(
                combineActions(
                  deleteRanges(
                    [
                      {
                        startBeats: -Infinity,
                        endBeats: selectionStartBeats,
                      },
                      {
                        startBeats: selectionEndBeats,
                        endBeats: Infinity,
                      },
                    ],
                    trackIds
                  ),
                  moveSelection(Math.round(-1 * selectionStartBeats)),
                  assignMetadata({
                    usedCrop: true,
                  })
                )
              );
              onClose();
              logWebUserEvent({
                actionName: 'EditV3Crop',
                context: {
                  editSessionId: editSessionId,
                  editingClipId: editClipId || 'MISSING_EDIT_CLIP_ID',
                  trigger: eventTrigger,
                },
              });
            }}
          >
            <ContextMenuItemMainText>Crop</ContextMenuItemMainText>
          </ContextMenuItem>
        )}
        {canSplit && !isReadOnlyMode && (
          <ContextMenuItem
            className='bg-transparent'
            icon={<SectionIcon className='h-4 w-4' />}
            onClick={() => {
              setState(splitTimelineSelection);
              onClose();
              logWebUserEvent({
                actionName: 'EditV3Split',
                context: {
                  editSessionId: editSessionId,
                  editingClipId: editClipId || 'MISSING_EDIT_CLIP_ID',
                  trigger: eventTrigger,
                },
              });
            }}
          >
            <ContextMenuItemMainText>Split</ContextMenuItemMainText>
            <ContextMenuItemSubtext>{ctrlStr}+E</ContextMenuItemSubtext>
          </ContextMenuItem>
        )}
        {hasZeroLengthSelection && editMode && (
          <ContextMenuItem
            className='bg-transparent'
            icon={<EmbedIcon className='h-4 w-4' />}
            onClick={() => {
              setState(
                combineActions(
                  splitTimelineSelection,
                  setGapSize(32),
                  assignMetadata({ usedCreateSection: true })
                )
              );
              onClose();
              logWebUserEvent({
                actionName: 'EditV3CreateSectionStarted',
                context: {
                  editSessionId: editSessionId,
                  editingClipId: editClipId || 'MISSING_EDIT_CLIP_ID',
                  beats: selectionStartBeats,
                  seconds: selectionStartSeconds,
                  trigger: eventTrigger,
                },
              });
            }}
          >
            <ContextMenuItemMainText>Create Section</ContextMenuItemMainText>
          </ContextMenuItem>
        )}
      </ContextMenuGroup>
      {focusedClip && (
        <ContextMenuGroup>
          <Tooltip
            label={`Synchronize any corrections you've made to this clip's lyrics via its song page or details form.`}
            placement='right'
          >
            <ContextMenuItem
              className='bg-transparent'
              disabled={reloadingLyrics}
              icon={
                reloadingLyrics ? (
                  <SpinnerSVG className='h-4 w-4' />
                ) : (
                  <MicrophoneIcon className='h-4 w-4' />
                )
              }
              onClick={async () => {
                setReloadingLyrics(true);
                try {
                  await reloadClipLyrics(focusedClip.id);
                } finally {
                  setReloadingLyrics(false);
                }
                logWebUserEvent({
                  actionName: 'EditV3ReloadLyrics',
                  context: {
                    editSessionId: editSessionId,
                    editingClipId: editClipId || 'MISSING_EDIT_CLIP_ID',
                    clipId: focusedClip.id,
                    trigger: eventTrigger,
                  },
                });
              }}
            >
              <ContextMenuItemMainText>Reload Lyrics</ContextMenuItemMainText>
            </ContextMenuItem>
          </Tooltip>
        </ContextMenuGroup>
      )}
    </>
  );
}
