import { useCallback } from 'react';

import { useStores } from '@/app/(root)/AppProviders';

import useUploadFile from './useUploadFile';

const uploadPromisesByFile = new Map<
  File,
  Promise<{
    clipId: string;
    lyrics: any;
    title: any;
    imageUrl: string;
    uploadId: string;
  }>
>();

const clipIdCallbacksByFile = new Map<File, ((clipId: string) => void)[]>();

const useUploadAndInitializeClip = () => {
  const uploadFile = useUploadFile();
  const { clips, library } = useStores();
  const uploadAndInitializeClip = useCallback(
    async (
      file: File,
      type: 'studio_file_upload' | 'file_upload' | 'audio_recording',
      fileNameOverride?: string,
      clipTitleOverride?: string,
      lyricsOverride?: string,
      stemRemixOfClipId?: string,
      combinedFromClipIds?: string[],
      downbeats?: [number, number][],
      onGetClipId?: (clipId: string) => void
    ) => {
      const { uploadId, title, imageUrl } = await uploadFile({
        file,
        type,
        nameOverride: fileNameOverride,
        isStemMix: stemRemixOfClipId !== undefined,
        onGetClipId,
      });

      const { data: initializeResponse, error } =
        stemRemixOfClipId === undefined
          ? await clips.apiClient.POST(
              '/api/uploads/audio/{upload_id}/initialize-clip/',
              {
                params: {
                  path: {
                    upload_id: uploadId!,
                  },
                },
                body: {
                  downbeats,
                },
              }
            )
          : await clips.apiClient.POST(
              '/api/uploads/audio/{upload_id}/initialize-stem-mix-clip',
              {
                params: {
                  path: {
                    upload_id: uploadId!,
                  },
                },
                body: {
                  stem_remix_of_clip_id: stemRemixOfClipId!,
                  combined_from_clip_ids: combinedFromClipIds,
                },
              }
            );

      if (!initializeResponse) {
        throw error;
      } else if (!initializeResponse.clip_id) {
        throw new Error('Failed to initialize clip');
      } else {
        const metadata = {
          clipId: initializeResponse.clip_id,
          imageUrl,
          title: clipTitleOverride || title,
          lyrics: lyricsOverride || undefined,
          isAudioUploadTOSAccepted: true,
          skipNotification: true,
        };

        await library.loadClips();

        const { data: metadataResponse, error } =
          await clips.setMetadata(metadata);

        if (error) {
          throw error;
        } else if (!metadataResponse) {
          throw new Error('Failed to set metadata');
        } else {
          return {
            clipId: initializeResponse.clip_id,
            lyrics: metadataResponse.prompt || '',
            title:
              metadataResponse.title !== undefined &&
              metadataResponse.title !== null
                ? metadataResponse.title
                : title,
            imageUrl,
            uploadId,
          };
        }
      }
    },
    [library, clips]
  );

  const cachedUploadAndInitializeClip = useCallback<
    typeof uploadAndInitializeClip
  >(
    (
      file,
      type,
      fileNameOverride,
      clipTitleOverride,
      lyricsOverride,
      stemRemixOfClipId,
      combinedFromClipIds,
      downbeats,
      onGetClipId
    ) => {
      if (onGetClipId) {
        let clipIdCallbacks = clipIdCallbacksByFile.get(file);
        if (!clipIdCallbacks) {
          clipIdCallbacks = [];
        }
        clipIdCallbacks.push(onGetClipId);
        clipIdCallbacksByFile.set(file, clipIdCallbacks);
      }

      if (uploadPromisesByFile.has(file)) {
        return uploadPromisesByFile.get(file)!;
      } else {
        const promise = uploadAndInitializeClip(
          file,
          type,
          fileNameOverride,
          clipTitleOverride,
          lyricsOverride,
          stemRemixOfClipId,
          combinedFromClipIds,
          downbeats,
          (clipId) => {
            clipIdCallbacksByFile
              .get(file)
              ?.forEach((callback) => callback(clipId));
          }
        );
        uploadPromisesByFile.set(file, promise);
        return promise;
      }
    },
    [uploadAndInitializeClip]
  );

  return cachedUploadAndInitializeClip;
};

export default useUploadAndInitializeClip;
