import styled from '@emotion/styled';
import { useAnimationFrame } from 'framer-motion';
import { observer } from 'mobx-react-lite';
import React, { useCallback, useEffect, useRef, useState } from 'react';
import { useLocalStorage } from 'usehooks-ts';

import { useContext, useContextSelector } from '@/hooks/useContextSelector';
import { ReferenceType } from '@/hooks/useGenerate';
import {
  CheckboxIcon,
  CheckboxOutlineIcon,
  ChevronDownIcon,
  ChevronUpIcon,
  EditIcon,
  EditUndoIcon,
  MinusIcon,
  PlusIcon,
} from '@/icons';
import { staticAssetUrl } from '@/utils/staticAssetUrl';
import { encodeTimeFormat } from '@/utils/utils';

import Button, {
  ButtonShape,
  ButtonSize,
  ButtonVariant,
} from '../button/Button';
import {
  ContextMenuGroup,
  ContextMenuItem,
  ContextMenuItemSubtext,
  ContextMenuTrigger,
} from '../contextMenu/ContextMenu';
import { getPlaintextLyrics } from '../edit2025/lyrics/getPlaintextLyrics';
import {
  Divider,
  Panel,
  PanelSize,
  VerticalPanelGroup,
} from '../panelSystem/PanelSystem';
import { Tooltip } from '../tooltip/Tooltip';
import StudioContext from './StudioContext';
import StudioGenerateButton from './StudioGenerateButton';
import StudioLyricsDisplayV2 from './StudioLyricsDisplayV2';
import {
  NarrowGapHorizontalDivider,
  ThickGapHorizontalDivider,
  oneFRPanelSize,
} from './StudioPanelUtilities';
import { getSelectionDurationBeats, getSelectionGapSize } from './selectors';
import { useDebouncedCommit } from './useDebouncedCommit';
import useFocusMemory from './useFocusMemory';
import useLyricsPresence from './useLyricsPresence';

const defaultReplaceableLyricsPanelSize: PanelSize = {
  unit: 'px',
  count: 450,
};

const defaultReplaceLyricsPanelSize: PanelSize = {
  unit: 'px',
  count: 168,
};

const defaultStylesPanelSize: PanelSize = {
  unit: 'px',
  count: 230,
};

const GenerateFormWrapper = styled.div`
  height: 100%;
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: stretch;
  justify-content: stretch;
`;

const FormScroller = styled.div`
  overflow: auto;
  flex-grow: 1;
  min-height: 0;
  height: 100%;
`;

const PanelGroupWrapper = styled.div`
  height: 100%;
  display: flex;
  flex-direction: column;
`;

const ModuleHeader = styled.div`
  padding: 14px 16px;
  flex-grow: 0;
  display: flex;
  align-items: center;
  justify-content: space-between;
  font-size: 14px;
  ${({ onClick }) => onClick && `cursor: pointer;`}
  button {
    background: var(--color-background-glass-thick);
  }
  ${({ onClick }) =>
    onClick &&
    `&:hover button {
    background: var(--color-background-glass-dense);
  }`}
`;

const Textarea = styled.textarea`
  flex-grow: 1;
  margin: 0 8px 8px 8px;
  padding: 8px;
  resize: none;
  height: 100%;
  font-size: 16px;
  line-height: 1.25;
  background-color: transparent;
  border: 1px solid var(--color-border-primary);
  border-radius: 8px;
  &:focus {
    outline: none;
    border-color: var(--color-border-secondary);
  }
`;

const PanelContent = styled.div`
  display: flex;
  flex-direction: column;
  gap: 0;
  justify-content: stretch;
  align-items: stretch;
  height: 100%;
`;

export const FormSegmentWrapper = styled(PanelContent)`
  background-color: var(--color-background-secondary);
  border-radius: 16px;
`;

const TimeDisplay = styled.div`
  display: flex;
  align-items: center;
  justify-content: center;
  flex-grow: 1;
  min-width: 70px;
  border-radius: 50px;
  background-color: var(--color-background-glass-thick);
`;

const LyricsWrapper = styled.div`
  position: relative;
  flex-grow: 1;
  .lyrics-display {
    position: absolute;
    top: 0;
    left: 8px;
    right: 8px;
    bottom: 8px;
    border-radius: 8px;
  }
`;

const ReplaceLyricsWrapper = styled.div`
  position: relative;
  height: 100%;
  display: flex;
  flex-direction: column;
  align-items: stretch;
  justify-content: stretch;
  background-color: var(--color-background-tertiary);
`;

const FixedBottomSectionContainer = styled.div`
  position: relative;
  background-color: var(--color-background-secondary);
  border-radius: 16px;
  border-top: 1px solid black;
  flex-grow: 0;
  box-shadow: 0 -15px 15px 0 rgba(0, 0, 0, 0.4);
`;

const Footer = styled.div`
  height: 50px;
  display: flex;
  align-items: center;
  justify-content: stretch;
  gap: 8px;
  padding: 0 8px;
`;

const collapsedPanelSize: PanelSize = {
  unit: 'px',
  count: 52,
};

const ContextWindowReadout = styled.div`
  width: 80px;
  text-align: center;
  font-family: 'PP Neue Montreal', sans-serif;
  font-size: 14px;
  flex-grow: 1;
`;

const FixedBottomSection = (props: { children: React.ReactNode }) => {
  const { hasMadeFirstSelection } = useContext(StudioContext);

  return (
    <FixedBottomSectionContainer>
      {!hasMadeFirstSelection && (
        <>
          {/* Background div with same color as video background to show while video is loading. */}
          <div className='absolute top-0 left-0 h-full w-full rounded-[16px] bg-background-primary' />
          <div className='absolute top-0 left-0 flex h-full w-full justify-between gap-4 overflow-hidden rounded-[16px] pr-8 pl-4'>
            <video
              src={staticAssetUrl('studio/learn_replace.mp4')}
              className='w-full max-w-[140px] scale-[1.5]'
              muted
              loop={true}
              autoPlay={true}
              playsInline
            />
            <p className='flex items-center justify-center text-sm text-foreground-primary'>
              Choose a section of the timeline or lyrics to start editing.
            </p>
          </div>
        </>
      )}
      <span
        style={{
          opacity: hasMadeFirstSelection ? 1 : 0,
          cursor: hasMadeFirstSelection ? 'default' : 'pointer',
          pointerEvents: hasMadeFirstSelection ? 'auto' : 'none',
        }}
      >
        {props.children}
      </span>
    </FixedBottomSectionContainer>
  );
};

export const BoundedPanel = ({
  title,
  stateKey,
  children,
  size,
  setSize,
  minSizePx,
  collapsible = true,
  defaultOpen = false,
}: {
  title: React.ReactNode;
  stateKey: string;
  children: React.ReactNode;
  size: PanelSize;
  setSize?: (size: PanelSize) => void;
  minSizePx?: number;
  collapsible?: boolean;
  defaultOpen?: boolean;
}) => {
  const studioProjectId = useContextSelector(
    StudioContext,
    (context) => context.studioProjectId
  );
  const [isOpen, setIsOpen] = useLocalStorage(
    `studio-generate-form-may-15-2025-${stateKey}-${studioProjectId}-open`,
    defaultOpen
  );
  const effectivelyOpen = !collapsible || isOpen;
  return (
    <>
      <Panel
        size={effectivelyOpen ? size : collapsedPanelSize}
        setSize={setSize}
        minSizePx={effectivelyOpen ? minSizePx : undefined}
      >
        <FormSegmentWrapper>
          <ModuleHeader
            onClick={() => collapsible && setIsOpen(!effectivelyOpen)}
          >
            <div className='grow'>{title}</div>
            {collapsible ? (
              <Button
                size={ButtonSize.Mini}
                shape={ButtonShape.Pill}
                className='p-1'
                icon={
                  effectivelyOpen ? (
                    <ChevronUpIcon className='h-4 w-4' />
                  ) : (
                    <ChevronDownIcon className='h-4 w-4' />
                  )
                }
                onClick={() => void 0}
              />
            ) : null}
          </ModuleHeader>
          {effectivelyOpen && children}
        </FormSegmentWrapper>
      </Panel>
      <Divider influence={effectivelyOpen ? 'before' : 'neither'}>
        <ThickGapHorizontalDivider transparent />
      </Divider>
    </>
  );
};

const AddLyricsPanel = () => {
  const editedLyrics = useContextSelector(
    StudioContext,
    (context) => context.lyricsEditController.editedLyrics
  );
  const setEditedLyrics = useContextSelector(
    StudioContext,
    (context) => context.lyricsEditController.setEditedLyrics
  );
  return (
    <BoundedPanel
      size={defaultReplaceableLyricsPanelSize}
      minSizePx={90}
      title='Add Lyrics'
      stateKey='Add Lyrics'
      defaultOpen={false}
    >
      <Textarea
        placeholder={'Enter lyrics to insert'}
        value={editedLyrics || ''}
        onChange={(e) => setEditedLyrics(e.target.value)}
      />
    </BoundedPanel>
  );
};

const ReplaceableLyricsPanel = () => {
  const editedLyrics = useContextSelector(
    StudioContext,
    (context) => context.lyricsEditController.editedLyrics
  );
  const setEditedLyrics = useContextSelector(
    StudioContext,
    (context) => context.lyricsEditController.setEditedLyrics
  );
  const getSplitLyrics = useContextSelector(
    StudioContext,
    (context) => context.lyricsEditController.getSplitLyrics
  );
  const canResetLyrics = useContextSelector(
    StudioContext,
    (context) => context.lyricsEditController.canResetLyrics
  );
  const resetEditedLyrics = useContextSelector(
    StudioContext,
    (context) => context.lyricsEditController.resetEditedLyrics
  );
  const replacingLyrics = useContextSelector(
    StudioContext,
    (context) => context.lyricsEditController.replacingLyrics
  );
  const canActivateReplaceLyrics = useContextSelector(
    StudioContext,
    (context) =>
      !replacingLyrics &&
      context.generateMode === 'replace' &&
      getSelectionDurationBeats(context.state) > 0.0001
  );
  const startReplacingLyrics = useContextSelector(
    StudioContext,
    (context) => context.startReplacingLyrics
  );
  const stopReplacingLyrics = useContextSelector(
    StudioContext,
    (context) => context.stopReplacingLyrics
  );

  const [fixAlignment, setFixAlignment] = useState<boolean>(false);
  const armForKeyTakeoverRef = useRef<boolean>(false);
  const focusOnMountRef = useRef<boolean>(false);

  const focus = useFocusMemory(
    useCallback(() => {
      armForKeyTakeoverRef.current = true;
    }, []),
    useCallback(() => {
      armForKeyTakeoverRef.current = false;
    }, [])
  );

  const { ref, onInput } = useDebouncedCommit(
    editedLyrics ?? '',
    setEditedLyrics
  );

  const receiveReplaceLyricsRef = useCallback(
    (el: HTMLTextAreaElement) => {
      if (el && !ref.current) {
        const wasFocused = document.activeElement === el;
        if (focusOnMountRef.current) {
          focusOnMountRef.current = false;
          el.focus();
        }
        if (!wasFocused) {
          if (editedLyrics === null) {
            el.value = getPlaintextLyrics(getSplitLyrics()[1]);
          } else {
            el.value = editedLyrics;
          }
        }
      }
      ref.current = el;
    },
    [editedLyrics, getSplitLyrics, setEditedLyrics]
  );

  useAnimationFrame(() => {
    if (!ref.current) return;
    if (document.activeElement === ref.current) return;
    if (editedLyrics !== null) return;
    const currentValue = getPlaintextLyrics(getSplitLyrics()[1]);
    if (ref.current.value !== currentValue) {
      ref.current.value = currentValue;
    }
  });

  useEffect(() => {
    const handleKeyDown = (e: KeyboardEvent) => {
      if (e.key === 'Escape') {
        armForKeyTakeoverRef.current = false;
        focusOnMountRef.current = false;
      } else if (e.key === ' ') {
        armForKeyTakeoverRef.current = false;
        focusOnMountRef.current = false;
      } else if (armForKeyTakeoverRef.current && !e.metaKey && !e.ctrlKey) {
        if (e.key.length > 1) return;
        armForKeyTakeoverRef.current = false;
        focusOnMountRef.current = true;
        setEditedLyrics('');
        startReplacingLyrics();
      }
    };
    window.addEventListener('keydown', handleKeyDown);
    return () => {
      window.removeEventListener('keydown', handleKeyDown);
    };
  }, [
    armForKeyTakeoverRef,
    focusOnMountRef,
    startReplacingLyrics,
    setEditedLyrics,
  ]);

  return (
    <BoundedPanel
      size={defaultReplaceableLyricsPanelSize}
      minSizePx={250}
      defaultOpen
      title='Lyrics'
      stateKey='Lyrics'
    >
      <VerticalPanelGroup>
        <Panel size={oneFRPanelSize}>
          <Tooltip
            label={
              fixAlignment ? (
                'Drag the handles such that the selected span of lyrics matches what you hear in the selected audio.'
              ) : replacingLyrics ? (
                'You can also adjust your selection in the timeline.'
              ) : (
                <>
                  Click and drag on song lyrics to select them in the timeline.
                  <br />
                  <br />
                  Use &ldquo;New Lyrics&rdquo; to enter new lyrics to replace
                  your selection.
                </>
              )
            }
            placement='right'
          >
            <div className='relative flex h-full flex-col items-stretch justify-stretch px-2'>
              <LyricsWrapper onMouseDown={focus}>
                <StudioLyricsDisplayV2 fixAlignment={fixAlignment} />
              </LyricsWrapper>
            </div>
          </Tooltip>
        </Panel>
        {replacingLyrics && (
          <>
            <Divider influence='after'>
              <NarrowGapHorizontalDivider />
            </Divider>
            <Panel size={defaultReplaceLyricsPanelSize} minSizePx={168}>
              <ReplaceLyricsWrapper>
                <ModuleHeader>
                  <span className='flex-1'>New Lyrics</span>

                  {replacingLyrics && canResetLyrics && (
                    <Tooltip label='Reset lyrics'>
                      <Button
                        className='mr-2 animate-fade-up p-1.5'
                        shape={ButtonShape.Pill}
                        size={ButtonSize.Mini}
                        onClick={() => {
                          resetEditedLyrics();
                        }}
                        icon={<EditUndoIcon className='h-4 w-4' />}
                      />
                    </Tooltip>
                  )}
                  {replacingLyrics && (
                    <Tooltip
                      placement='right'
                      label={
                        fixAlignment
                          ? 'Use the upper lyrics area to select the correct lyrics to replace'
                          : 'Activate if the selected lyrics do not match the selected audio'
                      }
                    >
                      <Button
                        shape={ButtonShape.Pill}
                        size={ButtonSize.Mini}
                        variant={ButtonVariant.Tertiary}
                        onClick={(e) => {
                          setFixAlignment((prev) => !prev);
                          e.preventDefault();
                        }}
                      >
                        {fixAlignment ? 'Discard Changes' : 'Fix Alignment'}
                      </Button>
                    </Tooltip>
                  )}
                </ModuleHeader>
                <Tooltip
                  label='Enter new lyrics to replace your selection.'
                  placement='right'
                >
                  {/* tooltip dismounts and remounts its direct children any time it is rendered or your mouse enters it */}
                  <div className='flex h-full items-stretch justify-stretch pb-[8px]'>
                    <Textarea ref={receiveReplaceLyricsRef} onInput={onInput} />
                  </div>
                </Tooltip>
              </ReplaceLyricsWrapper>
            </Panel>
          </>
        )}
      </VerticalPanelGroup>
      <Footer
        style={{
          backgroundColor: replacingLyrics
            ? 'var(--color-background-secondary)'
            : '',
          borderTop: '1px solid black',
          borderBottomLeftRadius: '16px',
          borderBottomRightRadius: '16px',
          justifyContent: 'space-between',
        }}
      >
        <Tooltip
          label={
            !canActivateReplaceLyrics ? 'Select a range of time to replace' : ''
          }
        >
          <Button
            disabled={!replacingLyrics && !canActivateReplaceLyrics}
            shape={ButtonShape.Pill}
            size={ButtonSize.Mini}
            icon={replacingLyrics ? null : <EditIcon className='h-5 w-5' />}
            onClick={() => {
              if (replacingLyrics) {
                stopReplacingLyrics();
                setFixAlignment(false);
              } else {
                focusOnMountRef.current = true;
                startReplacingLyrics();
              }
            }}
          >
            {replacingLyrics ? 'Cancel' : 'Replace Lyrics'}
          </Button>
        </Tooltip>
      </Footer>
    </BoundedPanel>
  );
};

const StylesPanel = () => {
  const styles = useContextSelector(
    StudioContext,
    (context) => context.stylesEditController.styles
  );
  const setStyles = useContextSelector(
    StudioContext,
    (context) => context.stylesEditController.setStyles
  );
  const excludeStyles = useContextSelector(
    StudioContext,
    (context) => context.stylesEditController.excludeStyles
  );
  const setExcludeStyles = useContextSelector(
    StudioContext,
    (context) => context.stylesEditController.setExcludeStyles
  );
  const getCurrentSelectionStyles = useContextSelector(
    StudioContext,
    (context) => context.stylesEditController.getCurrentSelectionStyles
  );
  const resetStyles = useContextSelector(
    StudioContext,
    (context) => context.resetStyles
  );
  const [size, setSize] = useState<PanelSize>(defaultStylesPanelSize);
  const [showExcludeStyles, setShowExcludeStyles] = useState(false);
  const lastShowExcludeStyles = useRef(showExcludeStyles);

  const toggleExcludeStyles = useCallback(() => {
    const newShowExcludeStyles = !showExcludeStyles;
    if (lastShowExcludeStyles.current !== newShowExcludeStyles) {
      setSize((size) => {
        const referenceSize =
          size.unit === 'px' ? size : defaultStylesPanelSize;
        return {
          ...referenceSize,
          count:
            referenceSize.count +
            (newShowExcludeStyles
              ? referenceSize.count - 102
              : (referenceSize.count - 102) / -2),
        };
      });
    }
    setShowExcludeStyles(newShowExcludeStyles);
    lastShowExcludeStyles.current = newShowExcludeStyles;
  }, [showExcludeStyles]);
  return (
    <BoundedPanel
      size={size}
      setSize={setSize}
      minSizePx={230}
      title={
        <div className='flex items-center justify-between'>
          <div>Styles</div>
          <Tooltip label='Reset styles'>
            <Button
              size={ButtonSize.Mini}
              shape={ButtonShape.Pill}
              className='p-1.5'
              icon={<EditUndoIcon className='h-4 w-4' />}
              onClick={() => {
                resetStyles();
              }}
            />
          </Tooltip>
        </div>
      }
      stateKey='Styles'
      defaultOpen
      collapsible={false}
    >
      <Textarea
        placeholder={
          getCurrentSelectionStyles().styles || 'Enter styles to apply'
        }
        value={styles}
        onChange={(e) => setStyles(e.target.value)}
      />
      <NarrowGapHorizontalDivider />
      <ModuleHeader onClick={toggleExcludeStyles}>
        <div>Exclude Styles</div>
        <Button
          size={ButtonSize.Mini}
          shape={ButtonShape.Pill}
          className='p-1'
          icon={
            showExcludeStyles ? (
              <ChevronUpIcon className='h-4 w-4' />
            ) : (
              <ChevronDownIcon className='h-4 w-4' />
            )
          }
          onClick={() => void 0}
        />
      </ModuleHeader>
      {showExcludeStyles && (
        <Textarea
          placeholder={
            getCurrentSelectionStyles().excludeStyles ||
            'Enter styles to exclude'
          }
          value={excludeStyles}
          onChange={(e) => setExcludeStyles(e.target.value)}
        />
      )}
    </BoundedPanel>
  );
};

const DurationPickerWrapper = styled.div`
  display: flex;
  align-items: center;
  justify-content: center;
  flex-grow: 1;
  border-radius: 200px;
  background-color: var(--color-background-glass-thick);
`;

const CreateForm = () => {
  const selectionGapSize = useContextSelector(StudioContext, (context) =>
    getSelectionGapSize(context.state)
  );
  const setSelectionGapSize = useContextSelector(
    StudioContext,
    (context) => context.setSelectionGapSize
  );

  return (
    <>
      <FormScroller>
        <PanelGroupWrapper>
          <VerticalPanelGroup>
            <AddLyricsPanel />
            <StylesPanel />
            <Panel size={oneFRPanelSize} />
          </VerticalPanelGroup>
        </PanelGroupWrapper>
      </FormScroller>
      <FixedBottomSection>
        <ModuleHeader>
          <div className='flex items-center gap-2'>
            Create Section
            <DurationPickerWrapper>
              <Button
                shape={ButtonShape.Pill}
                icon={<MinusIcon className='h-5 w-5' />}
                style={{
                  backgroundColor: 'transparent',
                }}
                onClick={() =>
                  setSelectionGapSize((prev) => Math.max(0, prev - 1))
                }
              />
              <div className='flex-1 px-4 text-center text-sm'>
                {selectionGapSize.toFixed(2)} Beats
              </div>
              <Button
                shape={ButtonShape.Pill}
                icon={<PlusIcon className='h-5 w-5' />}
                style={{
                  backgroundColor: 'transparent',
                }}
                onClick={() => setSelectionGapSize((prev) => prev + 1)}
              />
            </DurationPickerWrapper>
          </div>
        </ModuleHeader>
        <Footer>
          <StudioGenerateButton
            tooltipPlacement='top'
            className='w-full'
            disabled={selectionGapSize === 0}
            eventTrigger='sidebar'
          />
        </Footer>
      </FixedBottomSection>
    </>
  );
};

const ReplaceForm = () => {
  const lyricsPresence = useLyricsPresence();
  const replaceMode = useContextSelector(
    StudioContext,
    (context) => context.replaceMode
  );
  const setReplaceMode = useContextSelector(
    StudioContext,
    (context) => context.setReplaceMode
  );
  const contextBeatsSetting = useContextSelector(
    StudioContext,
    (context) => context.contextBeatsSetting
  );
  const setContextWindowBeats = useContextSelector(
    StudioContext,
    (context) => context.setContextWindowBeats
  );
  const showContextWindow = useContextSelector(
    StudioContext,
    (context) => context.showContextWindow
  );
  const setShowContextWindow = useContextSelector(
    StudioContext,
    (context) => context.setShowContextWindow
  );
  const regenerateType = useContextSelector(StudioContext, (context) => {
    const references = context.regenerateParams?.references;
    if (!references) return null;
    if (references.some((ref) => ref.type === ReferenceType.FixedInfill))
      return 'Fixed';
    if (references.some((ref) => ref.type === ReferenceType.Infill))
      return 'Classic';
    return null;
  });
  const hasRegenerateParams = useContextSelector(
    StudioContext,
    (context) => !!context.regenerateParams
  );
  const getEffectiveSelection = useContextSelector(
    StudioContext,
    (context) => context.getEffectiveSelection
  );

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

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

  const startTimeDisplayRef = useRef<HTMLDivElement>(null);
  const endTimeDisplayRef = useRef<HTMLDivElement>(null);

  const [replaceDisabled, setReplaceDisabled] = useState(
    !!generateBlockingErrorMessage
  );

  useEffect(() => {
    setReplaceDisabled(!!generateBlockingErrorMessage);
  }, [generateBlockingErrorMessage]);

  useAnimationFrame(() => {
    const effectiveSelection = getEffectiveSelection();
    if (startTimeDisplayRef.current) {
      startTimeDisplayRef.current.textContent = encodeTimeFormat(
        effectiveSelection.startSeconds,
        2
      );
    }
    if (endTimeDisplayRef.current) {
      endTimeDisplayRef.current.textContent = encodeTimeFormat(
        effectiveSelection.endSeconds,
        2
      );
    }
    setReplaceDisabled(
      !!getGenerateBlockingErrorMessage(
        effectiveSelection.startSeconds,
        effectiveSelection.endSeconds
      )
    );
  });

  return (
    <>
      <FormScroller>
        <PanelGroupWrapper>
          <VerticalPanelGroup>
            {lyricsPresence === 'present' && <ReplaceableLyricsPanel />}
            {lyricsPresence === 'empty' && <AddLyricsPanel />}
            <StylesPanel />
            <Panel size={oneFRPanelSize} />
          </VerticalPanelGroup>
        </PanelGroupWrapper>
      </FormScroller>

      <FixedBottomSection>
        <ModuleHeader>
          <div className='flex items-center gap-1'>
            <span className=''>Replace from</span>
            <TimeDisplay className='p-1' ref={startTimeDisplayRef} />
            <span className=''>-</span>
            <TimeDisplay className='p-1' ref={endTimeDisplayRef} />
          </div>
          <ContextMenuTrigger
            placement='top-right'
            ButtonComponent={(props) => (
              <Tooltip
                label={
                  !!hasRegenerateParams
                    ? 'Using same mode as selected clip'
                    : ''
                }
              >
                <Button
                  disabled={!!hasRegenerateParams}
                  icon={<ChevronDownIcon />}
                  shape={ButtonShape.Pill}
                  size={ButtonSize.Mini}
                  className='-my-1 -mr-2 h-full'
                  {...props}
                >
                  {regenerateType
                    ? regenerateType
                    : replaceMode === 'smart_infill'
                      ? 'Smart'
                      : replaceMode === 'infill'
                        ? 'Classic'
                        : replaceMode === 'fixed_infill'
                          ? 'Fixed'
                          : replaceMode || 'Pick Mode'}
                </Button>
              </Tooltip>
            )}
            ContentsComponent={() => (
              <>
                {replaceMode === 'infill' && (
                  <ContextMenuGroup>
                    <Tooltip
                      label={
                        showContextWindow ? (
                          <>
                            Span of time around selection that is referenced
                            while generating.
                            <br />
                            <br />
                            Short context windows follow styles more closely,
                            but may not be consistent with the rest of the song.
                          </>
                        ) : (
                          'Adjust the span of time around selection that is referenced while generating.'
                        )
                      }
                      placement='top'
                    >
                      <ContextMenuItem
                        keepMenusOpen
                        icon={
                          showContextWindow ? (
                            <CheckboxIcon />
                          ) : (
                            <CheckboxOutlineIcon />
                          )
                        }
                        onClick={() => setShowContextWindow(!showContextWindow)}
                      >
                        Custom Context Window
                      </ContextMenuItem>
                    </Tooltip>
                    {showContextWindow && (
                      <div className='flex items-center justify-stretch gap-2 bg-quaternary px-[16px] pt-1 pb-2'>
                        <Button
                          variant={ButtonVariant.Secondary}
                          onClick={() =>
                            setContextWindowBeats(
                              Math.max(1, contextBeatsSetting / 2)
                            )
                          }
                          icon={<MinusIcon className='h-4 w-4' />}
                        />
                        <ContextWindowReadout>
                          {contextBeatsSetting} Beat
                          {contextBeatsSetting === 1 ? '' : 's'}
                        </ContextWindowReadout>
                        <Button
                          variant={ButtonVariant.Secondary}
                          onClick={() =>
                            setContextWindowBeats(
                              Math.min(128, contextBeatsSetting * 2)
                            )
                          }
                          icon={<PlusIcon className='h-4 w-4' />}
                        />
                      </div>
                    )}
                  </ContextMenuGroup>
                )}

                <ContextMenuGroup>
                  <ContextMenuItem
                    keepMenusOpen
                    onClick={() => setReplaceMode('smart_infill')}
                    icon={
                      replaceMode === 'smart_infill' ? (
                        <CheckboxIcon />
                      ) : (
                        <CheckboxOutlineIcon />
                      )
                    }
                  >
                    Smart
                    <ContextMenuItemSubtext>
                      Auto-select based on length
                    </ContextMenuItemSubtext>
                  </ContextMenuItem>
                  <ContextMenuItem
                    keepMenusOpen
                    onClick={() => setReplaceMode('infill')}
                    icon={
                      replaceMode === 'infill' ? (
                        <CheckboxIcon />
                      ) : (
                        <CheckboxOutlineIcon />
                      )
                    }
                  >
                    Classic
                    <ContextMenuItemSubtext>
                      Better for long selections
                    </ContextMenuItemSubtext>
                  </ContextMenuItem>
                  <ContextMenuItem
                    keepMenusOpen
                    onClick={() => setReplaceMode('fixed_infill')}
                    icon={
                      replaceMode === 'fixed_infill' ? (
                        <CheckboxIcon />
                      ) : (
                        <CheckboxOutlineIcon />
                      )
                    }
                  >
                    Fixed
                    <ContextMenuItemSubtext>
                      Better for short selections.
                    </ContextMenuItemSubtext>
                  </ContextMenuItem>
                </ContextMenuGroup>
              </>
            )}
          />
        </ModuleHeader>
        <Footer>
          <StudioGenerateButton
            disabled={replaceDisabled}
            tooltipPlacement='top'
            className='w-full'
            eventTrigger='sidebar'
          />
        </Footer>
      </FixedBottomSection>
    </>
  );
};

const ExtendForm = () => {
  const getEffectiveSelection = useContextSelector(
    StudioContext,
    (context) => context.getEffectiveSelection
  );
  const lyricsPresence = useLyricsPresence();
  const startTimeDisplayRef = useRef<HTMLDivElement>(null);

  useAnimationFrame(() => {
    const effectiveSelection = getEffectiveSelection();
    if (startTimeDisplayRef.current) {
      startTimeDisplayRef.current.textContent = encodeTimeFormat(
        effectiveSelection.startSeconds,
        2
      );
    }
  });

  return (
    <>
      <FormScroller>
        <PanelGroupWrapper>
          <VerticalPanelGroup>
            {lyricsPresence === 'present' && <ReplaceableLyricsPanel />}
            {lyricsPresence === 'empty' && <AddLyricsPanel />}
            <StylesPanel />
            <Panel size={oneFRPanelSize} />
          </VerticalPanelGroup>
        </PanelGroupWrapper>
      </FormScroller>
      <FixedBottomSection>
        <ModuleHeader>
          <div className='flex items-center gap-1'>
            Extend from{' '}
            <TimeDisplay className='h-[32px]' ref={startTimeDisplayRef} />
          </div>
        </ModuleHeader>
        <Footer>
          <div className='flex-1 text-sm text-gray-600' />
          <StudioGenerateButton
            tooltipPlacement='top'
            className='w-full'
            eventTrigger='sidebar'
          />
        </Footer>
      </FixedBottomSection>
    </>
  );
};

export default observer(function StudioGenerateForm() {
  const generateMode = useContextSelector(
    StudioContext,
    (context) => context.generateMode
  );

  if (generateMode === 'replace') {
    return (
      <GenerateFormWrapper>
        <ReplaceForm />
      </GenerateFormWrapper>
    );
  } else if (generateMode === 'create') {
    return (
      <GenerateFormWrapper>
        <CreateForm />
      </GenerateFormWrapper>
    );
  } else if (generateMode === 'extend') {
    return (
      <GenerateFormWrapper>
        <ExtendForm />
      </GenerateFormWrapper>
    );
  } else if (generateMode === null) {
    return (
      <GenerateFormWrapper>
        <div className='flex h-full flex-col items-center justify-center p-2 opacity-50'>
          No edit options for current selection. <br />
          Click and drag in the timeline to select.
        </div>
      </GenerateFormWrapper>
    );
  }
});
