import React, { useState, useCallback, useEffect, useRef } from 'react';
import { motion } from 'framer-motion';
import { useGeneration } from '@/context/GenerationContext';
import type { GenerationInputs, VideoProvider, GeneratedVideo, VeoSegment } from '@/types';
import { 
  SparklesIcon, 
  ArrowLeftIcon,
  ImageIcon 
} from './Icons';
import { 
  generatePrompts, 
  uploadImage, 
  klingGenerate, 
  klingExtend,
  waitForKlingVideo, 
  generateVideoWithRetry,
  downloadVideo, 
  getVideoDuration,
  generateThumbnails,
  replicateGenerate,
  waitForReplicateVideo,
  whisperAnalyzeAndExtract
} from '@/utils/api';

interface GenerationFormProps {
  provider: VideoProvider;
  onBack: () => void;
}

const voiceTypes = ['Deep', 'Warm', 'Crisp', 'None'];
const energyLevels = ['Low', 'Medium', 'High'];
const cameraStyles = ['Standard', 'Handheld', 'Steadicam', 'FPV Drone'];
const narrativeStyles = ['Standard', 'Documentary', 'Action', 'Introspective'];
const aspectRatios = ['9:16', '16:9', '1:1'];

// Generation modes
type GenerationMode = 'extend' | 'frame-continuity';

export const GenerationForm: React.FC<GenerationFormProps> = ({ provider, onBack }) => {
  const { startGeneration, updateProgress, addVideo, setStep, setError, setRetryState, updateSegments, addTaskId, removeTaskId, state } = useGeneration();
  const { retryState, generatedVideos, segments, isCancelling } = state;
  
  // Draft storage key
  const draftKey = `video-gen-draft-${provider}`;
  
  // Load draft on mount - initialize state from localStorage
  const loadDraft = useCallback(() => {
    try {
      const savedDraft = localStorage.getItem(draftKey);
      if (savedDraft) {
        const draft = JSON.parse(savedDraft);
        return draft;
      }
    } catch (error) {
      console.warn('Failed to load draft:', error);
    }
    return null;
  }, [draftKey]);
  
  const draft = loadDraft();
  const [draftRestored, setDraftRestored] = useState(!!draft);
  
  const [formState, setFormState] = useState<GenerationInputs>(
    draft?.formState || {
      script: '',
      style: '',
      voiceType: 'Deep',
      energyLevel: 'Medium',
      cameraStyle: 'Standard',
      narrativeStyle: 'Standard',
      seedValue: 12005,
      aspectRatio: '9:16',
      model: provider === 'kling' ? 'veo3_fast' : 'google/veo-3',
    }
  );
  
  const [imageFile, setImageFile] = useState<File | null>(null);
  const [imagePreview, setImagePreview] = useState<string | null>(draft?.imagePreview || null);
  const [isDragging, setIsDragging] = useState(false);
  const [isGenerating, setIsGenerating] = useState(false);
  
  // Generation mode selection
  const [generationMode, setGenerationMode] = useState<GenerationMode>(draft?.generationMode || 'frame-continuity');
  
  // Retry editing state
  const [retryDialogue, setRetryDialogue] = useState('');
  const [retryEnvironment, setRetryEnvironment] = useState('');
  const [retryAction, setRetryAction] = useState('');
  
  // Initialize retry fields when error occurs
  useEffect(() => {
    if (retryState && segments[retryState.failedSegmentIndex]) {
      const seg = segments[retryState.failedSegmentIndex];
      setRetryDialogue(seg.action_timeline?.dialogue || '');
      setRetryEnvironment(seg.scene_continuity?.environment || '');
      setRetryAction(seg.character_description?.current_state || '');
    }
  }, [retryState, segments]);
  
  const handleRetrySubmit = () => {
    if (!retryState) return;
    
    const idx = retryState.failedSegmentIndex;
    const updatedSegments = [...segments];
    
    // Update the segment with edited values
    if (updatedSegments[idx]) {
      updatedSegments[idx] = {
        ...updatedSegments[idx],
        action_timeline: {
          ...updatedSegments[idx].action_timeline,
          dialogue: retryDialogue
        },
        scene_continuity: {
          ...updatedSegments[idx].scene_continuity,
          environment: retryEnvironment
        },
        character_description: {
          ...updatedSegments[idx].character_description,
          current_state: retryAction
        }
      };
      
      updateSegments(updatedSegments);
    }
    
    // Clear error and resume
    setRetryState(null);
    setStep('generating_video');
    setIsGenerating(true);
    
    // Resume generation based on provider
    if (provider === 'kling') {
        if (generationMode === 'frame-continuity') {
            handleKlingFrameContinuityFlow();
        } else {
            handleKlingExtendFlow();
        }
    } else {
        handleReplicateGeneration();
    }
  };

  const handleCancelRetry = () => {
    setRetryState(null);
    setIsGenerating(false);
  };

  // Show notification if draft was restored
  useEffect(() => {
    if (draftRestored) {
      console.log('📝 Draft restored from localStorage');
      // Auto-hide notification after 5 seconds
      const timer = setTimeout(() => setDraftRestored(false), 5000);
      return () => clearTimeout(timer);
    }
  }, [draftRestored]);
  
  // Save draft whenever formState, imagePreview, or generationMode changes
  // Skip saving on initial mount to avoid overwriting with default values
  const isInitialMount = useRef(true);
  useEffect(() => {
    if (isInitialMount.current) {
      isInitialMount.current = false;
      return;
    }
    
    try {
      const draft = {
        formState,
        imagePreview,
        generationMode,
        savedAt: new Date().toISOString(),
      };
      localStorage.setItem(draftKey, JSON.stringify(draft));
    } catch (error) {
      console.warn('Failed to save draft:', error);
    }
  }, [formState, imagePreview, generationMode, draftKey]);
  
  // Clear draft function
  const clearDraft = useCallback(() => {
    try {
      localStorage.removeItem(draftKey);
      setDraftRestored(false);
      console.log('🗑️ Draft cleared');
    } catch (error) {
      console.warn('Failed to clear draft:', error);
    }
  }, [draftKey]);

  // Calculate estimated segments
  const wordCount = formState.script.trim().split(/\s+/).filter(w => w).length;
  const estimatedSegments = wordCount > 0 ? Math.max(1, Math.min(Math.ceil(wordCount / 17), 10)) : 0;

  // Handle input changes
  const handleChange = (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement>) => {
    const { name, value } = e.target;
    setFormState(prev => ({ ...prev, [name]: value }));
  };

  // Handle image upload
  const handleImageUpload = useCallback((file: File) => {
    if (file.type.startsWith('image/')) {
      setImageFile(file);
      const reader = new FileReader();
      reader.onloadend = () => setImagePreview(reader.result as string);
      reader.readAsDataURL(file);
    }
  }, []);

  // Drag and drop handlers
  const handleDragOver = (e: React.DragEvent) => {
    e.preventDefault();
    setIsDragging(true);
  };

  const handleDragLeave = () => setIsDragging(false);

  const handleDrop = (e: React.DragEvent) => {
    e.preventDefault();
    setIsDragging(false);
    const file = e.dataTransfer.files[0];
    if (file) handleImageUpload(file);
  };

  // Extract last frame from video blob
  const extractLastFrame = async (videoBlob: Blob): Promise<File> => {
    return new Promise((resolve, reject) => {
      const video = document.createElement('video');
      video.preload = 'metadata';
      video.muted = true;
      video.src = URL.createObjectURL(videoBlob);

      video.onloadedmetadata = async () => {
        // Seek to near the end of the video
        const targetTime = Math.max(0, video.duration - 0.1);
        video.currentTime = targetTime;
      };

      video.onseeked = () => {
        // Create canvas and draw current frame
        const canvas = document.createElement('canvas');
        canvas.width = video.videoWidth;
        canvas.height = video.videoHeight;
        const ctx = canvas.getContext('2d');
        
        if (!ctx) {
          URL.revokeObjectURL(video.src);
          reject(new Error('Could not get canvas context'));
          return;
        }

        ctx.drawImage(video, 0, 0);
        
        // Convert to blob then to file
        canvas.toBlob((blob) => {
          URL.revokeObjectURL(video.src);
          
          if (!blob) {
            reject(new Error('Could not extract frame'));
            return;
          }

          const file = new File([blob], `frame-${Date.now()}.jpg`, { type: 'image/jpeg' });
          resolve(file);
        }, 'image/jpeg', 0.95);
      };

      video.onerror = () => {
        URL.revokeObjectURL(video.src);
        reject(new Error('Failed to load video for frame extraction'));
      };
    });
  };

  // ============================================
  // KIE GENERATION - FRAME CONTINUITY FLOW
  // ============================================
  // This mirrors the Replicate flow from standalone_video_creator.py:
  // 1. Generate first video with original reference image
  // 2. Extract last frame using the whisper analysis from generated video
  // 3. Use that frame as reference for next segment
  // 4. Repeat for all segments
  
  const handleKlingFrameContinuityFlow = async () => {
    if (!imageFile || !formState.script.trim()) return;

    setIsGenerating(true);
    setError(null);

    try {
      // Step 1: Generate prompts using GPT-4o
      updateProgress('Analyzing script with GPT-4o...');
      
      const formData = new FormData();
      formData.append('script', formState.script);
      formData.append('style', formState.style || 'clean, lifestyle UGC');
      formData.append('jsonFormat', 'standard');
      formData.append('continuationMode', 'true');
      formData.append('voiceType', formState.voiceType || '');
      formData.append('energyLevel', formState.energyLevel || '');
      formData.append('settingMode', 'single');
      formData.append('cameraStyle', formState.cameraStyle || '');
      formData.append('narrativeStyle', formState.narrativeStyle || '');
      formData.append('image', imageFile);

      const payload = await generatePrompts(formData);

      if (!payload?.segments?.length) {
        throw new Error('No segments generated from script');
      }

      const segments = payload.segments;
      updateProgress(`Generated ${segments.length} segments. Starting video generation...`);
      startGeneration(segments);

      // Track current reference image (starts with original)
      let currentImageFile = imageFile;
      const generatedVideos: GeneratedVideo[] = [];

      // Step 2: Generate videos segment by segment with frame continuity
      for (let i = 0; i < segments.length; i++) {
        const segment = segments[i];
        const isLastSegment = i === segments.length - 1;
        
        updateProgress(
          `Generating video ${i + 1} of ${segments.length}...${i > 0 ? ' (using last frame from previous)' : ''}`, 
          i, 
          segments.length
        );

        // Upload current reference image
        updateProgress(`Uploading reference image for segment ${i + 1}...`);
        const uploadResult = await uploadImage(currentImageFile);
        const hostedImageUrl = uploadResult.url;

        console.log(`🖼️ Segment ${i + 1} using image: ${i === 0 ? 'original' : 'last frame from previous'}`);

        // Generate video with current reference image
        updateProgress(`Submitting segment ${i + 1} to KIE Veo 3.1...`);
        const generateResult = await klingGenerate({
          prompt: segment,
          imageUrls: [hostedImageUrl],
          model: 'veo3_fast',
          aspectRatio: formState.aspectRatio,
          generationType: 'FIRST_AND_LAST_FRAMES_2_VIDEO',
          seeds: formState.seedValue,
          voiceType: formState.voiceType,
        });

        // Wait for completion
        updateProgress(`Processing video ${i + 1}... (this may take 1-2 minutes)`);
        const videoUrl = await waitForKlingVideo(generateResult.taskId);

        // Download video
        updateProgress(`Downloading video ${i + 1}...`);
        const videoBlob = await downloadVideo(videoUrl);
        const blobUrl = URL.createObjectURL(videoBlob);

        // Get video duration
        const videoFile = new File([videoBlob], `segment-${i + 1}.mp4`, { type: 'video/mp4' });
        const duration = await getVideoDuration(videoFile);
        const thumbnails = await generateThumbnails(videoFile);

        // Use Whisper to find optimal trim point, extract frame, and get transcription
        let trimPoint = duration; // Default to full duration
        let transcribedText = ''; // What Whisper actually heard
        
        if (!isLastSegment) {
          updateProgress(`Analyzing video ${i + 1} with Whisper for optimal continuity...`);
          try {
            // Get dialogue from segment for Whisper analysis
            const dialogue = segment.action_timeline?.dialogue || '';
            
            const whisperResult = await whisperAnalyzeAndExtract({
              video_url: videoUrl,
              dialogue: dialogue,
              buffer_time: 0.3,
              model_size: 'base'
            });
            
            if (whisperResult.success && whisperResult.frame_base64) {
              // Convert base64 frame to File for next segment
              const base64Data = whisperResult.frame_base64.split(',')[1] || whisperResult.frame_base64;
              const byteCharacters = atob(base64Data);
              const byteNumbers = new Array(byteCharacters.length);
              for (let j = 0; j < byteCharacters.length; j++) {
                byteNumbers[j] = byteCharacters.charCodeAt(j);
              }
              const byteArray = new Uint8Array(byteNumbers);
              const frameBlob = new Blob([byteArray], { type: 'image/jpeg' });
              currentImageFile = new File([frameBlob], `whisper-frame-${i + 1}.jpg`, { type: 'image/jpeg' });
              
              // Store trim point for later merge
              if (whisperResult.trim_point) {
                trimPoint = whisperResult.trim_point;
              }
              
              // Store transcribed text for prompt refinement
              if (whisperResult.transcribed_text) {
                transcribedText = whisperResult.transcribed_text;
                console.log(`📝 Whisper transcription: "${transcribedText.substring(0, 100)}..."`);
              }
              
              console.log(`✅ Whisper: Last word at ${whisperResult.last_word_timestamp?.toFixed(2)}s, frame at ${whisperResult.frame_timestamp?.toFixed(2)}s, trim at ${trimPoint.toFixed(2)}s`);
              
              // REFINE NEXT SEGMENT PROMPT with frame + transcription
              const nextSegment = segments[i + 1];
              if (nextSegment && currentImageFile) {
                updateProgress(`Refining segment ${i + 2} prompt with visual and audio context...`);
                try {
                  const { refinePromptWithContext } = await import('@/utils/api');
                  const refined = await refinePromptWithContext(
                    nextSegment,
                    currentImageFile,
                    transcribedText,
                    dialogue
                  );
                  // Update the next segment with refined prompt
                  segments[i + 1] = refined.refined_prompt as typeof nextSegment;
                  console.log(`✅ Refined segment ${i + 2} prompt for consistency`);
                } catch (refineError) {
                  console.warn(`⚠️ Prompt refinement failed, using original:`, refineError);
                }
              }
            } else {
              // Fallback to simple last frame extraction
              console.log(`⚠️ Whisper failed (${whisperResult.error}), falling back to last frame extraction`);
              const lastFrameFile = await extractLastFrame(videoBlob);
              currentImageFile = lastFrameFile;
            }
          } catch (frameError) {
            console.error(`⚠️ Whisper analysis failed, using fallback:`, frameError);
            try {
              const lastFrameFile = await extractLastFrame(videoBlob);
              currentImageFile = lastFrameFile;
            } catch {
              // Continue with current image if all extraction fails
            }
          }
        }

        // Add to generated videos with trim metadata
        const generatedVideo: GeneratedVideo = {
          id: `video-${Date.now()}-${i}`,
          url: videoUrl,
          blobUrl,
          segment,
          duration,
          thumbnails,
          trimPoint, // Store trim point for merge
        };
        generatedVideos.push(generatedVideo);
        addVideo(generatedVideo);

        updateProgress(`Completed video ${i + 1} of ${segments.length}`, i + 1, segments.length);
      }

      // All done!
      clearDraft(); // Clear draft on successful generation
      clearDraft(); // Clear draft on successful generation
      setStep('completed');
      updateProgress('All videos generated successfully!');

    } catch (err) {
      console.error('Generation error:', err);
      const errorMessage = err instanceof Error ? err.message : 'Generation failed';
      
      // Enable retry mode
      setRetryState({
        failedSegmentIndex: generatedVideos.length, // Current segment that failed
        error: errorMessage
      });
      setStep('configuring'); // Go back to form, but with retry overlay
      
    } finally {
      setIsGenerating(false);
    }
  };

  // ============================================
  // KIE GENERATION - EXTEND API FLOW
  // ============================================
  // Original flow using KIE's extend API
  
  const handleKlingExtendFlow = async () => {
    if (!imageFile || !formState.script.trim()) return;

    setIsGenerating(true);
    setError(null);

    try {
      // Step 1: Generate prompts using GPT-4o
      updateProgress('Analyzing script with GPT-4o...');
      
      const formData = new FormData();
      formData.append('script', formState.script);
      formData.append('style', formState.style || 'clean, lifestyle UGC');
      formData.append('jsonFormat', 'standard');
      formData.append('continuationMode', 'true');
      formData.append('voiceType', formState.voiceType || '');
      formData.append('energyLevel', formState.energyLevel || '');
      formData.append('settingMode', 'single');
      formData.append('cameraStyle', formState.cameraStyle || '');
      formData.append('narrativeStyle', formState.narrativeStyle || '');
      formData.append('image', imageFile);

      // Use existing segments if retrying, otherwise generate new ones
      let payload: { segments: VeoSegment[] };
      if (retryState && segments.length > 0) {
        // Retry mode: use existing segments (they may have been edited)
        payload = { segments };
        updateProgress(`Using existing ${segments.length} segments for retry...`);
      } else {
        // Normal mode: generate new segments
        payload = await generatePrompts(formData);
        if (!payload?.segments?.length) {
          throw new Error('No segments generated from script');
        }
        updateProgress(`Generated ${payload.segments.length} segments. Starting video generation...`);
        startGeneration(payload.segments);
      }

      // Step 2: Upload reference image once
      updateProgress('Uploading reference image...');
      const uploadResult = await uploadImage(imageFile);
      const hostedImageUrl = uploadResult.url;

      // Step 3: Generate videos (resume from where we left off if retrying)
      const startIndex = generatedVideos.length;
      let currentTaskId: string | null = null;
      let currentImageUrl = hostedImageUrl; // Start with original image

      // If resuming, extract last frame from previous video for continuity
      if (startIndex > 0 && generatedVideos[startIndex - 1]?.blobUrl) {
        updateProgress(`Extracting last frame from segment ${startIndex} for continuity...`);
        try {
          const lastVideoBlob = await fetch(generatedVideos[startIndex - 1].blobUrl!).then(r => r.blob());
          const lastFrameFile = await extractLastFrame(lastVideoBlob);
          const frameUploadResult = await uploadImage(lastFrameFile);
          currentImageUrl = frameUploadResult.url;
          updateProgress(`Using frame from segment ${startIndex} for segment ${startIndex + 1}...`);
        } catch (frameError) {
          console.warn('Failed to extract frame, using original image:', frameError);
          // Continue with original image
        }
      }

      for (let i = startIndex; i < payload.segments.length; i++) {
        const segment = payload.segments[i];
        
        updateProgress(`Generating video ${i + 1} of ${payload.segments.length}...`, i, payload.segments.length);

        // Generate video with automatic retry (retries once on failure)
        updateProgress(`Processing video ${i + 1}... (this may take 1-2 minutes)`);
        // Check if cancelled
        if (isCancelling) {
          throw new Error('Generation cancelled by user');
        }

        const videoUrl = await generateVideoWithRetry(async () => {
        if (i === 0 || (i === startIndex && startIndex > 0)) {
          // First segment OR resuming after failure: use generate API with current image
          const generateResult = await klingGenerate({
            prompt: segment,
            imageUrls: [currentImageUrl],
            model: 'veo3_fast',
            aspectRatio: formState.aspectRatio,
            generationType: 'FIRST_AND_LAST_FRAMES_2_VIDEO',
            seeds: formState.seedValue,
            voiceType: formState.voiceType,
          });
          currentTaskId = generateResult.taskId;
          addTaskId(currentTaskId);
            return generateResult;
        } else {
          // Subsequent segments: use extend API
            const extendResult = await klingExtend(
              currentTaskId!,
              segment,
              formState.seedValue,
              formState.voiceType
            );
          currentTaskId = extendResult.taskId;
          addTaskId(currentTaskId);
            return extendResult;
          }
        }, 300000, (attempt) => {
          updateProgress(`Retrying video ${i + 1}... (attempt ${attempt}/2)`);
        });

        // Download and save
        updateProgress(`Downloading video ${i + 1}...`);
        const videoBlob = await downloadVideo(videoUrl);
        const blobUrl = URL.createObjectURL(videoBlob);

        const videoFile = new File([videoBlob], `segment-${i + 1}.mp4`, { type: 'video/mp4' });
        const duration = await getVideoDuration(videoFile);
        const thumbnails = await generateThumbnails(videoFile);

        addVideo({
          id: `video-${Date.now()}-${i}`,
          url: videoUrl,
          blobUrl,
          segment,
          duration,
          thumbnails,
        });

        updateProgress(`Completed video ${i + 1} of ${payload.segments.length}`, i + 1, payload.segments.length);
        
        // Remove task ID after completion
        if (currentTaskId) {
          removeTaskId(currentTaskId);
        }
      }

      clearDraft(); // Clear draft on successful generation
      setStep('completed');
      updateProgress('All videos generated successfully!');

    } catch (err) {
      console.error('Generation error:', err);
      const errorMessage = err instanceof Error ? err.message : 'Generation failed';
      
      // If cancelled, don't show retry option
      if (errorMessage.includes('cancelled') || isCancelling) {
        setError('Generation cancelled by user');
        setStep('error');
      } else {
        // Enable retry mode
        setRetryState({
          failedSegmentIndex: generatedVideos.length, // Current segment that failed
          error: errorMessage
        });
        setStep('configuring'); // Go back to form, but with retry overlay
      }
      
    } finally {
      setIsGenerating(false);
      // Clean up any remaining task IDs
      state.activeTaskIds.forEach(taskId => removeTaskId(taskId));
    }
  };

  // ============================================
  // REPLICATE GENERATION - FRAME CONTINUITY FLOW
  // ============================================
  // This mirrors the approach from standalone_video_creator.py:
  // 1. Generate prompts using GPT-4o
  // 2. For each segment, generate video with current reference image
  // 3. Extract last frame from generated video
  // 4. Use that frame as reference for next segment
  // 5. Result: Perfect visual continuity across all segments
  
  const handleReplicateGeneration = async () => {
    if (!formState.script.trim()) return;

    setIsGenerating(true);
    setError(null);

    try {
      // Step 1: Generate prompts using GPT-4o
      // Note: Replicate can work without an image, but for consistency we encourage one
      updateProgress('Analyzing script with GPT-4o...');
      
      const formData = new FormData();
      formData.append('script', formState.script);
      formData.append('style', formState.style || 'clean, lifestyle UGC');
      formData.append('jsonFormat', 'standard');
      formData.append('continuationMode', 'true');
      formData.append('voiceType', formState.voiceType || '');
      formData.append('energyLevel', formState.energyLevel || '');
      formData.append('settingMode', 'single');
      formData.append('cameraStyle', formState.cameraStyle || '');
      formData.append('narrativeStyle', formState.narrativeStyle || '');
      
      // If image provided, include it for GPT-4o analysis
      if (imageFile) {
        formData.append('image', imageFile);
      } else {
        // Create a placeholder image for GPT-4o (it needs one for analysis)
        // In production, you might want to handle this differently
        const placeholderBlob = new Blob(['placeholder'], { type: 'image/jpeg' });
        formData.append('image', placeholderBlob, 'placeholder.jpg');
      }

      const payload = await generatePrompts(formData);

      if (!payload?.segments?.length) {
        throw new Error('No segments generated from script');
      }

      const segments = payload.segments;
      updateProgress(`Generated ${segments.length} segments. Starting Replicate generation...`);
      startGeneration(segments);

      // Track current reference image (starts with original if provided)
      let currentImageFile = imageFile;
      const generatedVideos: GeneratedVideo[] = [];

      // Step 2: Generate videos segment by segment with frame continuity
      for (let i = 0; i < segments.length; i++) {
        const segment = segments[i];
        const isLastSegment = i === segments.length - 1;
        
        updateProgress(
          `Generating video ${i + 1} of ${segments.length} with Replicate...${i > 0 ? ' (using last frame)' : ''}`, 
          i, 
          segments.length
        );

        // Convert structured segment to text prompt for Replicate
        // Replicate models typically expect text prompts
        const textPrompt = convertSegmentToTextPrompt(segment);
        
        console.log(`🎬 Segment ${i + 1} prompt:`, textPrompt.substring(0, 100) + '...');

        // Upload current reference image if available
        let imageUrl: string | undefined;
        if (currentImageFile) {
          updateProgress(`Uploading reference image for segment ${i + 1}...`);
          const uploadResult = await uploadImage(currentImageFile);
          imageUrl = uploadResult.url;
          console.log(`🖼️ Segment ${i + 1} using image: ${i === 0 ? 'original' : 'last frame from previous'}`);
        }

        // Generate video with Replicate
        updateProgress(`Submitting segment ${i + 1} to Replicate...`);
        const generateResult = await replicateGenerate({
          prompt: textPrompt,
          imageUrl: imageUrl,
          model: formState.model || 'google/veo-3',
          aspectRatio: formState.aspectRatio,
        });

        // Wait for completion (polling)
        updateProgress(`Processing video ${i + 1}... (this may take 2-5 minutes)`);
        const videoUrl = await waitForReplicateVideo(generateResult.id);

        // Download video
        updateProgress(`Downloading video ${i + 1}...`);
        const videoBlob = await downloadVideo(videoUrl);
        const blobUrl = URL.createObjectURL(videoBlob);

        // Get video duration and thumbnails
        const videoFile = new File([videoBlob], `segment-${i + 1}.mp4`, { type: 'video/mp4' });
        const duration = await getVideoDuration(videoFile);
        const thumbnails = await generateThumbnails(videoFile);

        // Use Whisper to find optimal trim point, extract frame, and get transcription
        // This is more accurate than extracting the very last frame
        let trimPoint = duration; // Default to full duration
        let transcribedText = ''; // What Whisper actually heard
        
        if (!isLastSegment) {
          updateProgress(`Analyzing video ${i + 1} with Whisper for optimal continuity...`);
          try {
            // Get dialogue from segment for Whisper analysis
            const dialogue = segment.action_timeline?.dialogue || textPrompt;
            
            const whisperResult = await whisperAnalyzeAndExtract({
              video_url: videoUrl,
              dialogue: dialogue,
              buffer_time: 0.3,
              model_size: 'base'
            });
            
            if (whisperResult.success && whisperResult.frame_base64) {
              // Convert base64 frame to File for next segment
              const base64Data = whisperResult.frame_base64.split(',')[1] || whisperResult.frame_base64;
              const byteCharacters = atob(base64Data);
              const byteNumbers = new Array(byteCharacters.length);
              for (let j = 0; j < byteCharacters.length; j++) {
                byteNumbers[j] = byteCharacters.charCodeAt(j);
              }
              const byteArray = new Uint8Array(byteNumbers);
              const frameBlob = new Blob([byteArray], { type: 'image/jpeg' });
              currentImageFile = new File([frameBlob], `whisper-frame-${i + 1}.jpg`, { type: 'image/jpeg' });
              
              // Store trim point for later merge
              if (whisperResult.trim_point) {
                trimPoint = whisperResult.trim_point;
              }
              
              // Store transcribed text for prompt refinement
              if (whisperResult.transcribed_text) {
                transcribedText = whisperResult.transcribed_text;
                console.log(`📝 Whisper transcription: "${transcribedText.substring(0, 100)}..."`);
              }
              
              console.log(`✅ Whisper: Last word at ${whisperResult.last_word_timestamp?.toFixed(2)}s, frame at ${whisperResult.frame_timestamp?.toFixed(2)}s, trim at ${trimPoint.toFixed(2)}s`);
              
              // REFINE NEXT SEGMENT PROMPT with frame + transcription
              const nextSegment = segments[i + 1];
              if (nextSegment && currentImageFile) {
                updateProgress(`Refining segment ${i + 2} prompt with visual and audio context...`);
                try {
                  const { refinePromptWithContext } = await import('@/utils/api');
                  const refined = await refinePromptWithContext(
                    nextSegment,
                    currentImageFile,
                    transcribedText,
                    dialogue
                  );
                  // Update the next segment with refined prompt
                  segments[i + 1] = refined.refined_prompt as typeof nextSegment;
                  console.log(`✅ Refined segment ${i + 2} prompt for consistency`);
                } catch (refineError) {
                  console.warn(`⚠️ Prompt refinement failed, using original:`, refineError);
                }
              }
            } else {
              // Fallback to simple last frame extraction
              console.log(`⚠️ Whisper failed (${whisperResult.error}), falling back to last frame extraction`);
              const lastFrameFile = await extractLastFrame(videoBlob);
              currentImageFile = lastFrameFile;
            }
          } catch (frameError) {
            console.error(`⚠️ Whisper analysis failed, using fallback:`, frameError);
            try {
              const lastFrameFile = await extractLastFrame(videoBlob);
              currentImageFile = lastFrameFile;
            } catch {
              // Continue with current image if all extraction fails
            }
          }
        }

        // Add to generated videos with trim metadata
        const generatedVideo: GeneratedVideo = {
          id: `video-${Date.now()}-${i}`,
          url: videoUrl,
          blobUrl,
          segment,
          duration,
          thumbnails,
          trimPoint, // Store trim point for merge
        };
        generatedVideos.push(generatedVideo);
        addVideo(generatedVideo);

        updateProgress(`Completed video ${i + 1} of ${segments.length}`, i + 1, segments.length);
      }

      // All done!
      setStep('completed');
      updateProgress('All videos generated successfully with Replicate!');

    } catch (err) {
      console.error('Replicate generation error:', err);
      const errorMessage = err instanceof Error ? err.message : 'Replicate generation failed';
      
      // Enable retry mode
      setRetryState({
        failedSegmentIndex: generatedVideos.length, // Current segment that failed
        error: errorMessage
      });
      setStep('configuring'); // Go back to form, but with retry overlay
      
    } finally {
      setIsGenerating(false);
    }
  };

  // Helper: Convert structured segment JSON to text prompt for Replicate
  // Replicate models typically expect plain text, not structured JSON
  const convertSegmentToTextPrompt = (segment: VeoSegment): string => {
    const parts: string[] = [];
    
    // Extract dialogue
    const dialogue = segment.action_timeline?.dialogue;
    if (dialogue) {
      parts.push(`"${dialogue}"`);
    }
    
    // Extract character description
    const character = segment.character_description;
    if (character?.current_state) {
      parts.push(`Character: ${character.current_state}`);
    }
    
    // Extract scene description
    const scene = segment.scene_continuity;
    if (scene?.environment) {
      parts.push(`Scene: ${scene.environment}`);
    }
    if (scene?.lighting_state) {
      parts.push(`Lighting: ${scene.lighting_state}`);
    }
    if (scene?.camera_position) {
      parts.push(`Camera: ${scene.camera_position}`);
    }
    if (scene?.camera_movement) {
      parts.push(`Movement: ${scene.camera_movement}`);
    }
    
    // Extract synchronized actions
    const syncedActions = segment.action_timeline?.synchronized_actions;
    if (syncedActions) {
      const actionsList = Object.entries(syncedActions)
        .filter(([, value]) => value)
        .map(([key, value]) => `${key}: ${value}`)
        .join('; ');
      if (actionsList) {
        parts.push(`Actions: ${actionsList}`);
      }
    }
    
    // Add instruction to not include captions/subtitles
    parts.push('Do not include any captions, subtitles, or text overlays in the video');
    
    return parts.join('. ');
  };

  // Main submit handler
  const handleSubmit = (e: React.FormEvent) => {
    e.preventDefault();
    
    if (provider === 'kling') {
      if (generationMode === 'frame-continuity') {
        handleKlingFrameContinuityFlow();
      } else {
        handleKlingExtendFlow();
      }
    } else {
      handleReplicateGeneration();
    }
  };

  const isValid = provider === 'kling' 
    ? !!imageFile && formState.script.trim().length > 0
    : formState.script.trim().length > 0;

  return (
    <motion.div
      initial={{ opacity: 0, x: 20 }}
      animate={{ opacity: 1, x: 0 }}
      exit={{ opacity: 0, x: -20 }}
      className="max-w-6xl mx-auto p-8"
    >
      {/* Header */}
      <div className="flex items-center justify-between mb-8">
        <div>
          <button
            onClick={onBack}
            className="flex items-center gap-2 text-void-400 hover:text-void-200 transition-colors mb-4"
          >
            <ArrowLeftIcon size={20} />
            <span>Back to providers</span>
          </button>
          <h1 className="text-3xl font-display font-bold">
            <span className={provider === 'kling' ? 'gradient-text' : 'gradient-text-electric'}>
              {provider === 'kling' ? 'KIE API' : 'Replicate'}
            </span>
            <span className="text-void-200"> Video Generation</span>
          </h1>
          <p className="text-void-400 mt-2">
            {provider === 'kling' 
              ? 'Generate professional UGC videos with AI-powered segmentation'
              : 'Create unique videos with open-source models'
            }
          </p>
        </div>
      </div>

      {/* Retry Modal */}
      {retryState && (
        <div className="fixed inset-0 z-50 flex items-center justify-center bg-black/80 backdrop-blur-sm p-4">
          <motion.div 
            initial={{ opacity: 0, scale: 0.95 }}
            animate={{ opacity: 1, scale: 1 }}
            className="bg-void-900 border border-void-700 rounded-2xl p-6 max-w-2xl w-full shadow-2xl overflow-y-auto max-h-[90vh]"
          >
            <div className="flex items-center gap-3 mb-4 text-red-400">
              <svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" />
              </svg>
              <h3 className="text-xl font-bold">Generation Failed</h3>
            </div>
            
            <p className="text-void-300 mb-6 p-4 bg-void-800 rounded-lg border border-void-700">
              Error at segment {retryState.failedSegmentIndex + 1}: <span className="text-red-300">{retryState.error}</span>
            </p>
            
            <div className="space-y-4 mb-8">
              <h4 className="font-semibold text-void-200">Edit Segment {retryState.failedSegmentIndex + 1} to fix the issue:</h4>
              
              <div>
                <label className="block text-sm font-medium text-void-400 mb-1">Dialogue</label>
                <textarea
                  value={retryDialogue}
                  onChange={(e) => setRetryDialogue(e.target.value)}
                  className="w-full bg-void-950 border border-void-700 rounded-lg p-3 text-void-100 h-24 focus:border-coral-500 focus:outline-none"
                  placeholder="Adjust dialogue..."
                />
              </div>
              
              <div>
                <label className="block text-sm font-medium text-void-400 mb-1">Action / Character State</label>
                <textarea
                  value={retryAction}
                  onChange={(e) => setRetryAction(e.target.value)}
                  className="w-full bg-void-950 border border-void-700 rounded-lg p-3 text-void-100 h-24 focus:border-coral-500 focus:outline-none"
                  placeholder="Adjust action description..."
                />
              </div>

              <div>
                <label className="block text-sm font-medium text-void-400 mb-1">Environment</label>
                <textarea
                  value={retryEnvironment}
                  onChange={(e) => setRetryEnvironment(e.target.value)}
                  className="w-full bg-void-950 border border-void-700 rounded-lg p-3 text-void-100 h-24 focus:border-coral-500 focus:outline-none"
                  placeholder="Adjust environment description..."
                />
              </div>
            </div>
            
            <div className="flex justify-end gap-3">
              <button 
                onClick={handleCancelRetry}
                className="px-4 py-2 rounded-lg text-void-300 hover:text-white hover:bg-void-800 transition-colors"
              >
                Cancel
              </button>
              <button 
                onClick={handleRetrySubmit}
                className="px-6 py-2 bg-gradient-to-r from-coral-500 to-coral-600 text-white font-semibold rounded-lg hover:from-coral-400 hover:to-coral-500 shadow-lg shadow-coral-500/20"
              >
                Retry Generation
              </button>
            </div>
          </motion.div>
        </div>
      )}

      {/* Draft Restored Notification */}
      {draftRestored && (
        <motion.div
          initial={{ opacity: 0, y: -20 }}
          animate={{ opacity: 1, y: 0 }}
          exit={{ opacity: 0, y: -20 }}
          className="mb-6 p-4 bg-void-800/80 border border-void-600 rounded-xl flex items-center justify-between"
        >
          <div className="flex items-center gap-3">
            <div className="w-2 h-2 rounded-full bg-green-500 animate-pulse" />
            <div>
              <p className="text-sm font-medium text-void-200">Draft restored</p>
              <p className="text-xs text-void-400">Your previous inputs have been loaded</p>
            </div>
          </div>
          <button
            onClick={() => setDraftRestored(false)}
            className="text-void-400 hover:text-void-200 transition-colors"
          >
            <svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
              <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
            </svg>
          </button>
        </motion.div>
      )}

      <form onSubmit={handleSubmit}>
        <div className="grid grid-cols-1 lg:grid-cols-2 gap-8">
          {/* Left Column - Script & Style */}
          <div className="space-y-6">
            {/* Script Input */}
            <div className="card">
              <label className="block text-sm font-semibold text-void-200 mb-3">
                Script <span className="text-coral-400">*</span>
              </label>
              <textarea
                name="script"
                value={formState.script}
                onChange={handleChange}
                rows={10}
                className="textarea-field font-mono text-sm"
                placeholder="Enter your complete video script here...

The AI will automatically analyze and segment your script into optimal video chunks, typically 8 seconds each."
                required
              />
              <div className="flex items-center justify-between mt-3">
                <p className="text-xs text-void-500">
                  AI will automatically segment your script
                </p>
                {wordCount > 0 && (
                  <div className="flex items-center gap-4 text-xs">
                    <span className="text-void-400">{wordCount} words</span>
                    <span className={`font-semibold ${provider === 'kling' ? 'text-coral-400' : 'text-electric-400'}`}>
                      ~{estimatedSegments} segments
                    </span>
                  </div>
                )}
              </div>
            </div>

            {/* Style Input */}
            <div className="card">
              <label className="block text-sm font-semibold text-void-200 mb-3">
                Visual Style
              </label>
              <textarea
                name="style"
                value={formState.style}
                onChange={handleChange}
                rows={3}
                className="textarea-field"
                placeholder="e.g., Cinematic, hyper-realistic, natural lighting, modern aesthetic, warm tones..."
              />
            </div>

            {/* Generation Mode Selection (Kling only) */}
            {provider === 'kling' && (
              <div className="card border-2 border-coral-500/30 bg-coral-500/5">
                <h4 className="text-sm font-semibold text-void-200 mb-4">
                  Generation Mode
                </h4>
                <div className="space-y-3">
                  <label className="flex items-start gap-3 cursor-pointer group">
                    <input
                      type="radio"
                      name="generationMode"
                      value="frame-continuity"
                      checked={generationMode === 'frame-continuity'}
                      onChange={() => setGenerationMode('frame-continuity')}
                      className="mt-1 w-4 h-4 accent-coral-500"
                    />
                    <div>
                      <span className="text-sm font-medium text-void-100 group-hover:text-coral-400 transition-colors">
                        🎯 Frame Continuity (Recommended)
                      </span>
                      <p className="text-xs text-void-400 mt-0.5">
                        Extract last frame from each video → Use as reference for next segment.
                        <br />
                        <span className="text-coral-400">Best for visual consistency across segments.</span>
                      </p>
                    </div>
                  </label>

                  <label className="flex items-start gap-3 cursor-pointer group">
                    <input
                      type="radio"
                      name="generationMode"
                      value="extend"
                      checked={generationMode === 'extend'}
                      onChange={() => setGenerationMode('extend')}
                      className="mt-1 w-4 h-4 accent-coral-500"
                    />
                    <div>
                      <span className="text-sm font-medium text-void-100 group-hover:text-coral-400 transition-colors">
                        ➕ Extend API
                      </span>
                      <p className="text-xs text-void-400 mt-0.5">
                        Use KIE's native extend API for video continuation.
                        <br />
                        <span className="text-void-500">Faster but may have less visual consistency.</span>
                      </p>
                    </div>
                  </label>
                </div>
              </div>
            )}

            {/* Generation Preview */}
            {estimatedSegments > 0 && (
              <motion.div 
                initial={{ opacity: 0, scale: 0.95 }}
                animate={{ opacity: 1, scale: 1 }}
                className={`card border-2 ${provider === 'kling' ? 'border-coral-500/30 bg-coral-500/5' : 'border-electric-500/30 bg-electric-500/5'}`}
              >
                <h4 className={`font-bold text-sm mb-3 ${provider === 'kling' ? 'text-coral-400' : 'text-electric-400'}`}>
                  AI Analysis Preview
                </h4>
                <div className="grid grid-cols-2 gap-3 text-sm">
                  <div className="flex justify-between">
                    <span className="text-void-400">Words:</span>
                    <span className="text-void-200 font-medium">{wordCount}</span>
                  </div>
                  <div className="flex justify-between">
                    <span className="text-void-400">Segments:</span>
                    <span className="text-void-200 font-medium">~{estimatedSegments}</span>
                  </div>
                  <div className="flex justify-between">
                    <span className="text-void-400">Duration:</span>
                    <span className="text-void-200 font-medium">~{estimatedSegments * 8}s</span>
                  </div>
                  <div className="flex justify-between">
                    <span className="text-void-400">Mode:</span>
                    <span className="text-void-200 font-medium">
                      {generationMode === 'frame-continuity' ? 'Frame' : 'Extend'}
                    </span>
                  </div>
                </div>
              </motion.div>
            )}
          </div>

          {/* Right Column - Image & Settings */}
          <div className="space-y-6">
            {/* Image Upload - Required for Kling, Optional for Replicate */}
            <div className="card">
              <label className="block text-sm font-semibold text-void-200 mb-3">
                Character Image {provider === 'kling' && <span className="text-coral-400">*</span>}
                {provider === 'replicate' && <span className="text-void-500 text-xs ml-2">(optional, for visual continuity)</span>}
              </label>
              <div
                onDragOver={handleDragOver}
                onDragLeave={handleDragLeave}
                onDrop={handleDrop}
                className={`dropzone flex flex-col items-center justify-center text-center min-h-[200px] ${isDragging ? 'active' : ''}`}
              >
                {imagePreview ? (
                  <div className="relative">
                    <img 
                      src={imagePreview} 
                      alt="Preview" 
                      className="max-h-40 rounded-lg shadow-lg"
                    />
                    <button
                      type="button"
                      onClick={() => { setImageFile(null); setImagePreview(null); }}
                      className={`absolute -top-2 -right-2 w-6 h-6 rounded-full flex items-center justify-center text-white text-xs transition-colors ${
                        provider === 'kling' ? 'bg-coral-500 hover:bg-coral-600' : 'bg-electric-500 hover:bg-electric-600'
                      }`}
                    >
                      ×
                    </button>
                  </div>
                ) : (
                  <>
                    <ImageIcon className="text-void-500 mb-4" size={48} />
                    <p className="text-void-300 mb-2">Drag and drop your image here</p>
                    <p className="text-void-500 text-sm mb-4">or</p>
                    <label className={`cursor-pointer ${provider === 'kling' ? 'btn-secondary' : 'btn-secondary-electric'}`}>
                      <span>Browse Files</span>
                      <input
                        type="file"
                        accept="image/*"
                        className="hidden"
                        onChange={(e) => e.target.files?.[0] && handleImageUpload(e.target.files[0])}
                      />
                    </label>
                  </>
                )}
              </div>
              <p className="text-xs text-void-500 mt-2">
                {provider === 'kling' 
                  ? 'PNG, JPG up to 10MB. This image will be used as your character reference.'
                  : 'PNG, JPG up to 10MB. Optional: Provides visual continuity across segments.'
                }
              </p>
            </div>

            {/* Settings Grid */}
            <div className="card">
              <h4 className="text-sm font-semibold text-void-200 mb-4">Generation Settings</h4>
              <div className="grid grid-cols-2 gap-4">
                <div>
                  <label className="block text-xs text-void-400 mb-1.5">Voice Type</label>
                  <select
                    name="voiceType"
                    value={formState.voiceType}
                    onChange={handleChange}
                    className="select-field"
                  >
                    {voiceTypes.map(v => <option key={v} value={v}>{v}</option>)}
                  </select>
                </div>
                <div>
                  <label className="block text-xs text-void-400 mb-1.5">Energy Level</label>
                  <select
                    name="energyLevel"
                    value={formState.energyLevel}
                    onChange={handleChange}
                    className="select-field"
                  >
                    {energyLevels.map(e => <option key={e} value={e}>{e}</option>)}
                  </select>
                </div>
                <div>
                  <label className="block text-xs text-void-400 mb-1.5">Camera Style</label>
                  <select
                    name="cameraStyle"
                    value={formState.cameraStyle}
                    onChange={handleChange}
                    className="select-field"
                  >
                    {cameraStyles.map(c => <option key={c} value={c}>{c}</option>)}
                  </select>
                </div>
                <div>
                  <label className="block text-xs text-void-400 mb-1.5">Narrative</label>
                  <select
                    name="narrativeStyle"
                    value={formState.narrativeStyle}
                    onChange={handleChange}
                    className="select-field"
                  >
                    {narrativeStyles.map(n => <option key={n} value={n}>{n}</option>)}
                  </select>
                </div>
                <div>
                  <label className="block text-xs text-void-400 mb-1.5">Aspect Ratio</label>
                  <select
                    name="aspectRatio"
                    value={formState.aspectRatio}
                    onChange={handleChange}
                    className="select-field"
                  >
                    {aspectRatios.map(a => <option key={a} value={a}>{a}</option>)}
                  </select>
                </div>
                {provider === 'kling' ? (
                  <div>
                    <label className="block text-xs text-void-400 mb-1.5">Seed Value</label>
                    <input
                      type="number"
                      name="seedValue"
                      value={formState.seedValue}
                      onChange={handleChange}
                      className="input-field"
                      placeholder="12005"
                    />
                  </div>
                ) : (
                  <div>
                    <label className="block text-xs text-void-400 mb-1.5">Model</label>
                    <select
                      name="model"
                      value={formState.model}
                      onChange={handleChange}
                      className="select-field"
                    >
                      <option value="google/veo-3">Google Veo 3</option>
                    </select>
                  </div>
                )}
              </div>
              <p className="text-xs text-void-500 mt-3">
                {provider === 'kling' 
                  ? 'Seed 12005 = Warm, flattering lighting. Use same seed for consistency.'
                  : 'Google Veo 3 recommended for best quality and consistency.'
                }
              </p>
            </div>

            {/* Submit Button */}
            <button
              type="submit"
              disabled={!isValid || isGenerating}
              className={`
                w-full py-4 font-semibold rounded-xl transition-all duration-300
                flex items-center justify-center gap-3
                ${provider === 'kling' ? 'btn-primary' : 'btn-electric'}
                disabled:opacity-50 disabled:cursor-not-allowed disabled:scale-100
              `}
            >
              {isGenerating ? (
                <>
                  <div className="w-5 h-5 border-2 border-white/30 border-t-white rounded-full animate-spin" />
                  <span>Generating...</span>
                </>
              ) : (
                <>
                  <SparklesIcon size={20} />
                  <span>
                    Generate Video 
                    {provider === 'kling' && generationMode === 'frame-continuity' && ' (Frame Continuity)'}
                    {provider === 'kling' && generationMode === 'extend' && ' (Extend API)'}
                  </span>
                </>
              )}
            </button>
          </div>
        </div>
      </form>
    </motion.div>
  );
};
