import styled from '@emotion/styled';
import { getValueAtBeatsCached } from '@suno/studiokit/timeMapping';
import { useAnimationFrame } from 'framer-motion';
import { useCallback, useMemo, useRef, useState } from 'react';

import { useContextSelector } from '@/hooks/useContextSelector';
import {
  CheckboxIcon,
  CheckboxOutlineIcon,
  GearIcon,
  MinusIcon,
  PlusIcon,
} from '@/icons';

import Button, { ButtonSize, ButtonVariant } from '../button/Button';
import {
  ContextMenuItem,
  ContextMenuItemSubtext,
  ContextMenuTrigger,
} from '../contextMenu/ContextMenu';
import { Tooltip } from '../tooltip/Tooltip';
import StudioContext from './StudioContext';
import {
  activateFollowTrackTiming,
  activateManualTiming,
} from './actions/setTiming';
import {
  getDerivedTiming,
  getSongEndBeats,
  getSongStartBeats,
  getTracks,
} from './selectors';

const TempoInput = styled.input`
  width: 50px;
  border: none;
  font-size: 20px;
  margin-top: -3px;
  font-family: 'Input Sans', monospace;
  background-color: transparent;
  text-align: center;
  color: ${({ disabled }) =>
    disabled
      ? 'var(--color-foreground-inactive)'
      : 'var(--color-foreground-primary)'};
  font-weight: 100;
  // hide the up/down arrows
  -moz-appearance: textfield;
  appearance: textfield;
  &::-webkit-outer-spin-button,
  &::-webkit-inner-spin-button {
    -webkit-appearance: none;
    margin: 0;
  }
`;

const validateBPM = (bpm: number) => {
  return !isNaN(bpm) && bpm >= 10 && bpm <= 999;
};

const BasicTempoControl = () => {
  const setState = useContextSelector(
    StudioContext,
    (context) => context.setState
  );
  const playbackController = useContextSelector(
    StudioContext,
    (context) => context.playbackController
  );
  const baseTiming = useContextSelector(
    StudioContext,
    (context) => context.state.timing
  );
  const derivedTiming = useContextSelector(StudioContext, (context) =>
    getDerivedTiming(context.state)
  );
  const songStartBeats = useContextSelector(StudioContext, (context) =>
    getSongStartBeats(context.state)
  );
  const songEndBeats = useContextSelector(StudioContext, (context) =>
    getSongEndBeats(context.state)
  );
  const firstTrackId = useContextSelector(
    StudioContext,
    (context) => getTracks(context.state)[0]?.id
  );
  const [bpm, setBpm] = useState(Math.round(derivedTiming.bps));
  const lastBPMRef = useRef<number>(bpm);
  const boundedAutomationPoints = useMemo(() => {
    return [
      {
        beats: songStartBeats,
        value: derivedTiming.bpsAutomation[0]?.value ?? derivedTiming.bps,
        curve: 0,
      },
      ...derivedTiming.bpsAutomation,
      {
        beats: songEndBeats,
        value:
          derivedTiming.bpsAutomation[derivedTiming.bpsAutomation.length - 1]
            ?.value ?? derivedTiming.bps,
        curve: 0,
      },
    ];
  }, [derivedTiming.bps, derivedTiming.bpsAutomation]);

  useAnimationFrame(() => {
    const currentBeats = playbackController.getCurrentBeats();
    const currentBPM = boundedAutomationPoints.length
      ? getValueAtBeatsCached(currentBeats, boundedAutomationPoints)[0] * 60
      : derivedTiming.bps * 60;
    const newBPM = Math.round(currentBPM);

    if (newBPM !== lastBPMRef.current) {
      lastBPMRef.current = newBPM;
      setBpm(newBPM);
    }
  });

  const submitBPM = useCallback(
    (bpm: number) => {
      setBpm(bpm);
      setState(activateManualTiming(bpm / 60));
    },
    [setBpm, setState, baseTiming]
  );

  return (
    <div className='h-[44px] rounded-md p-0 pr-1'>
      <div className='flex items-center'>
        <ContextMenuTrigger
          placement='top-right'
          ButtonComponent={(props) => (
            <Button
              size={ButtonSize.Medium}
              variant={ButtonVariant.Tertiary}
              icon={<GearIcon className='m-1 h-4 w-4' />}
              {...props}
            />
          )}
          ContentsComponent={() => (
            <>
              <ContextMenuItem
                keepMenusOpen
                disabled={!firstTrackId}
                icon={
                  baseTiming.type === 'follow-track' ? (
                    <CheckboxIcon className='m-1 h-4 w-4' />
                  ) : (
                    <CheckboxOutlineIcon className='m-1 h-4 w-4' />
                  )
                }
                onClick={() => {
                  setState(activateFollowTrackTiming(firstTrackId));
                }}
              >
                Follow Track{' '}
                <ContextMenuItemSubtext>(May Fluctuate)</ContextMenuItemSubtext>
              </ContextMenuItem>

              <ContextMenuItem
                keepMenusOpen
                icon={
                  baseTiming.type === 'manual' ? (
                    <CheckboxIcon className='m-1 h-4 w-4' />
                  ) : (
                    <CheckboxOutlineIcon className='m-1 h-4 w-4' />
                  )
                }
                onClick={() => {
                  setState(activateManualTiming());
                }}
              >
                Manual BPM{' '}
                <ContextMenuItemSubtext>(Consistent)</ContextMenuItemSubtext>
              </ContextMenuItem>
            </>
          )}
        />
        <Tooltip
          label={
            baseTiming.type === 'follow-track'
              ? 'Tempo is in "Follow Track" mode, click the gear to change.'
              : ''
          }
        >
          <TempoInput
            type='number'
            value={bpm}
            disabled={baseTiming.type === 'follow-track'}
            onChange={(e) => {
              const bpm = Number(e.currentTarget.value);
              setBpm(bpm);
            }}
            onBlur={(e) => {
              const bpm = Number(e.currentTarget.value);
              if (!validateBPM(bpm)) {
                setBpm(lastBPMRef.current);
              } else {
                submitBPM(bpm);
              }
            }}
            onKeyDown={(e) => {
              if (e.key === 'Enter') {
                const bpm = Number(e.currentTarget.value);
                if (!validateBPM(bpm)) {
                  setBpm(lastBPMRef.current);
                } else {
                  submitBPM(bpm);
                }
              } else if (e.key === 'Escape') {
                setBpm(lastBPMRef.current);
              }
            }}
          />
        </Tooltip>
        <Tooltip
          label={
            baseTiming.type === 'follow-track'
              ? 'Tempo is in "Follow Track" mode, click the gear to change.'
              : ''
          }
        >
          <div className='ml-2 flex flex-col items-center justify-center'>
            <Tooltip
              openDelay={500}
              placement='right'
              label={baseTiming.type === 'follow-track' ? '' : 'Increase Tempo'}
            >
              <Button
                variant={ButtonVariant.Tertiary}
                size={ButtonSize.Micro}
                icon={<PlusIcon className='mt-0.5' />}
                disabled={baseTiming.type === 'follow-track'}
                onClick={() => {
                  submitBPM(Math.min(bpm + 1, 999));
                }}
              />
            </Tooltip>
            <Tooltip
              openDelay={500}
              placement='right'
              label={baseTiming.type === 'follow-track' ? '' : 'Decrease Tempo'}
            >
              <Button
                variant={ButtonVariant.Tertiary}
                size={ButtonSize.Micro}
                icon={<MinusIcon className='mt-0.5' />}
                disabled={baseTiming.type === 'follow-track'}
                onClick={() => {
                  submitBPM(Math.max(bpm - 1, 10));
                }}
              />
            </Tooltip>
          </div>
        </Tooltip>
      </div>
    </div>
  );
};

export default BasicTempoControl;
