/* eslint jsx-a11y/click-events-have-key-events: warn */

/* eslint jsx-a11y/no-static-element-interactions: warn */
import {
  dbToMultiplier,
  multiplierToDb,
} from '@suno/studiokit/audioEngineeringUtils';
import { getWarpEnabledAndPopulated } from '@suno/studiokit/warpUtils';
import { useCallback, useEffect, useRef, useState } from 'react';

import { useContextSelector } from '@/hooks/useContextSelector';
import {
  CaretDownIcon,
  CaretRightIcon,
  CaretUpIcon,
  CheckboxIcon,
  CheckboxOutlineIcon,
  MinusIcon,
  PlusIcon,
} from '@/icons';
import sanitizeNumber from '@/utils/sanitizeNumber';
import snap from '@/utils/snap';

import Button, { ButtonShape, ButtonVariant } from '../button/Button';
import {
  ContextMenuItem,
  ContextMenuTrigger,
} from '../contextMenu/ContextMenu';
import StudioContext from '../studio/StudioContext';
import StudioHorizontalFader, {
  dbToZeroOne,
  multiplierToScaledZeroOne,
  scaledZeroOneToMultiplier,
} from '../studio/StudioHorizontalFader';
import { combineActions } from '../studio/actions/combineActions';
import updateClip from '../studio/actions/updateClip';
import updateClipSpeed from '../studio/actions/updateClipSpeed';
import updateClipTransposition from '../studio/actions/updateClipTransposition';
import { updateSelectedClips } from '../studio/actions/updateClips';
import { updateSelectedTracksAndTakeLanes } from '../studio/actions/updateTracksAndTakeLanes';
import { inFlightDragKeys } from '../studio/hooks/useInFlightDrags';
import {
  getSelectedClips,
  getSelectionEndBeats,
  getSelectionStartBeats,
} from '../studio/selectors';
import { StudioClip, StudioProjectState } from '../studio/types';
import { FALLBACK_METER_VALUE } from '../studio/useStudioPlaybackController';
import SpinnerSVG from '../svg/SpinnerSVG';
import ColorPicker from './ColorPicker';
import { Subsection } from './common';

export const TRANSPOSE_MAXIMUM_SEMITONES = 24;
export const TRANSPOSE_MINIMUM_SEMITONES = -24;
export const SPEED_MAXIMUM = 128;
export const SPEED_MINIMUM = 1 / 128;

interface EditableTranspositionDisplayProps {
  transpositionStr: string;
  currentTransposition: number;
  onUpdateTransposition: (delta: number) => void;
}

function EditableTranspositionDisplay({
  transpositionStr,
  currentTransposition,
  onUpdateTransposition,
}: EditableTranspositionDisplayProps) {
  const [isEditing, setIsEditing] = useState(false);
  const [inputValue, setInputValue] = useState('');
  const inputRef = useRef<HTMLInputElement>(null);

  const handleClick = () => {
    setIsEditing(true);
    setInputValue(transpositionStr);
    // Focus the input after it renders
    setTimeout(() => {
      inputRef.current?.focus();
      inputRef.current?.select();
    }, 0);
  };

  const handleSave = () => {
    const trimmedValue = inputValue.trim();
    if (trimmedValue === '') {
      setIsEditing(false);
      return;
    }

    // Remove leading + if present
    const cleanValue = trimmedValue.replace(/^\+/, '');
    const parsedValue = parseFloat(cleanValue);

    if (isNaN(parsedValue)) {
      // Invalid number, revert to original
      setIsEditing(false);
      return;
    }

    // Clamp to valid range
    const clampedValue = Math.max(
      TRANSPOSE_MINIMUM_SEMITONES,
      Math.min(TRANSPOSE_MAXIMUM_SEMITONES, parsedValue)
    );

    // Calculate the difference
    const delta = clampedValue - currentTransposition;

    if (delta !== 0) {
      onUpdateTransposition(delta);
    }

    setIsEditing(false);
  };

  const handleKeyDown = (e: React.KeyboardEvent) => {
    if (e.key === 'Enter') {
      handleSave();
    } else if (e.key === 'Escape') {
      setIsEditing(false);
    }
  };

  const handleBlur = () => {
    handleSave();
  };

  if (isEditing) {
    return (
      <input
        ref={inputRef}
        type='text'
        value={inputValue}
        onChange={(e) => setInputValue(e.target.value)}
        onKeyDown={handleKeyDown}
        onBlur={handleBlur}
        className='flex w-10 items-center justify-center border-none bg-transparent text-center text-lg outline-none'
      />
    );
  }

  return (
    <div
      className='flex min-w-10 cursor-pointer items-center justify-center rounded px-1 text-lg hover:bg-background-glass-thin'
      onClick={handleClick}
    >
      {transpositionStr}
    </div>
  );
}

export default function StudioClipSettingsSubsection({
  studioClips,
  keepOpen = false,
}: {
  studioClips: StudioClip[];
  keepOpen?: boolean;
}) {
  const [expanded, setExpanded] = useState(true);

  const setState = useContextSelector(StudioContext, (ctx) => ctx.setState);
  const stateRef = useContextSelector(StudioContext, (ctx) => ctx.stateRef);
  const setClipAmplitude = useContextSelector(
    StudioContext,
    (ctx) => ctx.playbackController.setClipAmplitude
  );
  const cutClips = useContextSelector(StudioContext, (ctx) => ctx.cutClips);
  const selectionStartBeats = useContextSelector(StudioContext, (ctx) =>
    getSelectionStartBeats(ctx.state)
  );
  const selectionEndBeats = useContextSelector(StudioContext, (ctx) =>
    getSelectionEndBeats(ctx.state)
  );

  const inFlightDrags = useContextSelector(
    StudioContext,
    (ctx) => ctx.inFlightDrags
  );

  // Handle both single and multiple clips
  const primaryClip = studioClips[0];

  const [localAmplitude, setLocalAmplitude] = useState(
    primaryClip?.amplitude || 1
  );

  useEffect(() => {
    setLocalAmplitude(primaryClip?.amplitude || 1);
  }, [primaryClip?.amplitude]);

  const transpositionStr = primaryClip
    ? primaryClip.transposition > 0
      ? `+${primaryClip.transposition}`
      : primaryClip.transposition.toString()
    : '0';

  const speedStr = `×${snap(primaryClip?.warp?.speed ?? 1, 0.001)}`;

  const selectedClipsRef = useRef<StudioClip[]>(studioClips);

  useEffect(() => {
    if (!inFlightDrags.isAnyDragging) selectedClipsRef.current = studioClips;
  }, [studioClips, inFlightDrags.isAnyDragging]);

  const cutSelection = useCallback(
    (updateFn?: (state: StudioProjectState) => StudioProjectState) => {
      const withCut = updateSelectedTracksAndTakeLanes((t) => ({
        ...t,
        clips: cutClips(t.clips, selectionStartBeats, selectionEndBeats),
      }))(stateRef.current);
      const withUpdate = updateFn ? updateFn(withCut) : withCut;
      selectedClipsRef.current = getSelectedClips(withUpdate);
      setState(withUpdate);
      return selectedClipsRef.current;
    },
    [
      cutClips,
      selectionStartBeats,
      selectionEndBeats,
      setState,
      stateRef,
      selectedClipsRef,
    ]
  );

  const canToggleWarp =
    !primaryClip?.warp.awaitingAnalysis ||
    Object.keys(primaryClip?.warp.markers).length > 1;

  return (
    <Subsection className='flex flex-col gap-4' style={{ fontSize: 14 }}>
      <div className='flex h-8 flex-row items-center justify-between gap-2'>
        {!keepOpen && (
          <Button
            onClick={() => setExpanded(!expanded)}
            variant={ButtonVariant.Tertiary}
            shape={ButtonShape.Pill}
            icon={
              expanded ? (
                <CaretUpIcon className='h-6 w-6' />
              ) : (
                <CaretRightIcon className='h-6 w-6' />
              )
            }
            className='-ml-2 pl-3 text-sm'
          >
            Clip Settings
          </Button>
        )}
        {keepOpen && <div className='flex-1'>Color</div>}
        <ColorPicker
          color={primaryClip?.color || '#ffffff22'}
          setColor={(color) =>
            cutSelection(updateSelectedClips((c) => ({ ...c, color })))
          }
        />
      </div>
      {(expanded || keepOpen) && (
        <>
          <div className='flex h-8 flex-row items-center justify-between gap-2'>
            Tempo
            <ContextMenuTrigger
              ButtonComponent={(props) => (
                <Button
                  disabled={!canToggleWarp}
                  {...props}
                  shape={ButtonShape.Pill}
                  className='py-1 pl-2 text-sm'
                  icon={
                    !canToggleWarp ? (
                      <SpinnerSVG className='h-4 w-4' />
                    ) : (
                      CaretDownIcon
                    )
                  }
                >
                  {!canToggleWarp
                    ? 'Analyzing...'
                    : primaryClip?.warp.enabled
                      ? 'On Beat'
                      : 'Original'}
                </Button>
              )}
              ContentsComponent={() => (
                <>
                  <ContextMenuItem
                    className={
                      primaryClip?.warp.enabled
                        ? 'bg-background-glass-thin'
                        : ''
                    }
                    icon={
                      primaryClip?.warp.enabled ? (
                        <CheckboxIcon />
                      ) : (
                        <CheckboxOutlineIcon />
                      )
                    }
                    onClick={() => {
                      cutSelection(
                        updateSelectedClips((c) => ({
                          ...c,
                          warp: {
                            ...c.warp,
                            enabled: true,
                          },
                        }))
                      );
                    }}
                  >
                    On Beat
                  </ContextMenuItem>
                  <ContextMenuItem
                    className={
                      !primaryClip?.warp.enabled
                        ? 'bg-background-glass-thin'
                        : ''
                    }
                    icon={
                      !primaryClip?.warp.enabled ? (
                        <CheckboxIcon />
                      ) : (
                        <CheckboxOutlineIcon />
                      )
                    }
                    onClick={() => {
                      cutSelection(
                        updateSelectedClips((c) => ({
                          ...c,
                          warp: {
                            ...c.warp,
                            enabled: false,
                          },
                        }))
                      );
                    }}
                  >
                    Original
                  </ContextMenuItem>
                </>
              )}
            />
          </div>
          <div className='flex h-8 flex-row items-center justify-between gap-2'>
            Transpose
            <div className='flex flex-row items-center gap-2'>
              <Button
                variant={ButtonVariant.LightGlass}
                shape={ButtonShape.Pill}
                icon={<MinusIcon />}
                onClick={() => {
                  cutSelection((state) => {
                    const selectedClips = getSelectedClips(state);
                    return updateClipTransposition(
                      selectedClips.map((c) => c.id),
                      -1,
                      true
                    )(state);
                  });
                }}
              />
              <EditableTranspositionDisplay
                transpositionStr={transpositionStr}
                currentTransposition={primaryClip?.transposition || 0}
                onUpdateTransposition={(delta) => {
                  cutSelection((state) => {
                    const selectedClips = getSelectedClips(state);
                    return updateClipTransposition(
                      selectedClips.map((c) => c.id),
                      delta,
                      true
                    )(state);
                  });
                }}
              />
              <Button
                variant={ButtonVariant.LightGlass}
                shape={ButtonShape.Pill}
                icon={<PlusIcon />}
                onClick={() => {
                  cutSelection((state) => {
                    const selectedClips = getSelectedClips(state);
                    return updateClipTransposition(
                      selectedClips.map((c) => c.id),
                      1,
                      true
                    )(state);
                  });
                }}
              />
            </div>
          </div>
          <div className='flex h-8 flex-row items-center justify-between gap-2'>
            Speed
            <div
              className={`flex flex-row items-center gap-2 ${
                primaryClip && getWarpEnabledAndPopulated(primaryClip.warp)
                  ? ''
                  : 'opacity-50'
              }`}
            >
              <Button
                variant={ButtonVariant.LightGlass}
                shape={ButtonShape.Pill}
                icon={<MinusIcon />}
                onClick={() => {
                  cutSelection(
                    updateClipSpeed(
                      studioClips.map((c) => c.id),
                      1 / 2,
                      true
                    )
                  );
                }}
              />
              <div className='flex min-w-10 items-center justify-center text-lg'>
                {speedStr}
              </div>

              <Button
                variant={ButtonVariant.LightGlass}
                shape={ButtonShape.Pill}
                icon={<PlusIcon />}
                onClick={() => {
                  cutSelection(
                    updateClipSpeed(
                      studioClips.map((c) => c.id),
                      2,
                      true
                    )
                  );
                }}
              />
            </div>
          </div>
          <div>
            <div className='flex h-8 flex-row items-center justify-between gap-2'>
              Clip Volume
              <span className='ml-2 text-sm text-foreground-tertiary'>
                {localAmplitude > 1 ? '+' : ''}
                {multiplierToDb(localAmplitude).toFixed(1)} dB
              </span>
            </div>
            <div
              className='relative h-[20px]'
              onDoubleClick={() => {
                const selectedClips = cutSelection(
                  updateSelectedClips((c) => ({
                    ...c,
                    amplitude: 1,
                  }))
                );
                selectedClips.forEach((clip) => {
                  setClipAmplitude(clip.id, 1);
                });
                setLocalAmplitude(1);
              }}
            >
              <StudioHorizontalFader
                color={primaryClip?.color || '#ffffff'}
                value={multiplierToScaledZeroOne(primaryClip?.amplitude || 1)}
                defaultValue={dbToZeroOne(0)}
                anchorValue={dbToZeroOne(0)}
                getMeterValue={() => FALLBACK_METER_VALUE}
                onChange={(scaledZeroOne, isFirst) => {
                  const multiplier = sanitizeNumber(
                    scaledZeroOneToMultiplier(scaledZeroOne)
                  );
                  if (isFirst) {
                    cutSelection();
                  }
                  setLocalAmplitude(multiplier);
                  const primaryClipDb = multiplierToDb(
                    primaryClip?.amplitude || 1
                  );
                  const deltaDb = multiplierToDb(multiplier) - primaryClipDb;
                  selectedClipsRef.current.forEach((clip) => {
                    const originalClipDb = multiplierToDb(clip.amplitude);
                    const dbWithAmplitudeChange = originalClipDb + deltaDb;
                    const multiplierWithAmplitudeChange = dbToMultiplier(
                      dbWithAmplitudeChange
                    );
                    inFlightDrags.update(
                      inFlightDragKeys.clipGain(clip.id),
                      multiplierWithAmplitudeChange - clip.amplitude
                    );
                    setClipAmplitude(clip.id, multiplierWithAmplitudeChange);
                  });
                }}
                onCommit={(scaledZeroOne) => {
                  const multiplier = sanitizeNumber(
                    scaledZeroOneToMultiplier(scaledZeroOne)
                  );
                  inFlightDrags.finishAll();
                  setLocalAmplitude(multiplier);
                  const primaryClipDb = multiplierToDb(
                    primaryClip?.amplitude || 1
                  );
                  const deltaDb = multiplierToDb(multiplier) - primaryClipDb;
                  selectedClipsRef.current.forEach((clip) => {
                    const originalClipDb = multiplierToDb(clip.amplitude);
                    const dbWithAmplitudeChange = originalClipDb + deltaDb;
                    const multiplierWithAmplitudeChange = sanitizeNumber(
                      dbToMultiplier(dbWithAmplitudeChange)
                    );
                    setClipAmplitude(clip.id, multiplierWithAmplitudeChange);
                  });
                  setState(
                    combineActions(
                      ...selectedClipsRef.current.map((clip) =>
                        updateClip(clip.id, (prev) => {
                          const originalClipDb = multiplierToDb(clip.amplitude);
                          const dbWithAmplitudeChange =
                            originalClipDb + deltaDb;
                          const multiplierWithAmplitudeChange = sanitizeNumber(
                            dbToMultiplier(dbWithAmplitudeChange)
                          );

                          return {
                            ...prev,
                            amplitude: multiplierWithAmplitudeChange,
                          };
                        })
                      )
                    ) as any
                  );
                }}
              />
            </div>
          </div>
        </>
      )}
    </Subsection>
  );
}
