'use client';

import * as Tabs from '@radix-ui/react-tabs';
import { useStatsigClient } from '@statsig/react-bindings';
import { useQueryClient } from '@tanstack/react-query';
import { observer } from 'mobx-react-lite';
import { useCallback, useEffect, useState } from 'react';

import { useStores } from '@/app/(root)/AppProviders';
import Button, {
  ButtonShape,
  ButtonSize,
  ButtonVariant,
} from '@/components/button/Button';
import { ModalTypes } from '@/components/modal/constants/ModalTypes';
import ModalHeader from '@/components/modal/publishSong/ModalHeader';
import PublishModalFooter from '@/components/modal/publishSong/PublishModalFooter';
import SelectorV2, { SelectorV2Option } from '@/components/select/SelectorV2';
import SpinnerSVG from '@/components/svg/SpinnerSVG';
import TextareaV2, { CharCountMode } from '@/components/textarea/TextareaV2';
import { Tooltip } from '@/components/tooltip/Tooltip';
import useLogOnMount from '@/hooks/useLogOnMount';
import type { GenerationCarouselState } from '@/hooks/useSongModal';
import { useSubscriptionInfo } from '@/hooks/useSubscriptionInfo';
import { HeartOutlineIcon, SparklesIcon, WandIcon } from '@/icons';
import { createTransactionLogger } from '@/logging/logWebUserEvent';
import { FeatureKey, PlanKey } from '@/state/sessionStore';

import GenerationHistoryModalContent from './GenerationHistoryModalContent';
import { ImageStartFrameButton } from './ImageStartFrameButton';
import type { MediaType } from './generationHistoryTypes';
import { generationQueryKeys } from './queryKeys';
import { useGenerateTextPrompt } from './useGenerateTextPrompt';
import { useImageGenerationPolling } from './useImageGenerationPolling';
import { useStartImageGeneration } from './useStartImageGeneration';
import {
  type VideoModel,
  useStartVideoGeneration,
} from './useStartVideoGeneration';

interface GenerateCoverArtModalContentProps {
  onClose: () => void;
  onCancel?: () => void;
  clipId: string;
  onGenerate: () => void;
  onViewFavorites: () => void;
  initialImageURLForVideoGen?: string;
  setInitialImageURLForVideoGen: (imageUrl: string | null) => void;
  navigateToGenerationCarousel: (state: GenerationCarouselState) => void;
  useClipCoverImage: boolean;
}

const GENERATE_PROMPT_TOOLTIP_KEY = 'generate-cover-art-prompt-tooltip';

export const GenerateCoverArtModalContent = observer(
  ({
    onClose,
    onCancel,
    clipId,
    useClipCoverImage,
    onGenerate,
    onViewFavorites,
    initialImageURLForVideoGen,
    setInitialImageURLForVideoGen,
    navigateToGenerationCarousel,
  }: GenerateCoverArtModalContentProps) => {
    // Track modal view
    useLogOnMount({
      actionName: 'GenerateCoverArtCreateModalViewed',
      principalObjectType: 'clip',
      principalObjectValue: clipId,
    });

    const [mediaType, setMediaType] = useState<MediaType>('image-to-video');
    const [videoPrompt, setVideoPrompt] = useState('');
    const [imagePrompt, setImagePrompt] = useState('');
    const [imageToVideoPrompt, setImageToVideoPrompt] = useState('');
    const [isGenerating, setIsGenerating] = useState(false);
    const [isGeneratingPrompt, setIsGeneratingPrompt] = useState(false);

    // Show tooltip by default on first visit
    const [showGeneratePromptTooltip, setShowGeneratePromptTooltip] = useState(
      () => {
        if (typeof window !== 'undefined') {
          return !localStorage.getItem(GENERATE_PROMPT_TOOLTIP_KEY);
        }
        return false;
      }
    );

    // Mark tooltip as seen when component unmounts or when user interacts
    useEffect(() => {
      return () => {
        if (typeof window !== 'undefined' && showGeneratePromptTooltip) {
          localStorage.setItem(GENERATE_PROMPT_TOOLTIP_KEY, 'true');
        }
      };
    }, [showGeneratePromptTooltip]);

    // Auto-dismiss tooltip after 3 seconds and dismiss on click anywhere
    useEffect(() => {
      if (!showGeneratePromptTooltip) return;

      const handleClickAnywhere = () => {
        setShowGeneratePromptTooltip(false);
        localStorage.setItem(GENERATE_PROMPT_TOOLTIP_KEY, 'true');
      };

      // Auto-dismiss after 3 seconds
      const autoDismissTimeout = setTimeout(() => {
        setShowGeneratePromptTooltip(false);
        localStorage.setItem(GENERATE_PROMPT_TOOLTIP_KEY, 'true');
      }, 3000);

      // Add click listener after a small delay to avoid immediate dismissal
      const clickListenerTimeout = setTimeout(() => {
        document.addEventListener('click', handleClickAnywhere);
      }, 100);

      return () => {
        clearTimeout(autoDismissTimeout);
        clearTimeout(clickListenerTimeout);
        document.removeEventListener('click', handleClickAnywhere);
      };
    }, [showGeneratePromptTooltip]);

    const [error, setError] = useState<string | null>(null);
    const [generatedVideoPromptTextId, setGeneratedVideoPromptTextId] =
      useState<string | null>(null);
    const [generatedImagePromptTextId, setGeneratedImagePromptTextId] =
      useState<string | null>(null);
    const [
      generatedImageToVideoPromptTextId,
      setGeneratedImageToVideoPromptTextId,
    ] = useState<string | null>(null);
    const [shouldUseCover, setShouldUseCover] = useState(useClipCoverImage);
    const [imageQuality, setImageQuality] = useState<'default' | 'high'>(
      'default'
    );
    const [videoModel, setVideoModel] = useState<VideoModel>(
      'fal-wan2.2-a14b-turbo'
    );

    const queryClient = useQueryClient();
    const { menus } = useStores();

    // Get subscription info (including credits) from server using TanStack Query
    const {
      data: subscriptionData,
      isLoading,
      isError,
    } = useSubscriptionInfo();
    const credits = subscriptionData?.total_credits_left ?? 0;

    // API mutation hooks
    const generateTextPrompt = useGenerateTextPrompt();
    const startImageGeneration = useStartImageGeneration({
      invalidateQueryKey: generationQueryKeys.history(clipId),
    });
    const startVideoGeneration = useStartVideoGeneration({
      invalidateQueryKey: generationQueryKeys.history(clipId),
    });

    // Image generation polling hook (for image selection dropdown)
    const {
      startGenerationFromSongWithPolling,
      isLoading: isLoadingImage,
      imageUrl,
      imageId: generatedImageId,
      coverImageUrl,
      coverImageId,
      existingCoverError,
      refetchExistingCover,
      uploadImage,
      uploadedImageUrl,
      uploadedImageId,
      resetUploadedImage,
      error: imageGenerationError,
      reset: resetImageGeneration,
      uploadImageMutation,
    } = useImageGenerationPolling({ clipId, shouldUseCover });

    // Get current prompt state based on active tab
    const prompt =
      mediaType === 'video'
        ? videoPrompt
        : mediaType === 'image-to-video'
          ? imageToVideoPrompt
          : imagePrompt;
    const setPrompt =
      mediaType === 'video'
        ? setVideoPrompt
        : mediaType === 'image-to-video'
          ? setImageToVideoPrompt
          : setImagePrompt;
    const setGeneratedPromptTextId =
      mediaType === 'video'
        ? setGeneratedVideoPromptTextId
        : mediaType === 'image-to-video'
          ? setGeneratedImageToVideoPromptTextId
          : setGeneratedImagePromptTextId;

    // Cleanup on unmount (resets local state)
    useEffect(() => {
      return () => {
        setVideoPrompt('');
        setImagePrompt('');
        setImageToVideoPrompt('');
        setIsGenerating(false);
        setError(null);
        setGeneratedVideoPromptTextId(null);
        setGeneratedImagePromptTextId(null);
        setGeneratedImageToVideoPromptTextId(null);
        // Note: resetImageGeneration() handled by hook itself
      };
    }, []);

    // Sync image generation errors to main error state
    useEffect(() => {
      if (imageGenerationError) {
        setError(imageGenerationError);
      }
    }, [imageGenerationError]);

    // Handle initial image URL by using existing cover
    useEffect(() => {
      if (initialImageURLForVideoGen && !coverImageUrl && !uploadedImageUrl) {
        setError(null);
        resetImageGeneration();
        setShouldUseCover(true);
        if (existingCoverError) {
          refetchExistingCover();
        }
      }
    }, [
      initialImageURLForVideoGen,
      coverImageUrl,
      uploadedImageUrl,
      existingCoverError,
      refetchExistingCover,
      resetImageGeneration,
    ]);

    const handleGenerateImageFromSong = useCallback(() => {
      setError(null);
      setShouldUseCover(false); // Don't use cover when generating new image
      startGenerationFromSongWithPolling({
        clipId,
        models: [
          imageQuality === 'high' ? 'fal_nano_banana' : 'sdxl-lightning',
        ],
      });
    }, [clipId, startGenerationFromSongWithPolling, imageQuality]);

    const handleUseExistingCover = useCallback(() => {
      setError(null);
      resetImageGeneration(); // Clear generated image if switching to existing cover
      setShouldUseCover(true); // Enable cover usage - will use cached data or show error
      // If there was an error on previous fetch, retry
      if (existingCoverError) {
        refetchExistingCover();
      }
    }, [existingCoverError, refetchExistingCover, resetImageGeneration]);

    const handleRemoveImage = useCallback(() => {
      if (initialImageURLForVideoGen) {
        setInitialImageURLForVideoGen(null);
      }
      if (uploadedImageUrl) {
        resetUploadedImage();
      } else if (coverImageUrl) {
        setShouldUseCover(false); // Stop showing cover
      } else {
        resetImageGeneration(); // Reset generated image
      }
    }, [
      initialImageURLForVideoGen,
      setInitialImageURLForVideoGen,
      coverImageUrl,
      uploadedImageUrl,
      resetImageGeneration,
      resetUploadedImage,
    ]);

    const handleUploadImage = useCallback(
      (file: File) => {
        setError(null);
        setShouldUseCover(false);
        uploadImage(file);
      },
      [uploadImage]
    );

    const handleGeneratePromptFromSong = async (target: 'video' | 'image') => {
      setError(null);
      setIsGeneratingPrompt(true);
      try {
        // Generate text prompt using mutation hook
        const textData = await generateTextPrompt({
          clipId,
          target,
          prompt: prompt.trim() || null,
        });

        if (target === 'video') {
          // For both 'video' and 'image-to-video' tabs
          if (mediaType === 'image-to-video') {
            setImageToVideoPrompt(textData.prompt);
            setGeneratedImageToVideoPromptTextId(textData.id);
          } else {
            setVideoPrompt(textData.prompt);
            setGeneratedVideoPromptTextId(textData.id);
          }
        } else {
          setImagePrompt(textData.prompt);
          setGeneratedImagePromptTextId(textData.id);
        }
      } catch (err) {
        setError('Failed to generate');
      } finally {
        setIsGeneratingPrompt(false);
      }
    };

    const handlePromptChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
      setPrompt(e.target.value);
      // Clear the generated ID when user manually edits the prompt
      setGeneratedPromptTextId(null);
    };

    const handleGenerate = async () => {
      // Calculate credits needed based on media type
      const creditsNeeded =
        mediaType === 'video' || mediaType === 'image-to-video' ? 100 : 0;

      // Check if user has enough credits
      if (creditsNeeded > 0 && credits < creditsNeeded) {
        // Determine which upsell feature to show based on current plan
        const currentPlanKey = subscriptionData?.plan?.plan_key;
        const isProUser =
          currentPlanKey === PlanKey.Pro ||
          currentPlanKey === PlanKey.Pro_20250501;

        // Show Premier plan for Pro users, otherwise show Pro plan
        const upsellFeature = isProUser
          ? FeatureKey.GENERATE_VIDEO // This will show Premier features
          : FeatureKey.OUT_OF_CREDITS; // This will show Pro features

        menus.setCurrentUpsellFeature(upsellFeature);
        menus.openModal(ModalTypes.UPSELL_MODAL);
        return;
      }

      setIsGenerating(true);
      setError(null);

      // we need to link a transaction uuid to the generation batch_id to correlate
      // the generation clicked event to all subequent events in the flow
      const { logWebUserEvent } = createTransactionLogger();

      // Log generate click event
      logWebUserEvent({
        actionName: 'GenerateCoverArtGenerateClicked',
        principalObjectType: 'clip',
        principalObjectValue: clipId,
        context: {
          generationType: mediaType === 'image' ? 'image' : 'video',
        },
      });

      try {
        if (mediaType === 'image') {
          // Text to Image generation
          const resp = await startImageGeneration({
            generatedTextId: generatedImagePromptTextId || undefined,
            prompt: imagePrompt,
            clipId: clipId,
            quantity: 2,
            models: [
              imageQuality === 'high' ? 'fal_nano_banana' : 'sdxl-lightning',
            ],
          });
          for (const imageId of resp.image_ids || []) {
            logWebUserEvent({
              actionName: 'GenerateCoverArtGenerationStarted',
              principalObjectType: 'clip',
              principalObjectValue: clipId,
              context: {
                generationType: 'image',
                batchId: resp.batch_id,
                imageId: imageId,
              },
            });
          }
        } else if (mediaType === 'image-to-video') {
          // Image to Video generation
          const resp = await startVideoGeneration({
            generatedTextId: generatedImageToVideoPromptTextId || undefined,
            generatedImageId: generatedImageId || undefined,
            uploadedImageId: uploadedImageId || undefined,
            imageS3Filename: coverImageId ? `${coverImageId}.jpeg` : undefined,
            clipId: clipId,
            prompt: imageToVideoPrompt,
            quantity: 2,
            models: [videoModel],
          });
          for (const videoId of resp.video_ids || []) {
            logWebUserEvent({
              actionName: 'GenerateCoverArtGenerationStarted',
              principalObjectType: 'clip',
              principalObjectValue: clipId,
              context: {
                generationType: 'video',
                batchId: resp.batch_id,
                videoId: videoId,
              },
            });
          }
        } else {
          // Text to Video generation
          const resp = await startVideoGeneration({
            generatedTextId: generatedVideoPromptTextId || undefined,
            generatedImageId: generatedImageId || undefined,
            uploadedImageId: undefined,
            imageS3Filename: undefined,
            clipId: clipId,
            prompt: videoPrompt,
            quantity: 2,
            models: [videoModel],
          });
          for (const videoId of resp.video_ids || []) {
            logWebUserEvent({
              actionName: 'GenerateCoverArtGenerationStarted',
              principalObjectType: 'clip',
              principalObjectValue: clipId,
              context: {
                generationType: 'video',
                batchId: resp.batch_id,
                videoId: videoId,
              },
            });
          }
        }

        // Refetch subscription info to update credits after successful generation
        await queryClient.invalidateQueries({ queryKey: ['subscriptionInfo'] });

        onGenerate();
      } catch (err) {
        setError('Failed to generate');
        const errorMessage =
          err instanceof Error
            ? err.message
            : `Failed to start ${mediaType} generation`;
        logWebUserEvent({
          actionName: 'GenerateCoverArtGenerationFailed',
          principalObjectType: 'clip',
          principalObjectValue: clipId,
          context: {
            generationType: mediaType === 'image' ? 'image' : 'video',
            message: errorMessage,
          },
        });
      } finally {
        setIsGenerating(false);
      }
    };

    const creditsNeeded =
      mediaType === 'video' || mediaType === 'image-to-video'
        ? 100
        : mediaType === 'image' && imageQuality === 'high'
          ? 40
          : 0;
    const hasImageForGeneration = imageUrl || coverImageUrl || uploadedImageUrl;
    const isSaveDisabled =
      mediaType === 'video'
        ? (!prompt.trim() && !hasImageForGeneration) || isGenerating // Text to Video: needs prompt or image
        : mediaType === 'image-to-video'
          ? !hasImageForGeneration || isGenerating // Image to Video: requires image (prompt optional)
          : !prompt.trim() || isGenerating; // Text to Image: needs prompt only

    const statsigClient = useStatsigClient();
    const newGenModelGate = statsigClient.checkGate('new-gen-model');

    return (
      <div className='flex h-full w-full flex-col'>
        <ModalHeader
          title='Generate Cover Art'
          onBack={onCancel}
          onClose={onClose}
        >
          <Button
            variant={ButtonVariant.Standard}
            shape={ButtonShape.Pill}
            icon={HeartOutlineIcon}
            onClick={onViewFavorites}
            aria-label='View favorites'
          />
        </ModalHeader>

        {/* Credits display */}
        <div className='px-10 pb-2 text-sm text-foreground-inactive'>
          {!isLoading && !isError && `${credits.toLocaleString()} Credits`}
        </div>

        <div className='flex flex-1 flex-col items-center gap-6 px-6 pb-6'>
          {/* Image Display or Generation History */}
          <div className='relative flex w-full items-center justify-center'>
            <GenerationHistoryModalContent
              clipId={clipId}
              setImageToVideoPrompt={setImageToVideoPrompt}
              setMediaType={setMediaType}
              handleUploadImage={handleUploadImage}
              navigateToGenerationCarousel={(items, initialIndex) => {
                navigateToGenerationCarousel({
                  items,
                  initialIndex,
                  previousScreen: 'generatingCover',
                });
              }}
            />
          </div>
          {/* Error Display */}
          {error && (
            <div className='w-full rounded-lg bg-accent-error/10 px-4 py-2 text-center text-sm text-accent-error-on-primary'>
              {error}
            </div>
          )}
          {/* Input Section */}
          <div className='w-full rounded-2xl bg-background-tertiary p-4'>
            <div className='flex flex-col gap-3'>
              {/* Tabs */}
              <Tabs.Root
                value={mediaType}
                onValueChange={(value) => setMediaType(value as MediaType)}
              >
                <Tabs.List className='flex items-center justify-between'>
                  <div className='flex gap-3'>
                    <Tabs.Trigger
                      value='image-to-video'
                      className='px-1 py-2 text-sm transition-colors hover:text-foreground-primary data-[state=active]:border-b data-[state=active]:border-foreground-primary data-[state=active]:text-foreground-primary data-[state=inactive]:text-foreground-inactive'
                    >
                      Image to Video
                    </Tabs.Trigger>
                    <Tabs.Trigger
                      value='video'
                      className='px-1 py-2 text-sm transition-colors hover:text-foreground-primary data-[state=active]:border-b data-[state=active]:border-foreground-primary data-[state=active]:text-foreground-primary data-[state=inactive]:text-foreground-inactive'
                    >
                      Text to Video
                    </Tabs.Trigger>
                    <Tabs.Trigger
                      value='image'
                      className='px-1 py-2 text-sm transition-colors hover:text-foreground-primary data-[state=active]:border-b data-[state=active]:border-foreground-primary data-[state=active]:text-foreground-primary data-[state=inactive]:text-foreground-inactive'
                    >
                      Text to Image
                    </Tabs.Trigger>
                  </div>
                </Tabs.List>
              </Tabs.Root>

              {/* Prompt Input */}
              <div className='flex items-start gap-2'>
                {/* Image Start Frame for Image-to-Video tab */}
                {mediaType === 'image-to-video' && (
                  <ImageStartFrameButton
                    imageUrl={uploadedImageUrl || coverImageUrl || imageUrl}
                    isLoading={isLoadingImage}
                    loadingType={
                      uploadImageMutation.isPending ? 'uploading' : 'generating'
                    }
                    onGenerateFromSong={handleGenerateImageFromSong}
                    onUseExistingCover={handleUseExistingCover}
                    onUploadImage={handleUploadImage}
                    onRemoveImage={handleRemoveImage}
                  />
                )}
                <div className='flex-1 px-2'>
                  <TextareaV2
                    value={prompt}
                    onChange={handlePromptChange}
                    placeholder={
                      mediaType === 'image-to-video'
                        ? 'Describe how you want your image to animate (optional)'
                        : 'Describe what you want to see...'
                    }
                    maxLength={400}
                    maxRows={3}
                    minRows={3}
                    resize={true}
                    charCountMode={CharCountMode.WhenCloseToFull}
                    className='border-none p-0'
                    textAreaClassName='text-sm pt-0 px-0 resize-none pb-7'
                    characterCountClassName='bg-background-tertiary right-3'
                  />
                </div>
                <Tooltip
                  label='Generate a prompt from your song'
                  isOpen={showGeneratePromptTooltip}
                  placement='left'
                  hasArrow
                >
                  <Button
                    variant={ButtonVariant.Aura}
                    size={ButtonSize.Small}
                    shape={ButtonShape.Pill}
                    onClick={() => {
                      setShowGeneratePromptTooltip(false);
                      handleGeneratePromptFromSong(
                        mediaType === 'image' ? 'image' : 'video'
                      );
                    }}
                    icon={
                      isGeneratingPrompt ? (
                        <SpinnerSVG className='h-4 w-4' />
                      ) : (
                        <WandIcon className='h-4 w-4' />
                      )
                    }
                    iconOnly
                    disabled={isGeneratingPrompt}
                  />
                </Tooltip>
              </div>

              {/* Bottom Row */}
              <div className='flex items-center justify-between'>
                {mediaType === 'image' && newGenModelGate ? (
                  <SelectorV2
                    value={imageQuality}
                    size={ButtonSize.Small}
                    onSetValue={(value) =>
                      setImageQuality(value as 'default' | 'high')
                    }
                    variant={ButtonVariant.Standard}
                    className='bg-background-fog-thin'
                    options={
                      [
                        {
                          value: 'default',
                          title: 'Quality: Default',
                          ListComponent: () => (
                            <div className='px-3 py-2 text-sm'>Default</div>
                          ),
                        },
                        {
                          value: 'high',
                          title: 'Quality: High',
                          ListComponent: () => (
                            <div className='px-3 py-2 text-sm'>High</div>
                          ),
                        },
                      ] as SelectorV2Option[]
                    }
                  />
                ) : newGenModelGate &&
                  (mediaType === 'video' || mediaType === 'image-to-video') ? (
                  <SelectorV2
                    value={videoModel}
                    size={ButtonSize.Small}
                    onSetValue={(value) => setVideoModel(value as VideoModel)}
                    variant={ButtonVariant.Standard}
                    className='bg-background-fog-thin'
                    options={
                      [
                        {
                          value: 'fal-wan2.2-a14b-turbo',
                          title: 'Model: WAN 2.2 Turbo',
                          ListComponent: () => (
                            <div className='min-w-[200px] px-3 py-2 text-sm'>
                              WAN 2.2 Turbo
                            </div>
                          ),
                        },
                        {
                          value: 'fal-wan2.5',
                          title: 'Model: WAN 2.5',
                          ListComponent: () => (
                            <div className='min-w-[200px] px-3 py-2 text-sm'>
                              WAN 2.5
                            </div>
                          ),
                        },
                        {
                          value: 'fal-minimax-video-01',
                          title: 'Model: Minimax',
                          ListComponent: () => (
                            <div className='min-w-[200px] px-3 py-2 text-sm'>
                              Minimax
                            </div>
                          ),
                        },
                        {
                          value: 'fal-kling-v2.5-turbo-pro',
                          title: 'Model: Kling',
                          ListComponent: () => (
                            <div className='min-w-[200px] px-3 py-2 text-sm'>
                              Kling
                            </div>
                          ),
                        },
                        {
                          value: 'fal-seedance-v1-pro',
                          title: 'Model: Seedance',
                          ListComponent: () => (
                            <div className='min-w-[200px] px-3 py-2 text-sm'>
                              Seedance
                            </div>
                          ),
                        },
                      ] as SelectorV2Option[]
                    }
                  />
                ) : (
                  <div />
                )}
                <Button
                  variant={ButtonVariant.Primary}
                  shape={ButtonShape.Pill}
                  size={ButtonSize.Small}
                  onClick={handleGenerate}
                  disabled={isSaveDisabled}
                  icon={
                    isGenerating ? (
                      <SpinnerSVG className='h-4 w-4' />
                    ) : (
                      <SparklesIcon />
                    )
                  }
                >
                  {creditsNeeded > 0
                    ? `Generate • ${creditsNeeded} credits`
                    : 'Generate'}
                </Button>
              </div>
            </div>
          </div>
        </div>

        {/* Footer Message */}
        <PublishModalFooter className='h-21'>
          <span className='mr-auto text-sm text-foreground-inactive'>
            Generations can take up to 5 minutes. You can close this window. We
            will alert you when your videos are ready.
          </span>
        </PublishModalFooter>
      </div>
    );
  }
);

export default GenerateCoverArtModalContent;
