import UppyAudio from '@uppy/audio';
import S3 from '@uppy/aws-s3';
import Uppy, { UppyFile } from '@uppy/core';
import { useCallback } from 'react';

import { useStores } from '@/app/(root)/AppProviders';
import { ModalTypes } from '@/components/modal/constants/ModalTypes';
import { toast } from '@/components/toast/Toast';
import { eventLogger } from '@/utils/event-logger';
import { ActionName } from '@/utils/event-names';

export interface UploadFileOptions {
  file: File;
  type: 'studio_file_upload' | 'file_upload' | 'audio_recording';
  nameOverride?: string;
  isStemMix?: boolean;
  onGetClipId?: (clipId: string) => void;
  getUploadCancelled?: (id: string) => boolean;
}

const useUploadFile = () => {
  const { session, genForm, menus } = useStores();
  const uploadFile = useCallback(async (options: UploadFileOptions) => {
    const {
      file,
      type,
      nameOverride,
      isStemMix,
      onGetClipId,
      getUploadCancelled,
    } = options;
    return await new Promise<{
      uploadId: string;
      title: string;
      imageUrl: string;
      hasVocal?: boolean;
      errorMessage?: string;
    }>((resolve, reject) => {
      let uploadId: string | null = null;

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

            if (error == 'copyright_infringment') {
              menus.openModal(ModalTypes.COPYRIGHT_WARNING);
              throw new Error('Copyright infringement detected');
            }

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

            uploadId = data.id;
            onGetClipId?.(uploadId);

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

      const pollUploadStatus = async (retries = 75) => {
        const { data } = await session.apiClient.GET(
          '/api/uploads/audio/{upload_id}/',
          {
            params: { path: { upload_id: uploadId! } },
          }
        );

        if (data?.status === 'error') {
          const error = data?.error_message;
          toast({
            title: data?.error_message,
            description: 'Please upload a different audio file.',
            status: 'error',
            duration: null,
            isClosable: true,
          });
          eventLogger.logAudioCreationEvent(
            genForm.isMobile,
            ActionName.uploadSong,
            genForm,
            session,
            {
              uploadId: uploadId,
              errorMessage: error,
              isUploaded: false,
              uploadType: type,
            }
          );
          resolve({
            uploadId: uploadId!,
            title: (data as any).title || undefined,
            imageUrl: (data as any).image_url || undefined,
            errorMessage: error ?? undefined,
          });
          return;
        }

        if (data?.status !== 'complete') {
          if (retries > 0) {
            setTimeout(() => pollUploadStatus(retries - 1), 4000);
          } else {
            reject('Audio processing timed out. Please try again.');
          }
        } else {
          eventLogger.logAudioCreationEvent(
            genForm.isMobile,
            ActionName.uploadSong,
            genForm,
            session,
            {
              uploadId: uploadId,
              isUploaded: true,
              uploadType: type,
            }
          );
          resolve({
            uploadId: uploadId!,
            title: (data as any).title || undefined,
            imageUrl: (data as any).image_url || undefined,
            hasVocal: (data as any).has_vocal || false,
          });
        }
      };

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

        // Check if upload was cancelled before finalizing
        if (getUploadCancelled && getUploadCancelled(uploadId)) {
          console.log('Upload cancelled, skipping finalize API call');
          return;
        }

        const { data } = await session.apiClient.POST(
          '/api/uploads/audio/{upload_id}/upload-finish/',
          {
            params: { path: { upload_id: uploadId } },
            body: {
              upload_type: type,
              upload_filename: nameOverride || file.name,
            },
          }
        );

        if (data) {
          pollUploadStatus();
        }
      });

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

      uppy.upload();
    });
  }, []);

  return uploadFile;
};

export default useUploadFile;
