import styled from '@emotion/styled';
import fixStudioProjectState from '@suno/studiokit/projectState/fixStudioProjectState';
import { useSearchParams } from 'next/navigation';
import {
  Dispatch,
  Fragment,
  SetStateAction,
  memo,
  useCallback,
  useEffect,
  useRef,
  useState,
} from 'react';

import { HeaderContainer } from '@/app/(root)/create/createV2/componentsQ3/CreateHeader';
import { FocusedObjectContext } from '@/app/(root)/create/v2/useFocusedObject';
import {
  useContextSelector,
  useContextSelectorRef,
} from '@/hooks/useContextSelector';
import {
  ClipZoomFitIcon,
  ClipZoomInIcon,
  EditRedoIcon,
  EditUndoIcon,
  ExtendRightIcon,
  LockIcon,
  MoreVerticalIcon,
  ProjectIcon,
  SidebarRightIcon,
  TracksCollapseIcon,
  TracksExpandIcon,
} from '@/icons';
import { Clip } from '@/state/clipStore';
import { getClipTitle } from '@/utils/clip';
import ctrlStr from '@/utils/ctrlStr';

import Button, {
  ButtonShape,
  ButtonSize,
  ButtonVariant,
} from '../button/Button';
import { ResponsiveChild, useContainer } from '../containerQueries/components';
import {
  ContextMenuItem,
  ContextMenuTrigger,
} from '../contextMenu/ContextMenu';
import CanvasRenderer from '../edit2025/canvasRenderer/CanvasRenderer';
import ScrollCatcher from '../edit2025/canvasRenderer/ScrollCatcher';
import { CanvasRendererContainer } from '../edit2025/components';
import { AutomationPoint } from '../edit2025/types';
import Link from '../link/Link';
import {
  Divider,
  HorizontalPanelGroup,
  Panel,
  VerticalPanelGroup,
} from '../panelSystem/PanelSystem';
import { Tooltip } from '../tooltip/Tooltip';
import CloneProjectModal from './CloneProjectModal';
import ContextualBar from './ContextualBar/ContextualBar';
import { DSPModuleContext } from './DSPModuleContext';
import GhostTrackHeader from './GhostTrackHeader';
import { HintMessage } from './HintMessage';
import LoadingOverlay from './LoadingOverlay';
import PanelButton from './PanelButton';
import ProjectNameButton from './ProjectNameButton';
import SaveIndicator from './SaveIndicator';
import StudioContext from './StudioContext';
import StudioContextMenuContents from './StudioContextMenuContents';
import StudioExportMenu from './StudioExportMenu';
import StudioNotification from './StudioNotification';
import {
  HorizontalDivider,
  ThickGapVerticalDivider,
  VerticalDivider,
  oneFRPanelSize,
  studioTransportPanelSize,
  trackHeadersPanelSize,
} from './StudioPanelUtilities';
import { StudioProjectManagementContext } from './StudioProjectManagementContext';
import StudioTakeLaneHeader from './StudioTakeLaneHeader';
import StudioTimelineDroppable from './StudioTimelineDroppable';
import StudioTrackHeader, { TrackHeaderButton } from './StudioTrackHeader';
import StudioTransport from './StudioTransport';
import StudioVersionsPanel from './StudioVersionsPanel';
import setTrackHeight, {
  setAllTrackHeights,
  setTakeLaneHeight,
} from './actions/setTrackHeight';
import { getCanvasDebug } from './canvasDebug';
import makeClipCreationIntentRegions from './canvasRegions/makeClipCreationIntentRegions';
import makeClipGenerationWebglRegions from './canvasRegions/makeClipGenerationWebglRegions';
import makeGapRegions from './canvasRegions/makeGapRegions';
import makeRecordingClipRegions from './canvasRegions/makeRecordingClipRegions';
import { makeSectionBoundaryRegions } from './canvasRegions/makeSectionBoundaryRegions';
import makeSongFadeRegions from './canvasRegions/makeSongFadeRegions';
import makeStudioBaseRegion from './canvasRegions/makeStudioBaseRegion';
import makeStudioBeatGridRegion, {
  RULER_HEIGHT,
} from './canvasRegions/makeStudioBeatGridRegion';
import makeStudioContextWindowRegions from './canvasRegions/makeStudioContextWindowRegions';
import makeStudioDragPreviewRegions from './canvasRegions/makeStudioDragPreviewRegions';
import makeStudioIncludedContextArrangementsRegions from './canvasRegions/makeStudioIncludedContextArrangementsRegions';
import makeStudioPlayheadRegion from './canvasRegions/makeStudioPlayheadRegion';
import makeStudioScrollbarRegions from './canvasRegions/makeStudioScrollbarRegions';
import makeStudioSelectionRegions from './canvasRegions/makeStudioSelectionRegions';
import makeStudioTimelineLoopRegions from './canvasRegions/makeStudioTimelineLoopRegions';
import makeStudioWaveformRegionsV2 from './canvasRegions/makeStudioWaveformRegionsV2';
import makeStudioClipRegions from './canvasRegions/studioClipRegions/makeStudioClipRegionsV2';
import { defaultStudioTrackHeight, minimumTrackHeight } from './createTrack';
import makePanelSize from './makePanelSize';
import makeStudioFileDropPreviewRegions from './makeStudioFileDropPreviewRegions';
import {
  getDerivedTiming,
  getEarliestClipBeats,
  getLatestClipBeats,
} from './selectors';
import shouldShowTakeLanes from './shouldShowTakeLanes';
import { AFTER_LAST_TRACK, StudioProjectState } from './types';
import { getBufferKeys } from './uploadedClipCache';
import { StudioLayoutManagerContext } from './useStudioLayoutManager';

const TimelineWrapper = styled.div`
  height: 100%;
  overflow: hidden;
  position: relative;
  border-bottom: 1px solid var(--color-border-primary);
`;

const TransportWrapper = styled.div``;

const TrackSection = styled.div`
  background-color: var(--color-background-primary);
  display: grid;
  min-height: 0;
  height: 100%;
  grid-template-rows: 1fr;
  position: relative;
`;

const TrackHeaders = styled.div`
  height: 100%;
  min-height: 0;
  overflow-y: hidden;
  position: relative;
  z-index: 1;
  > * {
    min-height: 0;
  }
`;

const TrackTopControls = styled.div`
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding-left: 1px;
  padding-right: 4px;
  height: ${RULER_HEIGHT + 1}px;
  background-color: var(--color-background-primary);
  border-bottom: 1px solid var(--color-border-primary);
  z-index: 2;
`;

export default memo(function StudioTimeline({
  setExpanded,
  setFocusedObjectSidebarExpanded,
  focusedObjectSidebarExpanded,
  setProjectsModalOpen,
  learnModalOpen,
}: {
  setExpanded?: Dispatch<SetStateAction<boolean>>;
  setFocusedObjectSidebarExpanded?: Dispatch<SetStateAction<boolean>>;
  focusedObjectSidebarExpanded?: boolean;
  setProjectsModalOpen: Dispatch<SetStateAction<boolean>>;
  learnModalOpen: boolean;
}) {
  const { context: dspContext } = useContextSelector(
    DSPModuleContext,
    (ctx) => ctx
  );
  const studioContextRef = useContextSelectorRef(
    StudioContext,
    (context) => context
  );
  const scrollXRef = useContextSelector(
    StudioContext,
    (context) => context.timelineController.scrollXRef
  );
  const pxPerBeatRef = useContextSelector(
    StudioContext,
    (context) => context.timelineController.pxPerBeatRef
  );
  const handleScroll = useContextSelector(
    StudioContext,
    (context) => context.timelineController.handleScroll
  );
  const handleZoom = useContextSelector(
    StudioContext,
    (context) => context.timelineController.handleZoom
  );
  const loading = useContextSelector(
    StudioContext,
    (context) => context.loading
  );
  const trackHeaderListRef = useContextSelector(
    StudioContext,
    (context) => context.timelineController.trackHeaderListRef
  );
  const receiveWrapperRef = useContextSelector(
    StudioContext,
    (context) => context.timelineController.receiveWrapperRef
  );
  const frameCountRef = useContextSelector(
    StudioContext,
    (context) => context.timelineController.frameCountRef
  );
  const setContextMenuConfig = useContextSelector(
    StudioContext,
    (context) => context.timelineController.setContextMenuConfig
  );
  const wrapperRef = useContextSelector(
    StudioContext,
    (context) => context.timelineController.wrapperRef
  );
  const beatsToCanvasX = useContextSelector(
    StudioContext,
    (context) => context.beatsToCanvasX
  );
  const receiveTrackHeader = useContextSelector(
    StudioContext,
    (context) => context.timelineController.receiveTrackHeader
  );
  const afterLastTrackRef = useContextSelector(
    StudioContext,
    (context) => context.timelineController.afterLastTrackRef
  );
  const showBeats = useContextSelector(
    StudioContext,
    (context) => context.timelineController.showBeats
  );
  const tracks = useContextSelector(
    StudioContext,
    (context) => context.state.tracks
  );
  const setState = useContextSelector(
    StudioContext,
    (context) => context.setState
  );
  const canUndo = useContextSelector(
    StudioContext,
    (context) => context.canUndo
  );
  const canRedo = useContextSelector(
    StudioContext,
    (context) => context.canRedo
  );
  const undo = useContextSelector(StudioContext, (context) => context.undo);
  const redo = useContextSelector(StudioContext, (context) => context.redo);
  const isReadOnlyMode = useContextSelector(
    StudioContext,
    (context) => context.isReadOnlyMode
  );

  // Get the current project version ID for cloning
  const searchParams = useSearchParams();
  const projectVersionId = searchParams?.get('project_version_id');

  const cloneProject = useContextSelector(
    StudioProjectManagementContext,
    (context) => context.cloneProject
  );
  const currentVersionId = useContextSelector(
    StudioProjectManagementContext,
    (context) => context.currentVersionId
  );

  const isSaving = useContextSelector(
    StudioProjectManagementContext,
    (context) => context.isSaving
  );
  const receiveTimelineAndTrackHeadersContainer = useContextSelector(
    StudioContext,
    (context) =>
      context.timelineController.receiveTimelineAndTrackHeadersContainer
  );
  const webGLCache = useContextSelector(
    StudioContext,
    (context) => context.webGLCache
  );
  const earliestClipBeats = useContextSelector(StudioContext, (context) =>
    getEarliestClipBeats(context.state)
  );
  const latestClipBeats = useContextSelector(StudioContext, (context) =>
    getLatestClipBeats(context.state)
  );
  const projectIsEmpty = useContextSelector(
    StudioContext,
    (context) =>
      context.state.tracks.length === 0 ||
      context.state.tracks.every(
        (t) =>
          t.clips.length === 0 && t.takeLanes.every((t) => t.clips.length === 0)
      )
  );
  const projectWasEmpty = useRef(projectIsEmpty);
  useEffect(() => {
    if (projectWasEmpty.current && !projectIsEmpty) {
      showBeats(earliestClipBeats, latestClipBeats, true);
    }
    projectWasEmpty.current = projectIsEmpty;
  }, [projectIsEmpty, earliestClipBeats, latestClipBeats, showBeats]);

  const trackListRef = useRef<HTMLDivElement>(null);

  useEffect(() => {
    frameCountRef.current++;
  }, [frameCountRef]);

  useEffect(() => {
    webGLCache.current.renderers.clear();
    webGLCache.current.programs.clear();
  }, [webGLCache]);

  const getCanvasRegions = useCallback(() => {
    const wrapper = wrapperRef.current;
    const trackList = trackListRef.current;

    if (!wrapper || !trackList) {
      return [];
    }

    const studioContext = studioContextRef.current;

    const { lines, shadedRegions, topSection } =
      makeStudioBeatGridRegion(studioContext);

    let lastAutomationPointX = -Infinity;
    const {
      belowGrid: belowGridClipRegions,
      belowGridLifted: belowGridLiftedClipRegions,
      aboveSelection: aboveSelectionClipRegions,
      belowSelection: belowSelectionClipRegions,
      belowSelectionLifted: belowSelectionLiftedClipRegions,
      previewClipRegions: previewClipRegions,
    } = makeStudioClipRegions(studioContext);

    const {
      aboveSelectionRegions: aboveSelectionGapRegions,
      belowSelectionRegions: belowSelectionGapRegions,
    } = makeGapRegions(studioContext);

    const {
      aboveRulerRegions: aboveRulerSelectionRegions,
      belowRulerRegions: belowRulerSelectionRegions,
    } = makeStudioSelectionRegions(studioContext);

    const timing = getDerivedTiming(studioContext.state);

    return [
      makeStudioBaseRegion(studioContext),
      shadedRegions,
      ...belowGridClipRegions,
      ...belowGridLiftedClipRegions,
      lines,
      ...(studioContext.mode === 'edit' ? belowSelectionGapRegions : []),
      ...belowSelectionClipRegions,
      ...belowSelectionLiftedClipRegions,
      ...(studioContext.oneTrackMode ? makeSongFadeRegions(studioContext) : []),
      ...(studioContext.mode === 'edit'
        ? makeSectionBoundaryRegions(studioContext)
        : []),
      ...makeStudioWaveformRegionsV2(dspContext, studioContext),
      ...makeStudioIncludedContextArrangementsRegions(studioContext),
      ...makeClipCreationIntentRegions(studioContext),
      ...belowRulerSelectionRegions,
      ...aboveSelectionClipRegions,
      ...(studioContext.mode === 'edit' ? aboveSelectionGapRegions : []),
      ...makeStudioDragPreviewRegions(studioContext),
      ...makeStudioScrollbarRegions(studioContext),
      ...makeClipGenerationWebglRegions(studioContext),
      ...previewClipRegions,
      ...makeStudioFileDropPreviewRegions(studioContext),
      ...makeRecordingClipRegions(studioContext),
      topSection,
      ...makeStudioContextWindowRegions(studioContext),
      ...aboveRulerSelectionRegions,
      ...makeStudioTimelineLoopRegions(studioContext),
      makeStudioPlayheadRegion(studioContext),
      ...timing.bpsAutomation.map((point: AutomationPoint) => ({
        render: (ctx: CanvasRenderingContext2D) => {
          if (!getCanvasDebug()) return;
          const minBPM = timing.bpsAutomation.reduce(
            (min: number, point: AutomationPoint) => Math.min(min, point.value),
            Infinity
          );
          const maxBPM = timing.bpsAutomation.reduce(
            (max: number, point: AutomationPoint) => Math.max(max, point.value),
            -Infinity
          );
          ctx.fillStyle = 'white';
          const x = beatsToCanvasX(point.beats);
          const height = (30 * (point.value - minBPM)) / (maxBPM - minBPM);
          const y =
            studioContext.timelineController.getWrapperRect().height - height;
          ctx.fillRect(x, y, 3, height);
          if (x > lastAutomationPointX + 50) {
            ctx.fillText((point.value * 60).toFixed(0) + 'BPM', x, y - 10);
            lastAutomationPointX = x;
          }
        },
      })),
    ];
  }, [dspContext, studioContextRef, beatsToCanvasX, wrapperRef]);

  const receiveScroll = useCallback(
    (x: number, y: number) => {
      handleScroll(x, y);
      trackListRef.current?.scrollBy(0, y);
    },
    [handleScroll]
  );

  const [cloneProjectModalOpen, setCloneProjectModalOpen] = useState(false);

  const [renderedClip, setRenderedClip] = useState<Clip | null>(null);
  const [versionsVisible, setVersionsVisible] = useState(false);
  const [currentRestoreData, setCurrentRestoreData] = useState<{
    versionId: string;
    state: any;
  } | null>(null);

  const isPreviewingVersion = useContextSelector(
    StudioContext,
    (context) =>
      currentRestoreData &&
      context.previewController.previewPackage?.id ===
        currentRestoreData?.versionId
  );

  const startPreviewing = useContextSelector(
    StudioContext,
    (context) => context.previewController.startPreviewing
  );

  const stopPreviewing = useContextSelector(
    StudioContext,
    (context) => context.previewController.stopPreviewing
  );

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

  const handleCloseVersions = useCallback(() => {
    // Stop any previewing
    stopPreviewing();

    setVersionsVisible(false);
    setCurrentRestoreData(null);
  }, [stopPreviewing, setState]);

  const handlePreviewVersion = useCallback(
    (versionId: string, versionState: unknown) => {
      if (versionState === null || typeof versionState !== 'object') {
        stopPreviewing();
        setCurrentRestoreData(null);
        return;
      }

      // Apply fixStudioProjectState to the version state
      // Note: fixStudioProjectState handles unknown input and validates/migrates it
      const stateToPreview = fixStudioProjectState(
        { ...versionState },
        {
          keepUploads: getBufferKeys(),
        }
      ) as StudioProjectState;
      startPreviewing(
        versionId,
        () => stateToPreview,
        false,
        undefined,
        undefined,
        false
      );

      // Set preview state
      setCurrentRestoreData({ versionId, state: versionState });
    },
    [startPreviewing, setState, stopPreviewing]
  );

  const handleRestoreVersion = useCallback(() => {
    commitPreview();

    // Close the versions panel after successful restore
    setVersionsVisible(false);
  }, [commitPreview]);

  const handleCloneProject = useCallback(
    async (title?: string) => {
      const versionIdToUse = currentVersionId || projectVersionId;
      if (!versionIdToUse) {
        throw new Error('No project version ID available for cloning');
      }

      return await cloneProject(versionIdToUse, title);
    },
    [cloneProject, currentVersionId, projectVersionId]
  );

  const { containerRef, containerName } = useContainer();

  const [trackHeadersCollapsedX, setTrackHeadersCollapsedX] = useState(false);

  const trackHeadersCollapsedY = useContextSelector(StudioContext, (context) =>
    context.state.tracks.every((t) => t.height <= minimumTrackHeight)
  );

  const setTrackHeadersCollapsedY = useCallback(
    (collapsed: boolean) =>
      setState(
        setAllTrackHeights(
          collapsed ? minimumTrackHeight : defaultStudioTrackHeight
        )
      ),
    [setState]
  );

  const fixBounds = useContextSelector(
    StudioContext,
    (context) => context.timelineController.fixBounds
  );

  const contextualBarExpanded = useContextSelector(
    StudioLayoutManagerContext,
    (context) => context?.contextualBarExpanded || false
  );

  const setFocusedObject = useContextSelector(
    FocusedObjectContext,
    (ctx) => ctx.setFocusedObject
  );

  const setStudioPanelMode = useContextSelector(
    FocusedObjectContext,
    (ctx) => ctx.setStudioPanelMode
  );

  const handleTimelineClick = useCallback(() => {
    setFocusedObject({ type: 'studio' });
  }, [setFocusedObject]);

  const handleTrackDoubleClick = useCallback(() => {
    setFocusedObjectSidebarExpanded?.(true);
    setStudioPanelMode('track');
  }, [setFocusedObjectSidebarExpanded, setStudioPanelMode]);

  return (
    <StudioTimelineDroppable>
      <VerticalPanelGroup>
        <Panel size={makePanelSize(48)}>
          <HeaderContainer borderBottom slim ref={containerRef}>
            <div className='flex items-center gap-2 select-none'>
              <PanelButton
                Icon={ProjectIcon}
                HoverIcon={ExtendRightIcon}
                enabled={!!setExpanded}
                onClick={() => setExpanded?.(false)}
                className='h-[40px] text-[12px]'
              />
              <ProjectNameButton
                setVersionsVisible={setVersionsVisible}
                setProjectsModalOpen={setProjectsModalOpen}
              />
            </div>
            <div className='flex items-center gap-2 select-none'>
              <Tooltip
                label={canUndo ? `Undo (${ctrlStr} Z)` : 'Nothing to undo'}
              >
                <Button
                  shape={ButtonShape.Pill}
                  variant={ButtonVariant.Tertiary}
                  icon={<EditUndoIcon className='h-4 w-4' />}
                  className='p-2.5'
                  disabled={!canUndo}
                  onClick={() => {
                    undo();
                  }}
                />
              </Tooltip>
              <Tooltip
                label={
                  canRedo ? `Redo (${ctrlStr} Shift Z)` : 'Nothing to redo'
                }
              >
                <Button
                  shape={ButtonShape.Pill}
                  variant={ButtonVariant.Tertiary}
                  icon={<EditRedoIcon className='h-4 w-4' />}
                  className='p-2.5'
                  disabled={!canRedo}
                  onClick={() => {
                    redo();
                  }}
                />
              </Tooltip>
              <ResponsiveChild
                containerName={containerName}
                hideBelowWidth={600}
              >
                <>
                  {isPreviewingVersion ? (
                    <>
                      <Button
                        shape={ButtonShape.Pill}
                        variant={ButtonVariant.Standard}
                        onClick={() => handlePreviewVersion('', null)}
                      >
                        Exit Preview
                      </Button>
                      <Button
                        variant={ButtonVariant.Primary}
                        shape={ButtonShape.Pill}
                        onClick={() => {
                          handleRestoreVersion();
                        }}
                      >
                        Restore this Version
                      </Button>
                    </>
                  ) : isReadOnlyMode ? (
                    <Button
                      shape={ButtonShape.Pill}
                      icon={<LockIcon className='h-4 w-4' />}
                      onClick={() => setCloneProjectModalOpen(true)}
                    >
                      Remix Project
                    </Button>
                  ) : (
                    <StudioExportMenu setRenderedClip={setRenderedClip} />
                  )}
                </>
              </ResponsiveChild>
              <ResponsiveChild
                containerName={containerName}
                showBelowWidth={600}
              >
                <ContextMenuTrigger
                  ButtonComponent={(props) => (
                    <Button
                      {...props}
                      icon={<MoreVerticalIcon className='h-4 w-4' />}
                      className='p-2.5'
                      shape={ButtonShape.Pill}
                    />
                  )}
                  ContentsComponent={() => (
                    <>
                      {isReadOnlyMode ? (
                        <ContextMenuItem
                          keepMenusOpen
                          onClick={() => setCloneProjectModalOpen(true)}
                        >
                          Remix Project
                        </ContextMenuItem>
                      ) : (
                        <StudioExportMenu setRenderedClip={setRenderedClip} />
                      )}
                    </>
                  )}
                />
              </ResponsiveChild>
              {!focusedObjectSidebarExpanded &&
                !!setFocusedObjectSidebarExpanded && (
                  <Button
                    shape={ButtonShape.Pill}
                    icon={<SidebarRightIcon className='h-4 w-4' />}
                    onClick={() =>
                      setFocusedObjectSidebarExpanded((prev) => !prev)
                    }
                    className='h-[40px] text-[12px]'
                  />
                )}
            </div>
          </HeaderContainer>
        </Panel>
        <Panel size={oneFRPanelSize}>
          <TimelineWrapper ref={receiveTimelineAndTrackHeadersContainer}>
            {renderedClip && (
              <StudioNotification
                title='Song Saved!'
                content={
                  <>
                    <Link href={`/song/${renderedClip.id}`} target='_blank'>
                      {getClipTitle(renderedClip)}
                    </Link>{' '}
                    was saved in your library.
                  </>
                }
                buttonContent='Go to Song'
                href={`/song/${renderedClip.id}`}
                onClose={() => {
                  setRenderedClip(null);
                }}
              />
            )}
            {loading && <LoadingOverlay />}
            <HorizontalPanelGroup>
              {versionsVisible && (
                <>
                  <Panel size={makePanelSize(300)} minSizePx={200}>
                    <StudioVersionsPanel
                      onClose={handleCloseVersions}
                      onPreviewVersion={handlePreviewVersion}
                      onRestoreVersion={handleRestoreVersion}
                    />
                  </Panel>
                  <Divider influence='both'>
                    <VerticalDivider />
                  </Divider>
                </>
              )}
              <Panel
                size={
                  trackHeadersCollapsedX
                    ? makePanelSize(112)
                    : trackHeadersPanelSize
                }
                minSizePx={trackHeadersCollapsedX ? 112 : 120}
              >
                <TrackSection ref={trackHeaderListRef}>
                  <TrackTopControls>
                    <Tooltip
                      label={
                        trackHeadersCollapsedY
                          ? 'Expand Tracks'
                          : 'Collapse Tracks'
                      }
                      openDelay={500}
                    >
                      <TrackHeaderButton
                        variant={ButtonVariant.Tertiary}
                        icon={
                          trackHeadersCollapsedY
                            ? TracksExpandIcon
                            : TracksCollapseIcon
                        }
                        size={ButtonSize.Mini}
                        className='!bg-transparent'
                        onClick={() =>
                          setTrackHeadersCollapsedY(!trackHeadersCollapsedY)
                        }
                      />
                    </Tooltip>
                    <Tooltip
                      label={
                        trackHeadersCollapsedX
                          ? 'Expand Tracks (Horizontal)'
                          : 'Collapse Tracks (Horizontal)'
                      }
                      openDelay={500}
                    >
                      <TrackHeaderButton
                        variant={ButtonVariant.Tertiary}
                        icon={
                          trackHeadersCollapsedX
                            ? ClipZoomInIcon
                            : ClipZoomFitIcon
                        }
                        size={ButtonSize.Mini}
                        className='!bg-transparent'
                        onClick={() =>
                          setTrackHeadersCollapsedX((prev) => !prev)
                        }
                      />
                    </Tooltip>
                  </TrackTopControls>
                  <TrackHeaders
                    className='track-list-container select-none'
                    onWheel={(e) => {
                      receiveScroll(e.deltaX, e.deltaY);
                    }}
                    ref={trackListRef}
                  >
                    <VerticalPanelGroup frameCountRef={frameCountRef}>
                      <Panel size={makePanelSize(RULER_HEIGHT)} />
                      <Divider influence={'neither'}>
                        <HorizontalDivider />
                      </Divider>
                      {tracks.map((track, index) => (
                        <Fragment key={track.id}>
                          <Panel
                            size={makePanelSize(track.height)}
                            minSizePx={minimumTrackHeight}
                            debounceSetSizeMS={100}
                            setSize={(size) => {
                              setState(setTrackHeight(track.id, size.count));
                              fixBounds();
                            }}
                          >
                            <div
                              className='h-full'
                              data-track-header={track.id}
                              ref={receiveTrackHeader}
                            >
                              <StudioTrackHeader
                                trackId={track.id}
                                onDoubleClick={handleTrackDoubleClick}
                                trackIndex={index}
                              />
                            </div>
                          </Panel>
                          {shouldShowTakeLanes(track) &&
                            track.takeLanes.map((takeLane, index) => (
                              <Fragment key={takeLane.id}>
                                <Divider influence={'before'}>
                                  <HorizontalDivider />
                                </Divider>
                                <Panel
                                  size={makePanelSize(takeLane.height)}
                                  minSizePx={minimumTrackHeight}
                                  setSize={(size) => {
                                    setState(
                                      setTakeLaneHeight(
                                        track.id,
                                        takeLane.id,
                                        size.count
                                      )
                                    );
                                    fixBounds();
                                  }}
                                >
                                  <div
                                    className='h-full'
                                    data-track-header={takeLane.id}
                                    ref={receiveTrackHeader}
                                  >
                                    <StudioTakeLaneHeader
                                      trackId={track.id}
                                      takeLaneId={takeLane.id}
                                      isLastTakeLane={
                                        index === track.takeLanes.length - 1
                                      }
                                    />
                                  </div>
                                </Panel>
                              </Fragment>
                            ))}
                          <Divider influence={'before'}>
                            <HorizontalDivider style={{ height: 2 }} />
                          </Divider>
                        </Fragment>
                      ))}
                      {/* Ghost track header - always shown to allow selection below last track */}
                      <Panel size={makePanelSize(defaultStudioTrackHeight)}>
                        <div
                          className='h-full'
                          data-track-header={AFTER_LAST_TRACK}
                          ref={receiveTrackHeader}
                        >
                          <GhostTrackHeader />
                        </div>
                      </Panel>
                      {/* for minimum bottom padding when content overflows and is scrollable */}
                      <Panel size={makePanelSize(defaultStudioTrackHeight)}>
                        <div className='h-full' ref={afterLastTrackRef} />
                      </Panel>
                      {/* to fill remaining space when content does not overflow */}
                      <Panel size={oneFRPanelSize} />
                      <Panel
                        size={makePanelSize(contextualBarExpanded ? 400 : 80)}
                      />
                    </VerticalPanelGroup>
                  </TrackHeaders>
                </TrackSection>
              </Panel>
              <Divider influence={trackHeadersCollapsedX ? 'neither' : 'both'}>
                <VerticalDivider />
              </Divider>
              <Panel size={oneFRPanelSize}>
                <CanvasRendererContainer
                  ref={receiveWrapperRef}
                  onClick={handleTimelineClick}
                  onContextMenu={(e) => {
                    if (e.target instanceof HTMLCanvasElement) {
                      e.preventDefault();
                      e.stopPropagation();

                      setContextMenuConfig({
                        ChildComponent: (props) => (
                          <StudioContextMenuContents
                            {...props}
                            eventTrigger='right-click'
                          />
                        ),
                        clientCoords: {
                          top: e.clientY,
                          left: e.clientX,
                        },
                      });
                    }
                  }}
                >
                  <HintMessage />
                  <ScrollCatcher
                    onScroll={receiveScroll}
                    onZoom={handleZoom}
                    scrollXRef={scrollXRef}
                    pxPerBeatRef={pxPerBeatRef}
                    invertZoom={false} // TODO
                    zoomOnResize
                  >
                    <CanvasRenderer
                      getCanvasRegions={getCanvasRegions}
                      frameCountRef={frameCountRef}
                      withWebGL={true}
                      allowRightClicks={true}
                    />
                  </ScrollCatcher>
                  {!learnModalOpen && <ContextualBar />}
                </CanvasRendererContainer>
              </Panel>
            </HorizontalPanelGroup>
            <SaveIndicator isSaving={isSaving} />
          </TimelineWrapper>
        </Panel>
        <Divider>
          <ThickGapVerticalDivider />
        </Divider>
        <Panel size={studioTransportPanelSize}>
          <TransportWrapper>
            <StudioTransport />
          </TransportWrapper>
        </Panel>
      </VerticalPanelGroup>
      <CloneProjectModal
        isOpen={cloneProjectModalOpen}
        onClose={() => setCloneProjectModalOpen(false)}
        onClone={handleCloneProject}
      />
    </StudioTimelineDroppable>
  );
});
