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

import { useStores } from '@/app/(root)/AppProviders';
import Button, { ButtonShape, ButtonVariant } from '@/components/button/Button';
import SpinnerSVG from '@/components/svg/SpinnerSVG';
import { Tooltip } from '@/components/tooltip/Tooltip';
import { useContextSelector } from '@/hooks/useContextSelector';
import { CaretLeftIcon, CaretRightIcon } from '@/icons';
import modulo from '@/utils/modulo';

import StudioContext, { hasExtendTask } from '../StudioContext';
import { AlternatesSection } from '../StudioLibrary';
import { fetchClipArrangementPackage } from '../fetchClipArrangementPackage';
import {
  applyInsertionSpec,
  getClosestInsertionSpec,
  getTimelineInsertionSpecs,
} from '../insertion';
import { getFocusedStudioClip } from '../selectors';
import {
  AlternateControllerContext,
  getAlternateClipId,
} from '../useAlternateController';
import useStudioClipArrangementPackage from '../useStudioClipArrangementPackage';
import { ContextMenuButton, InteractionPill } from './interactionComponents';

export default observer(function RegenerateInteractionUI() {
  const alternateClips = useContextSelector(
    AlternateControllerContext,
    (context) => context.list ?? []
  );
  const nonPreviewState = useContextSelector(
    StudioContext,
    (context) => context.nonPreviewState
  );
  const oneTrackMode = useContextSelector(
    StudioContext,
    (context) => context.oneTrackMode
  );
  const magnetMode = useContextSelector(
    StudioContext,
    (context) => context.magnetMode
  );
  const previewOrSetState = useContextSelector(
    StudioContext,
    (context) => context.previewOrSetState
  );
  const focusedStudioClip = useContextSelector(StudioContext, (context) =>
    getFocusedStudioClip(context.nonPreviewState)
  );
  const mode = useContextSelector(StudioContext, (context) => context.mode);
  const { clips: clipsStore } = useStores();

  const alternateClipIds = useMemo(() => {
    return uniq(alternateClips.map(getAlternateClipId));
  }, [alternateClips]);

  const [loadingAlternate, setLoadingAlternate] = useState(false);
  const loadingSomething = loadingAlternate;

  const focusedClipArrangementPackage =
    useStudioClipArrangementPackage(focusedStudioClip);

  const arrangeAlternate = useCallback(
    async (offset: number) => {
      if (!focusedStudioClip) {
        console.error('No focused clip!');
        return;
      }

      setLoadingAlternate(true);

      try {
        const clipId =
          alternateClipIds[
            modulo(
              alternateClipIds.findIndex(
                (a) => a === focusedStudioClip.clipId
              ) + offset,
              alternateClipIds.length
            )
          ];

        if (clipId) {
          const clipArrangementPackage = await fetchClipArrangementPackage(
            clipsStore,
            clipId
          );
          const insertionSpecs = getTimelineInsertionSpecs(
            clipArrangementPackage,
            focusedClipArrangementPackage,
            nonPreviewState,
            !hasExtendTask(clipArrangementPackage.clip) && mode === 'edit', // TEMPORARY. disable when tempo alignment for inserts works better.
            oneTrackMode && magnetMode
          );
          const insertionSpec = getClosestInsertionSpec(
            insertionSpecs,
            nonPreviewState.selection.focusedTrackId,
            (nonPreviewState.selection.anchorBeats +
              nonPreviewState.selection.focusBeats) /
              2
          );
          if (insertionSpec) {
            previewOrSetState(clipId, applyInsertionSpec(insertionSpec), true);
          } else {
            console.error('No insertion spec found!');
            debugger;
          }
        } else {
          console.error('No alternate found!');
          debugger;
        }
      } catch (e) {
        console.error('Error loading alternate:', e);
      } finally {
        setLoadingAlternate(false);
      }
    },
    [
      focusedStudioClip,
      focusedClipArrangementPackage,
      alternateClipIds,
      oneTrackMode,
      magnetMode,
    ]
  );

  const hasPrevAlternate = useMemo(() => {
    return (
      focusedStudioClip &&
      alternateClipIds.length > 1 &&
      alternateClipIds.findIndex((a) => a === focusedStudioClip?.clipId) > 0
    );
  }, [alternateClipIds, focusedStudioClip]);

  const hasNextAlternate = useMemo(() => {
    return (
      focusedStudioClip &&
      alternateClipIds.length > 1 &&
      alternateClipIds.findIndex((a) => a === focusedStudioClip?.clipId) <
        alternateClipIds.length - 1
    );
  }, [alternateClipIds, focusedStudioClip]);

  return (
    <div className='flex w-[350px] flex-col gap-2' data-block-scroll>
      <div className='flex items-center justify-center gap-2'>
        <InteractionPill>
          <Tooltip
            label={hasPrevAlternate ? 'Previous Version' : ''}
            placement='left'
            key='prev-alternate'
          >
            <Button
              icon={
                loadingSomething ? (
                  <SpinnerSVG />
                ) : (
                  <CaretLeftIcon className='h-5 w-5' />
                )
              }
              shape={ButtonShape.Pill}
              disabled={loadingSomething || !hasPrevAlternate}
              variant={
                hasPrevAlternate
                  ? ButtonVariant.Standard
                  : ButtonVariant.Tertiary
              }
              onClick={() => {
                arrangeAlternate(-1);
              }}
            />
          </Tooltip>
          <Tooltip
            label={hasNextAlternate ? 'Next Version' : ''}
            placement='right'
            key='next-alternate'
          >
            <Button
              disabled={loadingSomething || !hasNextAlternate}
              icon={
                loadingSomething ? (
                  <SpinnerSVG />
                ) : (
                  <CaretRightIcon className='h-5 w-5' />
                )
              }
              shape={ButtonShape.Pill}
              variant={
                hasNextAlternate
                  ? ButtonVariant.Standard
                  : ButtonVariant.Tertiary
              }
              onClick={() => {
                arrangeAlternate(1);
              }}
            />
          </Tooltip>
        </InteractionPill>
        <InteractionPill>
          <ContextMenuButton />
        </InteractionPill>
      </div>
      <AlternatesSection alternates={alternateClipIds} />
    </div>
  );
});
