'use client';

import { Modal, ModalContent, ModalOverlay } from '@chakra-ui/react';
import clsx from 'clsx';
import { observer } from 'mobx-react-lite';
import { usePathname } from 'next/navigation';
import {
  useCallback,
  useContext,
  useEffect,
  useMemo,
  useRef,
  useState,
} from 'react';

import { useStores } from '@/app/(root)/AppProviders';
import { UploadState } from '@/app/(root)/create/sandbox/VideoUploadState';
import { useGenerationNotifications } from '@/app/(root)/videogen/GenerationNotificationProvider';
import ContestShareModal from '@/components/contest/ContestShareModal';
import { DEFAULT_CAROUSEL_STATE } from '@/components/modal/GenerateCoverArtModal';
import { ModalTypes } from '@/components/modal/constants/ModalTypes';
import { AddMediaModalContent } from '@/components/modal/editClipMetadata/AddMediaModalContent';
import EditLyricsScreen from '@/components/modal/publishSong/EditLyricsScreen';
import MoreOptionsScreen from '@/components/modal/publishSong/MoreOptionsScreen';
import PublishMainContentScreen from '@/components/modal/publishSong/PublishMainContentScreen';
import { useModalContext } from '@/context/ModalContext';
import ContestClipContext from '@/hooks/useContestClip';
import useEditClipMetadata from '@/hooks/useEditClipMetadata';
import SongModalContext, { useSongModal } from '@/hooks/useSongModal';
import useUploadImageFile from '@/hooks/useUploadImageFile';
import { useApiClient } from '@/lib/apiClient';
import logWebUserEvent from '@/logging/logWebUserEvent';
import { getClipTitle } from '@/utils/clip';
import { DEFAULT_AURA_URL } from '@/utils/constants';
import { parseMentions, reconstructContent } from '@/utils/mentions';

import GenerateCoverArtModalContent from './editClipMetadata/GenerateCoverArtModalContent';
import GenerationFavoritesModalContent from './editClipMetadata/GenerateFavoritesModalContent';
import GenerationCarouselModalContent from './editClipMetadata/GenerationCarouselModalContent';

const PublishSongModal = observer(() => {
  const { clips, library, session } = useStores();
  const apiClient = useApiClient();
  const uploadImageFile = useUploadImageFile();
  const { closeModal, isModalOpen, openModalWithData, getModalData } =
    useModalContext();
  const { enablePendingToasts } = useGenerationNotifications();

  // Determine which modal is open (PUBLISH_SONG or UPDATE_CLIP_METADATA)
  const isPublishModalOpen = isModalOpen(ModalTypes.PUBLISH_SONG);
  const isUpdateModalOpen = isModalOpen(ModalTypes.UPDATE_CLIP_METADATA);
  const isOpen = isPublishModalOpen || isUpdateModalOpen;
  const modalType = isPublishModalOpen
    ? ModalTypes.PUBLISH_SONG
    : ModalTypes.UPDATE_CLIP_METADATA;

  // Create song modal context
  const songModalContextValue = useSongModal({ modalType });

  const {
    content: originalReconstructedCaption,
    mentionsUsedMap: originalMentionsUsedMap,
  } = useMemo(() => {
    return reconstructContent(
      library.activeClip?.caption || '',
      library.activeClip?.caption_mentions?.user_mentions || []
    );
  }, [
    library.activeClip?.caption,
    library.activeClip?.caption_mentions?.user_mentions,
  ]);

  const modalData = getModalData(modalType);
  const clipId = modalData?.clipId || library.activeClip?.id || '';
  const pathname = usePathname();

  const {
    currentScreen,
    setCurrentScreen,
    navigateTo,
    navigationState,
    setNavigationState,
  } = songModalContextValue;

  const {
    editedTitle,
    setEditedTitle,
    s3Id,
    setS3Id,
    setEditedImagePrompt,
    currentImage,
    setCurrentImage,
    setIsGenerating,
    addedImages,
    setAddedImages,
    setAddingImageDataURL,
    setAddingImageChanged,
    continuedClips,
    setContinuedClips,
    setInitialImageURL,
    currentActiveClip,
    setCurrentActiveClip,
    currentLyrics,
    setCurrentLyrics,
    currentCaption,
    setCurrentCaption,
    currentCaptionMentions,
    setCurrentCaptionMentions,
    isAddingMedia,
    setIsAddingMedia,
    selectedVideo,
    setSelectedVideo,
    displayTags,
    setDisplayTags,
    submitToContest,
    setSubmitToContest,
    isGeneratingCover,
    setIsGeneratingCover,
    navigateToGenerationCarousel,
    isViewingGenerateHistory,
    setIsViewingGenerateHistory,
    isViewingGenerationFavorites,
    setIsViewingGenerationFavorites,
  } = useEditClipMetadata(library.activeClip, navigateTo);

  const [mentionQuery, setMentionQuery] = useState<string | null>(null);
  const [hashtagQuery, setHashtagQuery] = useState<string | null>(null);
  const [selectedMedia, setSelectedMedia] = useState<File | null>(null);
  const [selectedMediaType, setSelectedMediaType] = useState<
    'image' | 'video' | null
  >(null);

  const stateRef = useRef({
    currentCaption,
    mentionQuery,
    mentionSuggestion: {} as {
      handle: string | null;
      displayName: string | null;
      isExact: boolean;
    },
    mentionsUsedMap: new Map<string, string>(),
    hashtagQuery,
    hashtagSuggestion: null as string | null,
  });

  const [hasUpdatedLyrics, setHasUpdatedLyrics] = useState(false);
  const [isPinned, setIsPinned] = useState(clips.isClipPinned(clipId));
  // Create uploadState instance
  const uploadState = useMemo(
    () =>
      new UploadState(apiClient, {
        isVideoCover: true,
        requireVideoSprite: false,
        requireModeration: true,
        videoUploadType: 'video_cover',
      }),
    [apiClient]
  );

  const { isContestSubmission, isContestEligible } =
    useContext(ContestClipContext);

  useEffect(() => {
    if (isOpen && modalData?.initialScreen) {
      navigateTo(modalData.initialScreen);
    }
  }, [isOpen, modalData?.initialScreen, navigateTo]);

  // Update current screen based on modal states for backward compatibility
  useEffect(() => {
    // NOTE: This should be ordered in reverse order of the ui flow. In a sequential order flow multiple of these useState dependnecies will be true at the same time.
    // Having them in reverse order will ensure that the correct screen is displayed.
    if (isViewingGenerationFavorites) {
      navigateTo('generationFavorites');
    } else if (isViewingGenerateHistory) {
      navigateTo('generationHistory');
    } else if (isAddingMedia) {
      navigateTo('addingMedia');
    } else if (isGeneratingCover) {
      navigateTo('generatingCover');
    }
  }, [
    isAddingMedia,
    isGeneratingCover,
    isViewingGenerateHistory,
    isViewingGenerationFavorites,
    isOpen,
    modalData?.initialScreen,
    navigateTo,
  ]);

  const handleClose = () => {
    setAddedImages([]);
    setAddingImageDataURL(null);
    setIsGenerating(false);
    setAddingImageChanged(false);
    setContinuedClips([]);
    setInitialImageURL(null);
    setEditedTitle('');
    setEditedImagePrompt('');
    setIsAddingMedia(false);
    setSelectedVideo(undefined);
    setCurrentScreen('main');
    setHasUpdatedLyrics(false);
    setIsPinned(clips.isClipPinned(clipId));
    setS3Id(null);
    setIsGeneratingCover(false);
    setIsViewingGenerateHistory(false);
    setIsViewingGenerationFavorites(false);
    setNavigationState({});
    closeModal(modalType);
  };

  useEffect(() => {
    setCurrentImage(library.activeClip?.image_url);
  }, [library.activeClip?.image_url]);

  useEffect(() => {
    if (isOpen) {
      setCurrentActiveClip(library.activeClip);
      setEditedTitle(
        library.activeClip ? getClipTitle(library.activeClip) : ''
      );
      setCurrentLyrics(library.activeClip?.metadata?.prompt || '');
      setCurrentCaptionMentions(
        library.activeClip?.caption_mentions?.user_mentions || []
      );
      setDisplayTags(library.activeClip?.display_tags || '');
      setIsPinned(clips.isClipPinned(clipId));
      setSelectedVideo(undefined);
      setCurrentImage(library.activeClip?.image_url);
      const submitToContestToggleValue = Boolean(
        isContestSubmission({ clip: library.activeClip }) ||
          // if the clip is not public, and it's contest eligible -> default to true
          (!library.activeClip?.is_public &&
            isContestEligible({ clip: library.activeClip }) &&
            modalType === ModalTypes.PUBLISH_SONG)
      );
      setSubmitToContest(submitToContestToggleValue);
    }
  }, [isOpen]);

  useEffect(() => {
    setCurrentCaption(originalReconstructedCaption);
    // always update mentionsUsedMap to match the current clip's mentions
    stateRef.current.mentionsUsedMap = originalMentionsUsedMap;
  }, [currentCaptionMentions, originalMentionsUsedMap]);

  const hasSubmitToContestChanged =
    submitToContest !== isContestSubmission({ clip: library.activeClip });

  const [initialImageURLForVideoGen, setInitialImageURLForVideoGen] = useState<
    string | null
  >(null);

  const handleSave = useCallback(async () => {
    if (
      clipId &&
      (editedTitle ||
        currentLyrics !== library.activeClip?.metadata.prompt ||
        currentImage ||
        currentCaption !== originalReconstructedCaption ||
        displayTags !== library.activeClip?.display_tags ||
        hasSubmitToContestChanged)
    ) {
      if (currentLyrics !== library.activeClip?.metadata.prompt) {
        setHasUpdatedLyrics(true);
      }
      const caption =
        currentCaption !== originalReconstructedCaption
          ? currentCaption
          : undefined;
      const { content: parsedCaption, userMentions } = parseMentions(
        currentCaption,
        stateRef.current.mentionsUsedMap
      );

      const result = await clips.setMetadataAndDisplayTags(
        {
          clipId,
          title: editedTitle,
          imageUrl: undefined,
          lyrics: currentLyrics,
          caption: parsedCaption,
          caption_mentions: {
            user_mentions: userMentions,
          },
          videoCoverUploadId: selectedVideo ? uploadState.uploadId : undefined,
          removeImageCover: currentImage === null,
          removeVideoCover: selectedVideo === null,
          showSuccessToast: false,
          s3Id: s3Id || null,
        },
        displayTags
      );
      if (result && result.success) {
        if (caption !== undefined) {
          logWebUserEvent({
            actionName: 'CaptionAddedInMetadataModal',
            context: {
              clipId,
              caption: caption || '',
              page: pathname,
            },
          });
        }
        setSelectedVideo(undefined);
      }
      return result;
    }
    return { success: false, error: 'No changes made' };
  }, [
    editedTitle,
    currentLyrics,
    library.activeClip?.metadata.prompt,
    currentImage,
    currentCaption,
    library.activeClip?.caption,
    library.activeClip?.caption_mentions?.user_mentions,
    clipId,
    clips,
    pathname,
    selectedVideo,
    uploadState.uploadId,
    displayTags,
    library.activeClip?.display_tags,
    submitToContest,
    hasSubmitToContestChanged,
  ]);
  // Render content based on current screen
  const renderScreenContent = () => {
    switch (currentScreen) {
      case 'generationCarousel':
        const carouselState =
          navigationState.generationCarousel ?? DEFAULT_CAROUSEL_STATE;
        return (
          <GenerationCarouselModalContent
            onClose={handleClose}
            onBack={() => {
              setNavigationState({});
              // we've checked that the generationCarousel state is not null, so we can safely access the previousScreen
              navigateTo(navigationState.generationCarousel!.previousScreen);
            }}
            onBackToMain={() => {
              setNavigationState({});
              navigateTo('main');
            }}
            initialState={carouselState}
            clipId={clipId}
          />
        );
      case 'generationFavorites':
        return (
          <GenerationFavoritesModalContent
            onClose={handleClose}
            onBack={() => {
              setIsViewingGenerationFavorites(false);
              setIsGeneratingCover(true);
            }}
            navigateToGenerationCarousel={(items, initialIndex) => {
              setIsViewingGenerationFavorites(false);
              navigateToGenerationCarousel({
                items,
                initialIndex,
                previousScreen: 'generationFavorites',
              });
            }}
          />
        );
      case 'generatingCover':
        return (
          <GenerateCoverArtModalContent
            onClose={handleClose}
            onCancel={() => {
              setIsGeneratingCover(false);
              navigateTo('main');
            }}
            clipId={clipId}
            useClipCoverImage={false}
            onGenerate={() => {
              enablePendingToasts();
            }}
            onViewFavorites={() => {
              setIsGeneratingCover(false);
              setIsViewingGenerationFavorites(true);
            }}
            initialImageURLForVideoGen={initialImageURLForVideoGen || undefined}
            setInitialImageURLForVideoGen={setInitialImageURLForVideoGen}
            navigateToGenerationCarousel={navigateToGenerationCarousel}
          />
        );
      case 'addingMedia':
        return (
          <AddMediaModalContent
            onMediaSelect={(file, mediaType) => {
              // Track selected media and its type
              setSelectedMedia(file);
              setSelectedMediaType(mediaType);

              if (mediaType === 'video') {
                setSelectedVideo(file);
                if (file) {
                  uploadState.uppy.addFile({
                    name: file.name,
                    type: file.type,
                    data: file,
                  });
                } else {
                  uploadState.uppy.getFiles().forEach((f) => {
                    uploadState.uppy.removeFile(f.id);
                  });
                }
              }
              // Log the upload action
              if (file && mediaType) {
                logWebUserEvent({
                  actionName: 'SongEditCoverArtUploadClicked',
                  principalObjectType: 'song',
                  principalObjectValue: clipId,
                  context: {
                    clipId,
                    mediaType: mediaType,
                  },
                });
              }
            }}
            selectedMedia={selectedMedia || undefined}
            mediaType={selectedMediaType}
            onClose={handleClose}
            onCancel={() => {
              setSelectedVideo(undefined);
              setSelectedMedia(null);
              setSelectedMediaType(null);
              uploadState.uppy.getFiles().forEach((file) => {
                uploadState.uppy.removeFile(file.id);
              });
              navigateTo('main');
            }}
            onGenerateCoverArt={() => {
              // Clear any selected media
              setSelectedVideo(undefined);
              setSelectedMedia(null);
              setSelectedMediaType(null);
              setIsAddingMedia(false);
              uploadState.uppy.getFiles().forEach((file) => {
                uploadState.uppy.removeFile(file.id);
              });
              // Navigate to generate cover art screen
              setIsGeneratingCover(true);
              logWebUserEvent({
                actionName: 'SongEditCoverArtGenerateClicked',
                principalObjectType: 'song',
                principalObjectValue: clipId,
                context: { clipId, mediaType: 'video' },
              });
            }}
            onSave={async () => {
              // Use the tracked selected media
              const file = selectedMedia;
              if (!file) return;

              const mediaType = selectedMediaType;
              if (!mediaType) return;

              setIsAddingMedia(false);

              if (mediaType === 'video') {
                // Upload video using existing uploadState
                await uploadState.uppy.upload();
              } else if (mediaType === 'image') {
                // Upload image using useUploadImageFile
                try {
                  // First, create a data URL for immediate display
                  const reader = new FileReader();
                  const dataUrlPromise = new Promise<string>((resolve) => {
                    reader.onloadend = () => resolve(reader.result as string);
                    reader.readAsDataURL(file);
                  });

                  // Upload the image
                  const result = await uploadImageFile(file, 'file_upload');
                  if (result.uploadId) {
                    setS3Id(result.uploadId);
                  }

                  // Set the data URL for immediate display
                  const dataUrl = await dataUrlPromise;
                  setCurrentImage(dataUrl);
                } catch (error) {
                  console.error('Image upload failed:', error);
                }
              }

              logWebUserEvent({
                actionName: 'SongEditCoverArtSaveClicked',
                principalObjectType: 'song',
                principalObjectValue: clipId,
                context: {
                  clipId,
                  mediaType,
                },
              });

              // Reset state
              setSelectedMedia(null);
              setSelectedMediaType(null);
              navigateTo('main');
            }}
          />
        );

      case 'lyrics':
        return (
          <EditLyricsScreen
            onSave={handleSave}
            editedTitle={editedTitle}
            currentLyrics={currentLyrics}
            setCurrentLyrics={setCurrentLyrics}
            clipId={clipId}
            shouldDisableSave={
              uploadState.isUploading ||
              uploadState.isProcessing ||
              currentLyrics === currentActiveClip?.metadata?.prompt
            }
          />
        );

      case 'moreOptions':
        return (
          <MoreOptionsScreen isPinned={isPinned} setIsPinned={setIsPinned} />
        );

      case 'shareContestSubmission':
        return (
          <ContestShareModal
            clipId={library.activeClip?.id || ''}
            imageUrl={currentImage || ''}
            onClose={handleClose}
            onShare={() => {
              logWebUserEvent({
                actionName: 'ShareContestSubmissionClicked',
                principalObjectType: 'song',
                principalObjectValue: library.activeClip?.id || '',
              });
              openModalWithData(
                ModalTypes.SHARE_CLIP,
                { clipId: library.activeClip?.id || '' },
                'ContestSubmissionSuccess'
              );
              session.clearTooltipOnShare();
              handleClose();
            }}
          />
        );

      default:
        const hasPinnedChanged = isPinned !== clips.isClipPinned(clipId);
        const hasChangedValues =
          !!selectedVideo ||
          (!!currentImage && currentImage !== library.activeClip?.image_url) ||
          // if the image is the default aura url, it means the user has not uploaded an image
          (!currentImage &&
            library.activeClip?.image_url !== DEFAULT_AURA_URL) ||
          currentLyrics !== library.activeClip?.metadata?.prompt ||
          // currentCaption !== (library.activeClip?.caption || '') ||
          currentCaption !== originalReconstructedCaption ||
          editedTitle !== getClipTitle(library.activeClip) ||
          displayTags !== (library.activeClip?.display_tags || '') ||
          hasPinnedChanged ||
          // save should only be an option if the song is already public
          (hasSubmitToContestChanged &&
            modalType === ModalTypes.UPDATE_CLIP_METADATA);

        const isUploading = uploadState.isUploading || uploadState.isProcessing;

        const shouldDisableSave =
          // Disable if no title or image provided
          (!editedTitle && !currentImage) ||
          !currentActiveClip ||
          // Disable if no changes were made
          !hasChangedValues ||
          // Disable if title is too long
          editedTitle.length > 100 ||
          // Disable during upload/processing states
          uploadState.isUploading ||
          uploadState.isProcessing ||
          (submitToContest && modalType === ModalTypes.PUBLISH_SONG);

        return (
          <PublishMainContentScreen
            onClose={handleClose}
            onSave={handleSave}
            clip={currentActiveClip}
            shouldShowConfirmationModal={hasChangedValues}
            isPinned={isPinned}
            hasPinnedChanged={hasPinnedChanged}
            hasSubmitToContestChanged={hasSubmitToContestChanged}
            submitToContest={submitToContest}
            editClipMetadataFormProps={{
              clipId,
              editedTitle,
              setEditedTitle,
              currentActiveClip,
              currentImage,
              setCurrentImage,
              continuedClips,
              addedImages,
              currentCaption,
              setCurrentCaption,
              mentionQuery,
              setMentionQuery,
              hashtagQuery,
              setHashtagQuery,
              selectedVideo,
              setSelectedVideo,
              setIsAddingMedia,
              setIsGeneratingCover,
              uploadState,
              displayTags,
              setDisplayTags,
              submitToContest,
              setSubmitToContest,
              hasSubmitToContestChanged,
              stateRef,
              setInitialImageURLForVideoGen,
            }}
            publishSongModalEventProps={{
              hasUploadedVideo: !!selectedVideo,
              hasUploadedImage: currentImage !== currentActiveClip?.image_url,
              hasUpdatedCaption:
                currentCaption !== (currentActiveClip?.caption || ''),
              hasUpdatedTitle: editedTitle !== currentActiveClip?.title,
              hasUpdatedLyrics: hasUpdatedLyrics,
              hasLyrics: !!currentActiveClip?.metadata?.prompt,
              hasCaption: !!currentActiveClip?.caption,
              hasTitle: !!currentActiveClip?.title,
              hasUpdatedStyleSummary:
                displayTags !== (currentActiveClip?.display_tags || ''),
              ...(isContestEligible({ clip: library.activeClip }) && {
                hasSubmitToContestChanged,
                submitToContest,
              }),
            }}
            shouldDisableSave={shouldDisableSave}
            isUploading={isUploading}
          />
        );
    }
  };

  return (
    <SongModalContext.Provider value={songModalContextValue}>
      <Modal
        isOpen={true}
        onClose={handleClose}
        data-qaid='modal-publish-clip-title'
        isCentered
        blockScrollOnMount={false}
      >
        <ModalOverlay />
        <ModalContent
          borderRadius='32px'
          maxW={{ base: '90%', md: '600px' }}
          position='relative'
          overflow='auto'
          className='max-h-[80vh] bg-background-secondary! text-foreground-primary!'
        >
          <div className='relative h-full w-full'>
            {/* Main screen */}
            <div
              className={clsx('h-full w-full', {
                block: currentScreen === 'main',
                hidden: currentScreen !== 'main',
              })}
            >
              {currentScreen === 'main' && renderScreenContent()}
            </div>

            {/* Other screens */}
            <div
              className={clsx('h-full w-full', {
                block: currentScreen !== 'main',
                hidden: currentScreen === 'main',
              })}
            >
              {currentScreen !== 'main' && renderScreenContent()}
            </div>
          </div>
        </ModalContent>
      </Modal>
    </SongModalContext.Provider>
  );
});

export default PublishSongModal;
