'use client';

import { observer } from 'mobx-react-lite';
import { useRouter } from 'next/navigation';
import { useEffect, useMemo, useState } from 'react';

import { useStores } from '@/app/(root)/AppProviders';
import { UploadState } from '@/app/(root)/create/sandbox/VideoUploadState';
import Button, {
  ButtonShape,
  ButtonSize,
  ButtonVariant,
} from '@/components/button/Button';
import { toast } from '@/components/toast/Toast';
import { Tooltip } from '@/components/tooltip/Tooltip';
import useUploadFile from '@/hooks/useUploadFile';
import { ShareArrowIcon } from '@/icons';
import { createTransactionLogger } from '@/logging/logWebUserEvent';
import { LOADING_SHIMMER_ANIMATION } from '@/utils/constants';
import { shareClip } from '@/utils/download';

import SimpleVideoAudioUploader from './SimpleVideoAudioUploader';
import SmoothFadeText from './SmoothFadeText';
import StartOverButton from './StartOverButton';
import SyncedVideoAudioPlayer from './SyncedVideoAudioPlayer';
import WaveformLoader from './WaveformLoader';

type ScreenType = 'upload' | 'create' | 'result';

const VideoSampleClient = observer(() => {
  const { session, clips, library } = useStores();
  const router = useRouter();
  const uploadFile = useUploadFile();
  const [currentScreen, setCurrentScreen] = useState<ScreenType>('upload');
  const [audioUrl, setAudioUrl] = useState<string | null>(null);
  const [audioFile, setAudioFile] = useState<File | null>(null);
  const [videoUrl, setVideoUrl] = useState<string | null>(null);
  const [videoFile, setVideoFile] = useState<File | null>(null);
  const [uploadedClipId, setUploadedClipId] = useState<string | null>(null);
  const [videoS3Id, setVideoS3Id] = useState<string | null>(null);
  const [videoUploadId, setVideoUploadId] = useState<string | null>(null);
  const [styleDescription, setStyleDescription] = useState<string>('');
  const [generatedClips, setGeneratedClips] = useState<any[]>([]);
  const [isCreating, setIsCreating] = useState(false);
  const [readyClips, setReadyClips] = useState<any[]>([]);
  const [isPolling, setIsPolling] = useState(false);
  const [processingStep, setProcessingStep] = useState<
    'idle' | 'processing' | 'extracting' | 'uploading' | 'done'
  >('idle');
  const [fileSelected, setFileSelected] = useState(false);
  const [mode, setMode] = useState<'underpainting' | 'cover'>('underpainting');

  console.log(audioUrl, audioFile, videoFile, isPolling, videoUploadId);

  const videoUploadState = useMemo(
    () =>
      new UploadState(library.apiClient, {
        requireVideoSprite: true,
        requireModeration: false,
        videoUploadType: 'video_hook',
      }),
    [library.apiClient]
  );

  const handleAudioExtracted = async (
    extractedAudioFile: File,
    originalVideoFile: File
  ) => {
    setProcessingStep('processing');
    setAudioFile(extractedAudioFile);
    setAudioUrl(URL.createObjectURL(extractedAudioFile));
    setVideoFile(originalVideoFile);
    setVideoUrl(URL.createObjectURL(originalVideoFile));

    try {
      setProcessingStep('extracting');
      const clipId = await uploadAudioDirectly(extractedAudioFile);
      if (!clipId) {
        throw new Error('Audio upload failed. Please try again.');
      }
      setProcessingStep('uploading');
      const s3Id = await uploadVideoDirectly(originalVideoFile);
      if (!s3Id) {
        throw new Error('Video upload failed. Please try again.');
      }
      setProcessingStep('done');
      setUploadedClipId(clipId);
      setVideoS3Id(s3Id);
      setCurrentScreen('create');
    } catch (error) {
      console.error('Error during extraction and upload:', error);
      toast({
        title: 'Error processing files',
        description:
          error instanceof Error
            ? error.message
            : 'Failed to extract and upload files. Please try again.',
        status: 'error',
        duration: 5000,
        isClosable: true,
      });
      setProcessingStep('idle');
    }
  };

  const uploadAudioDirectly = async (audioFile: File) => {
    try {
      const { uploadId, imageUrl, errorMessage } = await uploadFile({
        file: audioFile,
        type: 'file_upload',
        nameOverride: audioFile.name,
        isStemMix: false,
      });

      if (errorMessage) throw new Error(errorMessage);
      if (!uploadId) throw new Error('No upload ID returned from audio upload');

      const { data, error } = await library.apiClient.POST(
        '/api/uploads/audio/{upload_id}/initialize-clip/',
        { params: { path: { upload_id: uploadId } }, body: {} }
      );

      if (error || !data)
        throw new Error('Failed to initialize clip from audio upload');
      const clipId = (data as any).clip_id;
      if (!clipId)
        throw new Error('No clip ID returned from audio initialization');

      await library.loadClips();
      await clips.setMetadata({
        clipId,
        title: 'Extracted Audio from Video',
        imageUrl: imageUrl || '',
        isAudioUploadTOSAccepted: true,
      });

      const clip = clips.clipById[clipId];
      if (clip) {
        setUploadedClipId(clipId);
        return clipId;
      } else {
        throw new Error('Clip not found after audio initialization');
      }
    } catch (error) {
      console.error('Error in audio upload:', error);
      toast({
        title: 'Error uploading audio',
        description:
          error instanceof Error
            ? error.message
            : 'Failed to upload audio. Please try again.',
        status: 'error',
        duration: 5000,
        isClosable: true,
      });
    }
  };

  const uploadVideoDirectly = async (videoFile: File) => {
    try {
      videoUploadState.uppy.addFile({
        name: videoFile.name,
        type: videoFile.type,
        data: videoFile,
      });

      await videoUploadState.uppy.upload();

      let retries = 30;
      while (
        videoUploadState.isProcessing &&
        retries > 0 &&
        !videoUploadState.error
      ) {
        await new Promise((res) => setTimeout(res, 2000));
        retries -= 1;
      }
      if (videoUploadState.error) {
        throw new Error(videoUploadState.error);
      }
      if (videoUploadState.isProcessing) {
        throw new Error('Video processing timed out');
      }
      setVideoUploadId(videoUploadState.uploadId ?? null);
      setVideoS3Id(videoUploadState.uploadClipMetadata?.s3_id ?? null);
      return videoUploadState.uploadClipMetadata?.s3_id ?? null;
    } catch (error) {
      console.error('Error in video upload:', error);
      toast({
        title: 'Error uploading video',
        description:
          error instanceof Error
            ? error.message
            : 'Failed to upload video. Please try again.',
        status: 'error',
        duration: 5000,
        isClosable: true,
      });
      return null;
    }
  };

  const handleCreate = async () => {
    if (!styleDescription.trim()) {
      toast({
        title: 'Style description required',
        description: 'Please enter a style description before creating.',
        status: 'error',
        duration: 3000,
        isClosable: true,
      });
      return;
    }

    if (!videoS3Id || !uploadedClipId) {
      toast({
        title: 'Missing data',
        description:
          'Video or audio data not available. Please try uploading again.',
        status: 'error',
        duration: 3000,
        isClosable: true,
      });
      return;
    }

    try {
      setIsCreating(true);

      const audioClip = clips.clipById[uploadedClipId];
      const duration = audioClip?.metadata?.duration ?? 30;
      const underpaintingEnd = Math.min(30, duration);

      const { createV2, genForm } = clips.root;

      // Set model to bluejay if available (for both underpainting and cover)
      const availableModels = session
        .getViewableModels()
        .map((m) => m.external_key);
      const hasBluejay = availableModels.includes('bluejay');

      if (hasBluejay) {
        genForm.setMvUserPreference('chirp-bluejay');
      }

      const params: any = {
        style: styleDescription,
        tags: styleDescription,
      };

      if (mode === 'cover') {
        genForm.setTask('cover');
        createV2.setTagInput(styleDescription);
        genForm.setStyle(styleDescription);
        genForm.setCoverClip(audioClip);
        createV2.setControlSlider('audio_weight', 100);
        genForm.setMvUserPreference('chirp-bluejay');
        createV2.setActiveLyrics(audioClip?.metadata?.prompt || '');
        params.task = 'cover';
        params.cover_clip_id = uploadedClipId;
        params.mv = 'chirp-bluejay';
        params.tags = styleDescription;
      } else {
        genForm.setTask('underpainting');
        createV2.setTagInput(styleDescription);
        genForm.setStyle(styleDescription);
        createV2.setUnderpaintingClip(audioClip);
        genForm.setMvUserPreference('chirp-bluejay');
        createV2.setActiveLyrics(audioClip?.metadata?.prompt || '');
        params.task = 'underpainting';
        params.underpainting_clip_id = uploadedClipId;
        params.underpainting_start_s = 0;
        params.underpainting_end_s = underpaintingEnd;
        params.mv = 'chirp-bluejay';
        params.tags = styleDescription;
      }

      const result = await clips.runStream({
        transactionLogger: createTransactionLogger(),
        session,
        params,
      });

      if (result && Array.isArray(result)) {
        setGeneratedClips(result);
        const canonicalVideoUploadId = videoUploadState.uploadId;
        if (canonicalVideoUploadId) {
          for (const genClip of result) {
            try {
              await library.apiClient.POST(
                '/api/gen/{gen_id}/set_video_cover',
                {
                  params: { path: { gen_id: genClip.id } },
                  body: { video_cover_upload_id: canonicalVideoUploadId },
                }
              );
            } catch (err) {
              console.error('[set_video_cover] Error (post-gen):', err);
            }
          }
        } else {
          console.warn(
            'No uploadId available for set_video_cover after generation'
          );
        }
        setCurrentScreen('result');
      } else {
        throw new Error('No clips returned from generation');
      }
    } catch (error) {
      console.error('Error in creation:', error);
      toast({
        title: 'Error during creation',
        description:
          error instanceof Error
            ? error.message
            : 'Failed to create. Please try again.',
        status: 'error',
        duration: 5000,
        isClosable: true,
      });
    } finally {
      setIsCreating(false);
    }
  };

  const handleBackToUpload = () => {
    setCurrentScreen('upload');
    setAudioUrl(null);
    setAudioFile(null);
    setVideoUrl(null);
    setVideoFile(null);
    setUploadedClipId(null);
    setVideoS3Id(null);
    setVideoUploadId(null);
    setStyleDescription('');
    setGeneratedClips([]);
    setReadyClips([]);
    setIsCreating(false);
    setIsPolling(false);
    setProcessingStep('idle');
    setFileSelected(false);

    router.push('/video-sample');
  };

  useEffect(() => {
    if (currentScreen !== 'result' || generatedClips.length === 0) return;

    let cancelled = false;

    async function poll() {
      setIsPolling(true);

      let pollCount = 0;
      const maxPolls = 150; // 5 minutes max (150 * 2 seconds)

      while (!cancelled && pollCount < maxPolls) {
        pollCount++;
        const statuses = await Promise.all(
          generatedClips.map(async (clip) => {
            const data = await clips.loadClipById(clip.id);
            return {
              ...clip,
              status: data?.status,
              audio_url: data?.audio_url,
              video_url: data?.video_url,
              video_cover_url: data?.video_cover_url,
              metadata: data?.metadata,
            };
          })
        );

        statuses.forEach((c) => {
          if (c.status === 'complete' && !c.video_cover_url && !c.video_url) {
            console.warn(
              `[POLL WARNING] Clip ${c.id} is complete but has NO video URL!`,
              c
            );
          }
        });
        const completeClips = statuses.filter((c) => c.status === 'complete');
        setReadyClips([...completeClips]);
        if (completeClips.length === generatedClips.length) {
          setIsPolling(false);
          break;
        }
        if (pollCount >= maxPolls) {
          console.log('[POLL] Polling timed out after 5 minutes');
          setIsPolling(false);
          break;
        }
        await new Promise((res) => setTimeout(res, 2000));
      }
      return () => {
        cancelled = true;
      };
    }

    poll();

    return () => {
      cancelled = true;
    };
  }, [
    currentScreen,
    generatedClips,
    clips,
    library.apiClient,
    videoUploadState.uploadId,
  ]);

  // --- Upload Screen ---
  if (currentScreen === 'upload' && session.flags?.['video-sample']) {
    let statusText = 'Upload video here to begin.';
    if (fileSelected && processingStep !== 'done') {
      if (processingStep === 'processing' || processingStep === 'idle')
        statusText = 'Processing';
      if (processingStep === 'extracting') statusText = 'Extracting audio';
      if (processingStep === 'uploading') statusText = 'Uploading video';
    }
    const showFadeHeader = fileSelected && processingStep !== 'done';
    return (
      <div className='bg-background flex w-full flex-col items-center py-8'>
        {fileSelected && <StartOverButton onClick={handleBackToUpload} />}
        <div className='mt-32 flex w-full flex-col items-center'>
          {showFadeHeader ? (
            <SmoothFadeText text={statusText} />
          ) : (
            <h1 className='mb-8 text-center font-serif text-4xl font-light'>
              Upload audio or video here to begin.
            </h1>
          )}
          <div className='mx-auto flex w-full flex-col items-center gap-8'>
            <div className='flex w-full justify-center'>
              <div className='bg-background/80 flex w-full flex-col items-center rounded-lg p-8 transition-shadow hover:shadow-lg'>
                <SimpleVideoAudioUploader
                  onAudioExtracted={handleAudioExtracted}
                  onFileSelected={() => setFileSelected(true)}
                  fileSelected={fileSelected}
                />
              </div>
            </div>
          </div>
        </div>
        {fileSelected && processingStep !== 'done' && (
          <div className='mt-8 flex w-full flex-col items-center'>
            <WaveformLoader height={100} className='w-full' />
          </div>
        )}
      </div>
    );
  }

  // --- Create Screen ---
  if (currentScreen === 'create' && session.flags?.['video-sample']) {
    return (
      <div className='bg-background flex min-h-screen w-full flex-col items-center p-8'>
        <StartOverButton onClick={handleBackToUpload} />
        <div className='mt-32 flex w-full max-w-2xl flex-col items-center'>
          <h1 className='mb-8 text-center font-serif text-4xl font-light'>
            Choose a style for your video.
          </h1>
          {videoUrl && (
            <div className='mb-6 flex w-full flex-col items-center'>
              <video
                controls
                className='mx-auto max-h-[400px] w-full rounded-lg bg-black object-contain'
                src={videoUrl}
                style={{ maxWidth: 400 }}
              >
                Your browser does not support the video element.
              </video>
            </div>
          )}
          <div className='mb-6 flex gap-4'>
            <button
              className={`rounded-full border border-primary px-4 py-2 font-sans text-base shadow focus:outline-none ${mode === 'underpainting' ? 'bg-primary text-black' : 'bg-transparent text-primary'} hover:border-primary hover:bg-primary hover:text-black`}
              onClick={() => setMode('underpainting')}
              disabled={mode === 'underpainting'}
            >
              Add Instrumentals
            </button>
            <button
              className={`rounded-full border border-primary px-4 py-2 font-sans text-base shadow focus:outline-none ${mode === 'cover' ? 'bg-primary text-black' : 'bg-transparent text-primary'} hover:border-primary hover:bg-primary hover:text-black`}
              onClick={() => setMode('cover')}
              disabled={mode === 'cover'}
            >
              Cover
            </button>
          </div>
          <div className='flex w-full items-end gap-4'>
            <div className='flex-1'>
              <input
                type='text'
                value={styleDescription}
                onChange={(e) => setStyleDescription(e.target.value)}
                placeholder='Ex: gothic synthwave'
                className='w-full rounded-none border-0 border-b-2 border-white bg-transparent px-0 py-2 text-white placeholder-white/80 focus:outline-none'
              />
            </div>
            <div className='flex flex-col items-center gap-2'>
              <Button
                variant={ButtonVariant.Primary}
                shape={ButtonShape.Pill}
                size={ButtonSize.Medium}
                onClick={handleCreate}
                disabled={isCreating || !styleDescription.trim()}
              >
                {isCreating ? 'Creating...' : 'Create'}
              </Button>
            </div>
          </div>
        </div>
      </div>
    );
  }

  // --- Result Screen ---
  if (currentScreen === 'result' && generatedClips.length > 0) {
    // All videos are ready if every readyClip has a video_cover_url
    const allVideosReady =
      readyClips.length === generatedClips.length &&
      readyClips.every((clip) => clip.video_cover_url);
    return (
      <div className='bg-background flex min-h-screen w-full flex-col items-center'>
        <StartOverButton onClick={handleBackToUpload} />
        <div className='mt-32 flex w-full flex-col items-center'>
          {allVideosReady ? (
            <h1 className='mb-8 text-center font-serif text-4xl font-light'>
              Your videos are ready
            </h1>
          ) : (
            <SmoothFadeText text={'Creating your videos'} />
          )}
        </div>
        <div className='mt-8 flex w-full flex-row justify-center gap-8'>
          {generatedClips.map((clip) => {
            const readyClip = readyClips.find((c) => c.id === clip.id);
            return (
              <div
                key={clip.id}
                className='flex flex-col items-center rounded-lg p-4'
                style={{ width: 400 }}
              >
                {readyClip && readyClip.video_cover_url ? (
                  <>
                    <div
                      style={{
                        maxWidth: 400,
                        maxHeight: 400,
                        width: '100%',
                        aspectRatio: '16/9',
                        display: 'flex',
                        alignItems: 'center',
                        justifyContent: 'center',
                        background: '#111',
                      }}
                    >
                      <SyncedVideoAudioPlayer
                        videoUrl={readyClip.video_cover_url}
                        audioUrl={readyClip.audio_url}
                        imageUrl={readyClip.image_url}
                        className='block h-full w-full rounded object-contain'
                      />
                    </div>
                    <div className='mt-4 flex flex-row gap-2'>
                      <Tooltip label='Copy song link' placement='bottom'>
                        <button
                          className='flex h-10 w-10 items-center justify-center rounded-full border border-primary text-primary transition-colors hover:bg-primary hover:text-black focus:outline-none'
                          aria-label='Share'
                          onClick={async () =>
                            await shareClip(clips.apiClient, readyClip)
                          }
                        >
                          <ShareArrowIcon width={22} height={22} />
                        </button>
                      </Tooltip>
                      {/*
                      <button
                        className='w-10 h-10 flex items-center justify-center rounded-full border border-primary text-primary hover:bg-primary hover:text-black transition-colors focus:outline-none'
                        aria-label='Download video with audio'
                        onClick={async () => {
                          if (!readyClip.video_cover_url) {
                            toast({
                              title: 'Video not ready',
                              description:
                                'The video is still processing. Please try again in a few seconds.',
                              status: 'warning',
                              duration: 3000,
                              isClosable: true,
                            });
                            return;
                          }
                          try {
                            // Use type assertion since mux endpoint is new and not in generated types
                            const response = await (clips.apiClient as any).GET(
                              `/api/clips/${readyClip.id}/mux/`,
                              { responseType: 'blob' }
                            );

                            if (response.error) {
                              throw new Error(
                                response.error.message ||
                                  'Failed to download muxed video'
                              );
                            }

                            // Create download link
                            const blob = new Blob([response.data as BlobPart], {
                              type: 'video/mp4',
                            });
                            const url = window.URL.createObjectURL(blob);
                            const a = document.createElement('a');
                            a.href = url;
                            a.download = `${readyClip.title || 'clip'}_${readyClip.id.slice(0, 8)}_muxed.mp4`;
                            document.body.appendChild(a);
                            a.click();
                            document.body.removeChild(a);
                            window.URL.revokeObjectURL(url);

                            toast({
                              title: 'Download started',
                              description: 'Your muxed video is downloading.',
                              status: 'info',
                              duration: 3000,
                              isClosable: true,
                            });
                          } catch (error) {
                            console.error(
                              'Error downloading muxed video:',
                              error
                            );
                            toast({
                              title: 'Download failed',
                              description:
                                error instanceof Error
                                  ? error.message
                                  : 'Failed to download muxed video.',
                              status: 'error',
                              duration: 5000,
                              isClosable: true,
                            });
                          }
                        }}
                      >
                        <DownloadIcon width={22} height={22} />
                      </button>
                      */}
                      <Tooltip label='Go to song page' placement='bottom'>
                        <button
                          className='flex h-10 w-10 items-center justify-center rounded-full border border-primary text-primary transition-colors hover:bg-primary hover:text-black focus:outline-none'
                          aria-label='Go to song page'
                          onClick={() => router.push(`/song/${readyClip.id}`)}
                        >
                          <span className='text-lg font-bold'>→</span>
                        </button>
                      </Tooltip>
                    </div>
                  </>
                ) : (
                  <div
                    className={`w-full rounded bg-quaternary`}
                    style={{
                      maxWidth: 400,
                      maxHeight: 400,
                      width: '100%',
                      aspectRatio: '16/9',
                      ...{},
                    }}
                  >
                    <div
                      className={`h-full w-full ${LOADING_SHIMMER_ANIMATION} rounded`}
                      style={{ height: '100%' }}
                    />
                  </div>
                )}
              </div>
            );
          })}
        </div>
      </div>
    );
  }

  // --- Fallback ---
  return (
    <div className='bg-background flex min-h-screen w-full flex-col items-center justify-center p-8'>
      <StartOverButton onClick={handleBackToUpload} />
      {/* You can add a fallback message or loader here if needed */}
    </div>
  );
});

export default VideoSampleClient;
