/**
 * This store is deprecated and will be removed in the future.
 * It is replaced by CreateFormContext.tsx
 */
import { shuffle, uniq } from 'lodash-es';
import { reaction, runInAction } from 'mobx';
import { RefObject } from 'react';

import { accessibleGenres } from '@/app/(root)/create/createV2/genres';
import { toast } from '@/components/toast/Toast';
import { components } from '@/lib/gen';
import {
  DEFAULT_AUDIO_WEIGHT_VALUE,
  DEFAULT_CREATE_CONTROL_VALUE,
  FOUR_MINUTES,
} from '@/utils/constants';
import { RemixHandlerProps } from '@/utils/remixUtils';
import { capitalizeFirstLetter } from '@/utils/utils';

import { Clip, Playlist } from './clipStore';
import { Persona } from './personaStore';
import { RootStore, Substore } from './rootStore';
import { makeAutoObservableSubstore } from './utils';

const CREATE_V2_STATE_STORAGE_KEY = 'createV2-state';
const CREATE_V2_PROMPTS_STORAGE_KEY = 'createV2-prompts';

export enum TagSuggestionContexts {
  SimpleTags = 'simple_tags',
  AdvancedTags = 'advanced_tags',
  AdvancedExcludeTags = 'advanced_exclude_tags',
}

export type ClipPrompt = components['schemas']['ClipPromptSchema'];
export type ControlSliderSchema = components['schemas']['ControlSlidersSchema'];
export type SavedPromptSchema = {
  gptDescriptionPrompt?: string;
  tags?: string;
  negativeTags?: string;
  lyrics?: string;
  title?: string;
  sliders?: ControlSliderSchema;
  id?: string;
  savedPromptId?: string;
};
export enum AudioUploadStatus {
  UPLOADING = 'uploading',
  INITIALIZING = 'initializing',
  VOCALS_DETECTED = 'vocals_detected',
  INVALID_UPLOAD = 'invalid_upload',
  COMPLETE = 'complete',
  CANCELLED = 'cancelled',
  ERROR = 'error',
}

export type ControlSliderKey =
  | 'audio_weight'
  | 'style_weight'
  | 'weirdness_constraint';
export type ControlSliders = { [key in ControlSliderKey]?: number };

export class CreateV2Store implements Substore {
  activeTags: string[] = [];
  activeNegativeTags: string[] = [];
  stylePreviewClipMapping: { [key: string]: Clip[] } = {};
  suggestedTags: { [key in TagSuggestionContexts]: string[] } = {
    [TagSuggestionContexts.SimpleTags]: [],
    [TagSuggestionContexts.AdvancedTags]: [],
    [TagSuggestionContexts.AdvancedExcludeTags]: [],
  };
  activePersona: Persona | null = null;
  activePersonas: Persona[] = [];
  activeLyrics: string = '';
  activeLyricsSubject: string = '';
  suggestedLyrics: string = '';
  suggestedTitle: string | null = null;
  _activeLyricsMode: ('auto' | 'custom' | 'instrumental' | 'mumble_mode')[] = [
    'auto',
    'custom',
  ]; // auto, custom, instrumental
  _activeLyricsModeTab: ('auto' | 'custom' | 'instrumental' | 'mumble_mode')[] =
    ['auto', 'custom']; // auto, custom, instrumental
  activePrompt: string = '';
  // TODO: rename
  coverExtendMode: 'cover' | 'extend' | 'cover_extend' | null = null;
  tagStrength: number = DEFAULT_CREATE_CONTROL_VALUE;
  weirdness: number = DEFAULT_CREATE_CONTROL_VALUE;
  activeContinueAtSeconds: string | null = null;
  isDraggingContinueAtSeconds: boolean = false;
  draggingContinueAtSecondsStartPosition: number | null = null;
  hasDraggedContinueAtSeconds: boolean = false;
  isAddPopoverOpen: boolean = false;
  isSettingsPopoverOpen: boolean = false;
  isMoreOptionsCollapsed: boolean = true;
  isSavedPromptMessageShown: boolean = false;
  addMenuState: 'main' | 'personas' | 'prompts' | 'inspo' = 'main';
  isPregeneratingLyrics: boolean = false;
  isGeneratingTags: boolean = false;
  isGeneratingClip: boolean = false;
  isEditingTitle: boolean = false;
  hasEditedTitle: boolean = false;
  activeTitle: string = '';
  tagInput: string = '';
  augmentedTagInput: string | null = null;
  augmentedTagInputHistory: string[] = [];
  negativeTagInput: string = '';
  top1KGenres: string[] = [];
  genreAdjectives: string[] = [];
  lyricsTopics: string[] = [];
  isSuggestionsHidden: boolean = false;
  isExcludeStylesHidden: boolean = true;
  isExcludeSuggestionsHidden: boolean = false;
  isAdvancedMode: boolean = false;
  isPromptSaveable: boolean = true;
  isSimpleInstrumental: boolean = false;
  promptLibrary: ClipPrompt[] = [];
  audioUploadMode: 'recording' | 'uploading' | null = null;
  promptId: string | null = null;
  promptPlaceholder: string | null = null;
  pendingMetadataInMode: 'custom' | 'auto' | null = null;
  pendingSavedPrompt: SavedPromptSchema | null = null;
  pendingDeletePromptId: string | null = null;
  _lastGenMetadataInMode: {
    custom: {
      gpt_description_prompt?: string | null;
      prompt?: string | null;
      tags?: string | null;
      clip?: Clip;
    } | null;
    auto: {
      gpt_description_prompt?: string | null;
      prompt?: string | null;
      tags?: string | null;
      clip?: Clip;
    } | null;
  } = { custom: null, auto: null };
  reusePromptMode: boolean = false;
  promptInputHeight: number = 0;
  tagInputHeight: number = 0;
  lyricsSectionHeight: number = 0;
  simpleLyricsSectionHeight: number = 0;
  isCreatePersonaModalOpen: boolean = false;
  isAdvancedOptionsCollapsed: boolean = true;
  vocalGender: 'm' | 'f' | null = null;
  selectedProjectId: string | null = null;
  controlSliders: ControlSliders = {
    style_weight: DEFAULT_CREATE_CONTROL_VALUE,
    audio_weight: DEFAULT_AUDIO_WEIGHT_VALUE,
    weirdness_constraint: DEFAULT_CREATE_CONTROL_VALUE,
  };

  selectedRemixClip: Clip | null = null;

  audioUploadStatus: AudioUploadStatus | null = null;
  studioUploadStatus: {
    isUploading: boolean;
    message: string;
    uploads: Map<
      string,
      {
        id: string;
        fileName: string;
        status: 'uploading' | 'failed' | 'completed' | 'cancelled';
        error?: string;
        originalFile?: File;
      }
    >;
    totalUploading: number;
  } | null = null;
  audioUploadError: string | null = null;
  audioUploadOnClipInitialized: ((props: RemixHandlerProps) => void) | null =
    null;
  audioUploadPendingClip: Clip | { title: string; isLoading: true } | null =
    null;
  audioUploadPendingProjectId: string | null = null;
  audioUploadFileConfig: {
    clientSelectedFile: File;
    audioBuffer: AudioBuffer;
    title: string;
  } | null = null;

  speedMultiplier: number = 1;
  keepPitch: boolean = true;

  conditioningPlaylist: Playlist | null = null;
  enablePersonalization: boolean = false;

  overpaintingClip: Clip | null = null;
  overpaintingStartSeconds: number | null = null;
  overpaintingEndSeconds: number | null = null;

  underpaintingClip: Clip | null = null;
  underpaintingStartSeconds: number | null = null;
  underpaintingEndSeconds: number | null = null;

  isChatMode: boolean = false;
  mumbleMode: boolean = false;

  readonly root: RootStore;
  get apiClient() {
    return this.root.apiClient;
  }
  get logger() {
    return this.root.logger;
  }
  // DO NOT ADD INIT LOGIC IN constructor. this will run on every page! considering adding any it in the component init logic instead
  constructor(root: RootStore) {
    this.root = root;
    makeAutoObservableSubstore(this);
  }

  // this will run on every page! considering adding any it in the component init logic instead
  initialize() {
    this.loadFromLocalStorage();
    this.initLocalStorageUpdate();
  }

  storageObject = () => {
    return {
      isAdvancedMode: this.isAdvancedMode,
      isSuggestionsHidden: this.isSuggestionsHidden,
      isExcludeStylesHidden: this.isExcludeStylesHidden,
      isExcludeSuggestionsHidden: this.isExcludeSuggestionsHidden,
      promptInputHeight: this.promptInputHeight,
      tagInputHeight: this.tagInputHeight,
      lyricsSectionHeight: this.lyricsSectionHeight,
      simpleLyricsSectionHeight: this.simpleLyricsSectionHeight,
      isAdvancedOptionsCollapsed: this.isAdvancedOptionsCollapsed,
    };
  };

  initLocalStorageUpdate = () => {
    reaction(this.storageObject, () => {
      if (this.root.isLocalStorageAvailable) {
        const storageStr = JSON.stringify(this.storageObject());
        localStorage.setItem(CREATE_V2_STATE_STORAGE_KEY, storageStr);
      }
    });
  };

  loadFromLocalStorage = () => {
    if (this.root.isLocalStorageAvailable) {
      const storageStr = localStorage.getItem(CREATE_V2_STATE_STORAGE_KEY);
      try {
        const stateJson = JSON.parse(storageStr || '');
        if (stateJson) {
          runInAction(() => {
            this.isAdvancedMode = stateJson.isAdvancedMode;
            this.isSuggestionsHidden = stateJson.isSuggestionsHidden;
            this.promptInputHeight = stateJson.promptInputHeight;
            this.tagInputHeight = stateJson.tagInputHeight;
            this.lyricsSectionHeight = stateJson.lyricsSectionHeight;
            this.simpleLyricsSectionHeight =
              stateJson.simpleLyricsSectionHeight;
            this.isAdvancedOptionsCollapsed =
              stateJson.isAdvancedOptionsCollapsed;

            // this.isExcludeSuggestionsHidden =
            //   stateJson.isExcludeSuggestionsHidden;
            // this.isExcludeStylesHidden = stateJson.isExcludeStylesHidden;
          });
        }
      } catch (e) {}
    }
  };

  get activeLyricsMode() {
    return this._activeLyricsMode[this.isAdvancedMode ? 1 : 0] as
      | 'auto'
      | 'custom'
      | 'instrumental'
      | 'mumble_mode';
  }

  get activeLyricsModeTab() {
    return this._activeLyricsModeTab[this.isAdvancedMode ? 1 : 0] as
      | 'auto'
      | 'custom'
      | 'instrumental'
      | 'mumble_mode';
  }

  setActiveTags(tags: string[]) {
    this.activeTags = tags;
  }

  setActiveNegativeTags(tags: string[]) {
    this.activeNegativeTags = tags;
  }

  setSuggestedTags(tags: { [key in TagSuggestionContexts]: string[] }) {
    this.suggestedTags = { ...this.suggestedTags, ...tags };
  }

  setActivePersona(persona: Persona | null) {
    this.activePersona = persona;
    if (persona) {
      this.activePersonas = [persona];
    } else {
      this.activePersonas = [];
    }
  }

  addActivePersona(persona: Persona) {
    if (!this.activePersonas.find((p) => p.id === persona.id)) {
      this.activePersonas.push(persona);
      this.activePersona = persona;
    }
  }

  removeActivePersona(personaId: string) {
    this.activePersonas = this.activePersonas.filter((p) => p.id !== personaId);
    this.activePersona =
      this.activePersonas[this.activePersonas.length - 1] || null;
  }

  clearActivePersonas() {
    this.activePersonas = [];
    this.activePersona = null;
  }

  setActiveLyrics(lyrics: string) {
    this.activeLyrics = lyrics;
  }

  setSuggestedLyrics(lyrics: string) {
    this.suggestedLyrics = lyrics;
  }

  setStylePreviewClipMapping(clipMapping: { [key: string]: Clip[] }) {
    this.stylePreviewClipMapping = clipMapping;
  }

  setWeirdness(weirdness: number) {
    this.weirdness = weirdness;
  }

  setTagStrength(tagStrength: number) {
    this.tagStrength = tagStrength;
  }

  setActiveLyricsMode(
    mode: 'auto' | 'custom' | 'instrumental' | 'mumble_mode'
  ) {
    this._activeLyricsMode[this.isAdvancedMode ? 1 : 0] = mode;
  }

  setActiveLyricsModeTab(
    mode: 'auto' | 'custom' | 'instrumental' | 'mumble_mode'
  ) {
    this._activeLyricsModeTab[this.isAdvancedMode ? 1 : 0] = mode;
  }

  setActivePrompt(prompt: string) {
    if (prompt.length <= 200) {
      this.activePrompt = prompt;
    }
  }

  setIsDraggingContinueAtSeconds(isDragging: boolean) {
    this.isDraggingContinueAtSeconds = isDragging;
  }

  setDraggingContinueAtSecondsStartPosition(position: number | null) {
    this.draggingContinueAtSecondsStartPosition = position;
  }

  setHasDraggedContinueAtSeconds(hasDragged: boolean) {
    this.hasDraggedContinueAtSeconds = hasDragged;
  }

  setCoverExtendMode(mode: 'cover' | 'extend' | 'cover_extend' | null) {
    this.coverExtendMode = mode;
  }

  setIsAddPopoverOpen(isOpen: boolean) {
    this.isAddPopoverOpen = isOpen;
  }

  setIsSettingsPopoverOpen(isOpen: boolean) {
    this.isSettingsPopoverOpen = isOpen;
  }

  setAddMenuState(state: 'main' | 'personas' | 'prompts' | 'inspo') {
    this.addMenuState = state;
  }

  setActiveContinueAtSeconds(continueAtSeconds: string | null) {
    this.activeContinueAtSeconds = continueAtSeconds;
  }

  setSuggestedTitle(title: string | null) {
    this.suggestedTitle = title;
  }

  setIsPregeneratingLyrics(isPregeneratingLyrics: boolean) {
    this.isPregeneratingLyrics = isPregeneratingLyrics;
  }

  setTagInput(input: string) {
    this.tagInput = input;
  }

  setAugmentedTagInput(input: string | null) {
    this.augmentedTagInput = input;
  }

  setNegativeTagInput(input: string) {
    this.negativeTagInput = input;
  }

  pushAugmentedTagInput(input: string) {
    this.augmentedTagInputHistory.push(input);
    this.setAugmentedTagInput(input);
  }

  clearAugmentedTagInputHistory() {
    this.augmentedTagInputHistory = [];
  }

  popAugmentedTagInput() {
    this.augmentedTagInputHistory.pop();
    if (this.augmentedTagInputHistory.length > 0) {
      this.setAugmentedTagInput(
        this.augmentedTagInputHistory[this.augmentedTagInputHistory.length - 1]
      );
    } else {
      this.setAugmentedTagInput(null);
    }
  }

  async getRecommendedStyles(styles: string[]) {
    const { data } = await this.apiClient.POST('/api/tags/recommend', {
      body: {
        tags: styles,
      },
    });
    const recs: string[] = data?.recommended_tags || [];
    return recs;
  }

  async getRandomGenres() {
    const accessibleGenresSample = shuffle(accessibleGenres).slice(0, 5);
    const { data } = await this.apiClient.POST('/api/tags/recommend', {
      body: {
        tags: [],
      },
    });

    const returnGenres = uniq([
      ...accessibleGenresSample,
      ...(data?.recommended_tags || []),
    ]) as string[];
    return [...shuffle(returnGenres.slice(0, 10)), ...returnGenres.slice(10)];
  }

  setIsSuggestionsHidden(isHidden: boolean) {
    this.isSuggestionsHidden = isHidden;
  }

  setIsExcludeSuggestionsHidden(isHidden: boolean) {
    this.isExcludeSuggestionsHidden = isHidden;
  }

  setIsAdvancedMode(isAdvancedMode: boolean) {
    this.isAdvancedMode = isAdvancedMode;
  }

  setActiveTitle(title: string) {
    this.activeTitle = title;
  }

  setIsEditingTitle(isEditing: boolean) {
    this.isEditingTitle = isEditing;
  }

  setHasEditedTitle(hasEdited: boolean) {
    this.hasEditedTitle = hasEdited;
  }

  setIsGeneratingTags(isGenerating: boolean) {
    this.isGeneratingTags = isGenerating;
  }

  setActiveLyricsSubject(lyricsSubject: string) {
    this.activeLyricsSubject = lyricsSubject;
  }

  setIsExcludeStylesHidden(isHidden: boolean) {
    this.isExcludeStylesHidden = isHidden;
  }

  setPromptLibrary(prompts: ClipPrompt[]) {
    this.promptLibrary = prompts;
  }

  reuseClipPrompt(clip: Clip) {
    this.setIsAdvancedMode(true);
    this.setTagInput(clip.metadata?.tags || '');
    this.setAugmentedTagInput(null);
    if (clip.metadata?.negative_tags) {
      this.setNegativeTagInput(clip.metadata?.negative_tags || '');
      this.setIsExcludeStylesHidden(false);
    }
    this.setActiveLyricsMode('custom');
    this.setActiveLyricsModeTab('custom');
    this.setActiveLyrics(clip.metadata?.prompt || '');
    this.setActiveTitle(clip.title || '');
  }

  setPromptId(promptId: string | null) {
    this.promptId = promptId;
  }

  getSavedPrompts() {
    if (this.root.isLocalStorageAvailable) {
      const promptsStr = localStorage.getItem(CREATE_V2_PROMPTS_STORAGE_KEY);
      const promptsData = JSON.parse(promptsStr || '');
      return promptsData;
    }
    return [];
  }

  async savePrompt(prompt: SavedPromptSchema, elementRef?: RefObject<any>) {
    // const { id } = prompt;
    // if (this.root.isLocalStorageAvailable) {
    //   const promptsStr = localStorage.getItem(CREATE_V2_PROMPTS_STORAGE_KEY);
    //   const promptsData = JSON.parse(promptsStr || '');
    //   if (promptsData) {
    //     const editingIndex = promptsData.findIndex(
    //       (prompt: any) => prompt.id === id
    //     );
    //     if (editingIndex > -1) {
    //       promptsData[editingIndex] = {
    //         ...promptsData[editingIndex],
    //         ...prompt,
    //       };
    //     } else {
    //       promptsData.push(prompt);
    //     }
    //     localStorage.setItem(
    //       CREATE_V2_PROMPTS_STORAGE_KEY,
    //       JSON.stringify([...promptsData])
    //     );
    //   }
    // }
    const response = await this.apiClient.POST('/api/prompts/', {
      body: {
        gpt_description_prompt: prompt.gptDescriptionPrompt,
        lyrics: prompt.lyrics,
        tags: prompt.tags,
        negative_tags: prompt.negativeTags,
        title: prompt.title,
        sliders: prompt.sliders,
        id: prompt.id,
      },
    });
    if (!!response.data) {
      // async fetch prompt library
      // TODO refactor this to have the POST return the new prompt
      // and prepend it to the prompt library, more efficient
      await this.fetchPromptLibrary();
      toast({
        title: 'Prompt saved.',
        status: 'info',
        duration: 2000,
        isClosable: true,
      });
      const animationClass = 'animate-[medium-bounce_0.5s_ease-in-out_2]';
      if (elementRef?.current) {
        elementRef?.current.classList.remove(animationClass);
        void elementRef?.current.offsetWidth;
        elementRef?.current.classList.add(animationClass);
      }
      this.isSavedPromptMessageShown = true;
      setTimeout(() => {
        this.isSavedPromptMessageShown = false;
      }, 5000);
    } else if (!!response.error) {
      toast({
        title: 'Prompt could not be saved. Try again.',
        status: 'error',
        duration: 2000,
        isClosable: true,
      });
    }
    if ((response as any)?.data?.id) {
      this.promptId = (response as any)?.data?.id;
    }
  }

  getPromptPlaceholder() {
    if (!this.top1KGenres.length || !this.lyricsTopics.length) {
      return null;
    }
    const adjective =
      this.genreAdjectives[
        Math.floor(Math.random() * this.genreAdjectives.length)
      ];
    const style =
      this.top1KGenres[Math.floor(Math.random() * this.top1KGenres.length)];
    const prompt = `song about ${this.lyricsTopics[Math.floor(Math.random() * this.lyricsTopics.length)]}`;
    return capitalizeFirstLetter(`${adjective} ${style} ${prompt}`);
  }

  setLastGenMetadataInMode(
    metadata: {
      gpt_description_prompt?: string | null;
      prompt?: string | null;
      tags?: string | null;
      clip?: Clip;
    } | null,
    _mode?: 'custom' | 'auto'
  ) {
    //const mode = this.isAdvancedMode ? 'custom' : 'auto';
    const mode = _mode || this.pendingMetadataInMode;
    if (mode) {
      this._lastGenMetadataInMode[mode] = metadata;
    }
  }

  get lastGenMetadataInMode() {
    return this._lastGenMetadataInMode[this.isAdvancedMode ? 'custom' : 'auto'];
  }

  setPendingMetadataInMode(
    mode: 'custom' | 'auto' | null = this.isAdvancedMode ? 'custom' : 'auto'
  ) {
    this.pendingMetadataInMode = mode;
    this.setLastGenMetadataInMode(null);
  }

  setIsGeneratingClip(isGenerating: boolean) {
    this.isGeneratingClip = isGenerating;
  }

  setPendingSavedPrompt(prompt: SavedPromptSchema) {
    this.pendingSavedPrompt = prompt;
  }

  setPendingDeletePromptId(promptId: string | null) {
    this.pendingDeletePromptId = promptId;
  }

  setIsPromptSaveable(isSaveable: boolean) {
    this.isPromptSaveable = isSaveable;
  }

  setIsSimpleInstrumental(isSimpleInstrumental: boolean) {
    this.isSimpleInstrumental = isSimpleInstrumental;
  }

  async fetchPromptLibrary() {
    const { data } = await this.apiClient.GET('/api/clips/clip_prompts/');
    this.setPromptLibrary(data?.prompts || []);
  }

  async deletePrompt(savedPromptId: string) {
    const { response } = await this.apiClient.POST('/api/prompts/delete/', {
      body: { id: savedPromptId },
    });
    if (response.ok) {
      this.setPromptLibrary(
        (this.promptLibrary || []).filter(
          (prompt: ClipPrompt) => prompt.saved_prompt_id !== savedPromptId
        )
      );
    }
  }

  setIsMoreOptionsCollapsed(isCollapsed: boolean) {
    this.isMoreOptionsCollapsed = isCollapsed;
  }

  setIsAdvancedOptionsCollapsed(isCollapsed: boolean) {
    this.isAdvancedOptionsCollapsed = isCollapsed;
  }

  setReusePromptMode(mode: boolean) {
    this.reusePromptMode = mode;
  }

  setPromptInputHeight(height: number) {
    this.promptInputHeight = height;
  }

  setTagInputHeight(height: number) {
    this.tagInputHeight = height;
  }

  setLyricsSectionHeight(height: number) {
    this.lyricsSectionHeight = height;
  }

  setSimpleLyricsSectionHeight(height: number) {
    this.simpleLyricsSectionHeight = height;
  }

  setIsCreatePersonaModalOpen(isOpen: boolean) {
    this.isCreatePersonaModalOpen = isOpen;
  }

  setSelectedRemixClip(clip: Clip | null) {
    this.selectedRemixClip = clip;
  }

  setVocalGender(gender: 'm' | 'f' | null) {
    this.vocalGender = gender;
  }

  setSelectedProjectId(projectId: string | null) {
    this.selectedProjectId = projectId;
  }

  setStudioUploadStatus(
    status: { isUploading: boolean; message: string } | null
  ) {
    if (status === null) {
      this.studioUploadStatus = null;
    } else {
      this.studioUploadStatus = {
        ...status,
        uploads: new Map(),
        totalUploading: 0,
      };
    }
  }

  updateStudioUploadStatusForClip(options: {
    clipId: string;
    fileName: string;
    status: 'uploading' | 'failed' | 'completed' | 'cancelled';
    error?: string;
    originalFile?: File;
  }) {
    const { clipId, fileName, status, error, originalFile } = options;
    if (!this.studioUploadStatus) {
      this.studioUploadStatus = {
        isUploading: true,
        message: '',
        uploads: new Map(),
        totalUploading: 0,
      };
    }

    const uploads = this.studioUploadStatus.uploads;

    if (status === 'completed' || status === 'cancelled') {
      // Remove completed/cancelled uploads from tracking
      uploads.delete(clipId);
    } else {
      uploads.set(clipId, {
        id: clipId,
        fileName,
        status,
        error,
        originalFile,
      });
    }

    // Update totalUploading count
    this.studioUploadStatus.totalUploading = Array.from(
      uploads.values()
    ).filter((upload) => upload.status === 'uploading').length;

    // Update overall uploading state
    this.studioUploadStatus.isUploading =
      this.studioUploadStatus.totalUploading > 0;

    // Update message based on state
    if (this.studioUploadStatus.totalUploading > 0) {
      if (this.studioUploadStatus.totalUploading === 1) {
        // Show filename for single upload
        const uploadingFile = Array.from(uploads.values()).find(
          (upload) => upload.status === 'uploading'
        );
        this.studioUploadStatus.message = uploadingFile
          ? `${uploadingFile.fileName} is coming`
          : 'Uploading...';
      } else {
        // Show count for multiple uploads
        this.studioUploadStatus.message = `${this.studioUploadStatus.totalUploading} Files Uploading...`;
      }
    } else {
      this.studioUploadStatus.isUploading = false;
    }

    // Clear entire status if no uploads are being tracked
    if (uploads.size === 0) {
      this.studioUploadStatus = null;
    }
  }

  removeStudioUpload(clipId: string) {
    if (this.studioUploadStatus?.uploads) {
      this.studioUploadStatus.uploads.delete(clipId);

      // Update counts and clear if empty
      const uploads = this.studioUploadStatus.uploads;
      this.studioUploadStatus.totalUploading = Array.from(
        uploads.values()
      ).filter((upload) => upload.status === 'uploading').length;

      this.studioUploadStatus.isUploading =
        this.studioUploadStatus.totalUploading > 0;

      if (uploads.size === 0) {
        this.studioUploadStatus = null;
      }
    }
  }

  retryStudioUpload(clipId: string) {
    if (this.studioUploadStatus?.uploads.has(clipId)) {
      const upload = this.studioUploadStatus.uploads.get(clipId);
      if (upload && upload.status === 'failed' && upload.originalFile) {
        // Remove the failed upload from tracking
        this.studioUploadStatus.uploads.delete(clipId);

        // Add a new upload entry to show progress immediately
        const newUploadId = `retry-${Date.now()}`;
        this.updateStudioUploadStatusForClip({
          clipId: newUploadId,
          fileName: upload.fileName,
          status: 'uploading',
          originalFile: upload.originalFile,
        });

        // Start actual file re-upload
        this.performFileReupload(
          upload.originalFile,
          upload.fileName,
          newUploadId
        );
      }
    }
  }

  private async performFileReupload(
    file: File,
    fileName: string,
    trackingId: string
  ) {
    try {
      const UppyAudio = (await import('@uppy/audio')).default;
      const S3 = (await import('@uppy/aws-s3')).default;
      const Uppy = (await import('@uppy/core')).default;

      let uploadId: string | null = null;

      const uppy = new Uppy({
        restrictions: {
          maxNumberOfFiles: 1,
          maxFileSize: 500 * 1024 * 1024,
        },
      })
        .use(UppyAudio)
        .use(S3, {
          getUploadParameters: async (uppyFile: any) => {
            const { data, error } = await this.apiClient.POST(
              '/api/uploads/audio/',
              {
                body: {
                  extension: uppyFile.extension,
                  is_stem_mix: false,
                },
              }
            );

            if (error === 'copyright_infringment') {
              throw new Error('Copyright infringement detected');
            }

            if (!data) throw new Error('Failed to fetch upload parameters');

            uploadId = data.id;

            // Update tracking with real upload ID
            if (this.studioUploadStatus?.uploads.has(trackingId)) {
              this.studioUploadStatus.uploads.delete(trackingId);
              this.updateStudioUploadStatusForClip({
                clipId: uploadId,
                fileName,
                status: 'uploading',
                originalFile: file,
              });
            }

            return {
              url: data?.url,
              fields: data.fields as Record<string, never>,
            };
          },
        });

      uppy.on('complete', async () => {
        if (!uploadId) return;

        // Check if upload was cancelled
        if (this.isStudioUploadCancelled(uploadId)) {
          console.log('Upload cancelled, skipping finalize API call');
          return;
        }

        try {
          const { data } = await this.apiClient.POST(
            '/api/uploads/audio/{upload_id}/upload-finish/',
            {
              params: { path: { upload_id: uploadId } },
              body: {
                upload_type: 'studio_file_upload',
                upload_filename: fileName,
              },
            }
          );

          if (data) {
            // Poll for completion
            this.pollUploadStatus(uploadId, fileName, file);
          }
        } catch (error) {
          console.error('Upload finish failed:', error);
          this.updateStudioUploadStatusForClip({
            clipId: uploadId,
            fileName,
            status: 'failed',
            error: 'Upload finish failed',
          });
        }
      });

      uppy.on('upload-error', (file, error) => {
        console.error('Upload error:', error);
        if (uploadId) {
          this.updateStudioUploadStatusForClip({
            clipId: uploadId,
            fileName,
            status: 'failed',
            error: 'Upload failed',
          });
        }
      });

      uppy.addFile({
        name: fileName,
        type: file.type,
        data: file,
      });

      uppy.upload();
    } catch (error) {
      console.error('Reupload failed:', error);
      this.updateStudioUploadStatusForClip({
        clipId: trackingId,
        fileName,
        status: 'failed',
        error: `Reupload failed: ${error}`,
      });
    }
  }

  private async pollUploadStatus(
    uploadId: string,
    fileName: string,
    file: File,
    retries = 75
  ) {
    try {
      const { data } = await this.apiClient.GET(
        '/api/uploads/audio/{upload_id}/',
        {
          params: { path: { upload_id: uploadId } },
        }
      );

      if (data?.status === 'error') {
        this.updateStudioUploadStatusForClip({
          clipId: uploadId,
          fileName,
          status: 'failed',
          error: data?.error_message || 'Processing failed',
          originalFile: file,
        });
        return;
      }

      if (data?.status !== 'complete') {
        if (retries > 0) {
          setTimeout(
            () => this.pollUploadStatus(uploadId, fileName, file, retries - 1),
            4000
          );
        } else {
          this.updateStudioUploadStatusForClip({
            clipId: uploadId,
            fileName,
            status: 'failed',
            error: 'Upload processing timed out',
            originalFile: file,
          });
        }
      } else {
        // Upload completed successfully
        this.updateStudioUploadStatusForClip({
          clipId: uploadId,
          fileName,
          status: 'completed',
        });
      }
    } catch (error) {
      console.error('Polling failed:', error);
      this.updateStudioUploadStatusForClip({
        clipId: uploadId,
        fileName,
        status: 'failed',
        error: 'Status check failed',
        originalFile: file,
      });
    }
  }

  cancelStudioUpload(clipId: string) {
    if (this.studioUploadStatus?.uploads.has(clipId)) {
      const upload = this.studioUploadStatus.uploads.get(clipId);
      if (upload) {
        // Mark as cancelled to prevent finalize API call
        this.updateStudioUploadStatusForClip({
          clipId,
          fileName: upload.fileName,
          status: 'cancelled',
        });
      }
    }
  }

  isStudioUploadCancelled(clipId: string): boolean {
    return this.studioUploadStatus?.uploads.get(clipId)?.status === 'cancelled';
  }

  setAudioUploadError(errorMessage: string | null) {
    this.audioUploadError = errorMessage;
  }

  /* END AUDIO UPLOAD RELATED STATE */

  setSpeedMultiplier(multiplier: number) {
    this.speedMultiplier = multiplier;
  }

  setKeepPitch(keepPitch: boolean) {
    this.keepPitch = keepPitch;
  }

  setControlSlider(sliderKey: ControlSliderKey, value: number) {
    this.controlSliders[sliderKey] = value;
  }

  resetControlSliders = () => {
    this.setControlSlider('weirdness_constraint', DEFAULT_CREATE_CONTROL_VALUE);
    this.setControlSlider('style_weight', DEFAULT_CREATE_CONTROL_VALUE);
    this.setControlSlider('audio_weight', DEFAULT_AUDIO_WEIGHT_VALUE);
  };

  setConditioningPlaylist(conditioningPlaylist: Playlist | null) {
    this.conditioningPlaylist = conditioningPlaylist;
  }

  setUnderpaintingClip(underpaintingClip: Clip | null) {
    this.underpaintingClip = underpaintingClip;
    const isInstrumentalStem =
      underpaintingClip?.metadata?.stem_type_group_name === 'Instrumental';
    const lyrics =
      underpaintingClip?.metadata?.prompt ||
      (isInstrumentalStem
        ? (underpaintingClip?.metadata as any)?.['continued_from_prompt']
        : '') ||
      '';
    this.setActiveLyrics(lyrics);
    this.root.genForm.setLyrics(lyrics);
    // Clear overpainting clip when setting underpainting
    if (underpaintingClip) {
      this.overpaintingClip = null;
      // Set audio weight to 75% for underpainting
      this.setControlSlider('audio_weight', 75);
    }

    this.underpaintingStartSeconds = 0;

    const clipDuration = underpaintingClip?.metadata?.duration || 0;

    this.underpaintingEndSeconds =
      clipDuration <= FOUR_MINUTES ? clipDuration : FOUR_MINUTES;
  }

  setMumbleMode(mumbleMode: boolean) {
    this.mumbleMode = mumbleMode;
  }

  clearMumbleMode() {
    this.mumbleMode = false;
  }

  setOverpaintingClip(overpaintingClip: Clip | null) {
    this.overpaintingClip = overpaintingClip;
    this.setTagInput(overpaintingClip?.metadata?.tags || '');
    this.root.genForm.setStyle(overpaintingClip?.metadata?.tags || '');
    // Clear underpainting clip when setting overpainting
    if (overpaintingClip) {
      this.resetUnderpainting();
      // Set audio weight to 75% for overpainting
      this.setControlSlider('audio_weight', 75);
    }
    this.overpaintingStartSeconds = 0;
    this.overpaintingEndSeconds = Math.min(
      FOUR_MINUTES,
      overpaintingClip?.metadata?.duration || 0
    );
  }

  setEnablePersonalization(enablePersonalization: boolean) {
    this.enablePersonalization = enablePersonalization;
  }

  resetPlaylistConditioning = () => {
    this.conditioningPlaylist = null;
  };

  resetPainting = () => {
    this.resetUnderpainting();
    this.resetOverpainting();
  };

  resetUnderpainting = () => {
    this.underpaintingClip = null;
    this.underpaintingStartSeconds = null;
    this.underpaintingEndSeconds = null;
  };

  resetOverpainting = () => {
    this.overpaintingClip = null;
    this.overpaintingStartSeconds = null;
    this.overpaintingEndSeconds = null;
  };

  setIsChatMode = (isChatMode: boolean) => {
    this.isChatMode = isChatMode;
  };

  get hasAudioCondition() {
    return (
      this.coverExtendMode !== null ||
      this.activePersona !== null ||
      this.conditioningPlaylist !== null ||
      this.underpaintingClip !== null ||
      this.overpaintingClip !== null
    );
  }
}
