import { useState, useCallback, useRef } from 'react';

export const useOfflineVideoExport = () => {
  const [state, setState] = useState({
    isExporting: false,
    progress: 0,
    error: null,
    stage: 'idle' as 'idle' | 'preparing' | 'rendering' | 'encoding' | 'complete',
    fps: 0,
  });

  const cancelledRef = useRef(false);

  const downloadBlob = useCallback((blob: Blob, filename: string) => {
    const url = URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.href = url;
    a.download = filename;
    document.body.appendChild(a);
    a.click();
    document.body.removeChild(a);
    URL.revokeObjectURL(url);
  }, []);

  const cancelExport = useCallback(() => {
    console.log('Cancel export requested');
    cancelledRef.current = true;
    setState(prev => ({ ...prev, error: 'Cancelling...' }));
  }, []);

  const generateVideoWithAudio = useCallback(async (
    audioFile: File,
    drawFrame: (ctx: CanvasRenderingContext2D, width: number, height: number, leftData: Uint8Array, rightData: Uint8Array) => void,
    options: { fps: number; format: 'webm' | 'mp4'; width: number; height: number; quality?: 'low' | 'medium' | 'high'; }
  ): Promise<Blob | null> => {
    console.log('🚀 Starting video export with options:', options);
    cancelledRef.current = false;
    setState({ isExporting: true, progress: 0, error: null, stage: 'preparing', fps: 0 });

    try {
      const { fps, width, height, quality = 'medium' } = options;

      // Quality settings
      const qualitySettings = {
        low: { bitrateMultiplier: 0.5, samplesPerFrame: 1024 },
        medium: { bitrateMultiplier: 1.0, samplesPerFrame: 2048 },
        high: { bitrateMultiplier: 1.5, samplesPerFrame: 4096 }
      };

      const qualityConfig = qualitySettings[quality];

      // Create canvas for rendering
      const canvas = document.createElement('canvas');
      canvas.width = width;
      canvas.height = height;
      const ctx = canvas.getContext('2d');

      if (!ctx) {
        throw new Error('Canvas not supported');
      }

      setState(prev => ({ ...prev, stage: 'rendering', progress: 5 }));

      // Load intro video - always use webm
      console.log('📹 Loading intro video...');
      const introVideo = document.createElement('video');
      introVideo.muted = true;
      introVideo.playsInline = true;
      introVideo.preload = 'auto';
      introVideo.src = '/intro.webm';
      
      let introDuration = 0;
      
      // Wait for video to be fully loaded
      await new Promise<void>((resolve) => {
        introVideo.onloadeddata = () => {
          introDuration = introVideo.duration;
          console.log(`✅ Intro video loaded: ${introDuration.toFixed(2)}s, ${introVideo.videoWidth}x${introVideo.videoHeight}`);
          resolve();
        };
        introVideo.onerror = (e) => {
          console.error('❌ Failed to load intro video:', e);
          resolve();
        };
        introVideo.load();
      });

      setState(prev => ({ ...prev, progress: 10 }));

      // Get supported codecs
      const codecs = [
        'video/webm;codecs=vp9',
        'video/webm;codecs=vp8',
        'video/mp4;codecs=h264',
        'video/mp4',
        'video/webm'
      ];

      let selectedCodec = null;
      let videoBitsPerSecond = 2000000; // Default 2Mbps

      for (const codec of codecs) {
        if (MediaRecorder.isTypeSupported(codec)) {
          selectedCodec = codec;
          console.log(`✅ Using codec: ${codec}`);

          // Adjust bitrate based on codec and quality setting
          if (codec.includes('vp9')) {
            videoBitsPerSecond = Math.floor(3000000 * qualityConfig.bitrateMultiplier);
          } else if (codec.includes('h264')) {
            videoBitsPerSecond = Math.floor(4000000 * qualityConfig.bitrateMultiplier);
          } else if (codec.includes('vp8')) {
            videoBitsPerSecond = Math.floor(2000000 * qualityConfig.bitrateMultiplier);
          }

          break;
        }
      }

      if (!selectedCodec) {
        throw new Error('No video codec supported');
      }

      // Use real audio data if available, otherwise generate mock data
      let audioBuffer: AudioBuffer;
      let sampleRate: number;
      let totalSamples: number;
      let duration: number;

      try {
        // Try to decode the actual uploaded audio file
        const arrayBuffer = await audioFile.arrayBuffer();
        const audioContext = new AudioContext();
        audioBuffer = await audioContext.decodeAudioData(arrayBuffer);
        sampleRate = audioBuffer.sampleRate;
        totalSamples = audioBuffer.length;
        duration = totalSamples / sampleRate;
        console.log(`✅ Using real audio: ${duration.toFixed(1)}s, ${totalSamples} samples`);
      } catch (audioError) {
        console.warn('⚠️ Could not decode audio file, using mock data:', audioError);
        // Generate mock audio data
        duration = 5.0; // 5 seconds
        sampleRate = 44100;
        totalSamples = Math.floor(duration * sampleRate);

        // Create a proper AudioBuffer for mock data
        const mockAudioContext = new AudioContext();
        audioBuffer = mockAudioContext.createBuffer(2, totalSamples, sampleRate);

        // Fill with sine wave
        const leftChannel = audioBuffer.getChannelData(0);
        const rightChannel = audioBuffer.getChannelData(1);

        for (let i = 0; i < totalSamples; i++) {
          const time = i / sampleRate;
          const frequency = 440; // A4 note
          const value = Math.sin(2 * Math.PI * frequency * time) * 0.5;
          leftChannel[i] = value;
          rightChannel[i] = value;
        }
        console.log(`📊 Using mock audio: ${duration.toFixed(1)}s, ${totalSamples} samples`);
      }

      // Create audio context for recording
      const recordingAudioContext = new AudioContext();

      // Resume audio context if suspended
      if (recordingAudioContext.state === 'suspended') {
        await recordingAudioContext.resume();
      }

      // Create audio source and destination
      const recordingAudioSource = recordingAudioContext.createBufferSource();
      recordingAudioSource.buffer = audioBuffer;
      recordingAudioSource.loop = false;

      const audioDestination = recordingAudioContext.createMediaStreamDestination();
      recordingAudioSource.connect(audioDestination);
      recordingAudioSource.connect(recordingAudioContext.destination);

      // Combine video and audio streams
      const combinedStream = new MediaStream();
      canvas.captureStream(fps).getVideoTracks().forEach(track => combinedStream.addTrack(track));
      audioDestination.stream.getAudioTracks().forEach(track => combinedStream.addTrack(track));

      console.log(`✅ Combined stream: ${combinedStream.getVideoTracks().length} video, ${combinedStream.getAudioTracks().length} audio tracks`);

      // Chunks array to collect recorded data
      const chunks: Blob[] = [];

      const recorder = new MediaRecorder(combinedStream, {
        mimeType: selectedCodec,
        videoBitsPerSecond: videoBitsPerSecond,
      });

      recorder.ondataavailable = (e) => {
        if (e.data.size > 0) {
          chunks.push(e.data);
        }
      };

      console.log('✅ MediaRecorder created with audio and video');
      recorder.start(1000); // 1 second chunks

      // Calculate total frames including intro
      const introFrames = introDuration > 0 ? Math.ceil(introDuration * fps) : 0;
      const mainFrames = Math.ceil(duration * fps);
      const fadeFrames = Math.ceil(fps * 0.5); // 0.5 second fade
      const totalFrames = introFrames + mainFrames;
      const samplesPerFrame = Math.min(qualityConfig.samplesPerFrame, Math.floor(totalSamples / mainFrames));

      console.log(`🎬 Total frames: ${totalFrames} (intro: ${introFrames}, main: ${mainFrames}, fade: ${fadeFrames})`);

      // Render intro frames first
      if (introFrames > 0) {
        console.log('📹 Rendering intro frames...');
        
        for (let frameIndex = 0; frameIndex < introFrames; frameIndex++) {
          if (cancelledRef.current) {
            recorder.stop();
            setState({ isExporting: false, progress: 0, error: 'Cancelled', stage: 'idle', fps: 0 });
            return null;
          }

          // Seek to correct time and wait for frame
          const targetTime = frameIndex / fps;
          introVideo.currentTime = targetTime;
          
          // Wait for the seek to complete
          await new Promise<void>((resolve) => {
            const onSeeked = () => {
              introVideo.removeEventListener('seeked', onSeeked);
              resolve();
            };
            introVideo.addEventListener('seeked', onSeeked);
            // Fallback timeout
            setTimeout(resolve, 50);
          });
          
          // Draw intro video frame scaled to canvas
          ctx.fillStyle = '#0a0f0a';
          ctx.fillRect(0, 0, width, height);
          
          // Calculate aspect-ratio-correct scaling
          const videoAspect = introVideo.videoWidth / introVideo.videoHeight;
          const canvasAspect = width / height;
          let drawWidth = width;
          let drawHeight = height;
          let drawX = 0;
          let drawY = 0;
          
          if (videoAspect > canvasAspect) {
            drawHeight = width / videoAspect;
            drawY = (height - drawHeight) / 2;
          } else {
            drawWidth = height * videoAspect;
            drawX = (width - drawWidth) / 2;
          }
          
          ctx.drawImage(introVideo, drawX, drawY, drawWidth, drawHeight);

          const progress = 10 + Math.round((frameIndex / introFrames) * 20);
          setState(prev => ({ ...prev, progress }));

          await new Promise(resolve => setTimeout(resolve, 1000 / fps));
        }
        
        console.log('✅ Intro frames complete');
      }

      // Start audio playback for main content
      recordingAudioSource.start(0);
      console.log('🔊 Audio playback started for recording');

      // Render main oscilloscope frames with fade-in from intro
      for (let frameIndex = 0; frameIndex < mainFrames; frameIndex++) {
        if (cancelledRef.current) {
          try {
            recordingAudioSource.stop();
            recordingAudioContext.close();
          } catch (e) {}
          recorder.stop();
          setState({ isExporting: false, progress: 0, error: 'Cancelled', stage: 'idle', fps: 0 });
          return null;
        }

        // Calculate current audio position for this frame
        const currentSample = Math.min(frameIndex * samplesPerFrame, totalSamples - samplesPerFrame);

        // Get waveform data from actual audio buffer
        const leftChannel = audioBuffer.getChannelData(0);
        const rightChannel = audioBuffer.numberOfChannels > 1 ? audioBuffer.getChannelData(1) : leftChannel;

        // Create waveform data for this frame
        const leftData = new Uint8Array(samplesPerFrame);
        const rightData = new Uint8Array(samplesPerFrame);

        for (let i = 0; i < samplesPerFrame; i++) {
          const sampleIndex = currentSample + i;
          if (sampleIndex >= 0 && sampleIndex < totalSamples) {
            // Convert from -1..1 range to 0..255 range
            leftData[i] = Math.round(((leftChannel[sampleIndex] + 1) / 2) * 255);
            rightData[i] = Math.round(((rightChannel[sampleIndex] + 1) / 2) * 255);
          } else {
            leftData[i] = 128;
            rightData[i] = 128;
          }
        }

        // Clear canvas
        ctx.fillStyle = '#0a0f0a';
        ctx.fillRect(0, 0, width, height);

        // Draw oscilloscope with audio data
        try {
          drawFrame(ctx, width, height, leftData, rightData);
        } catch (drawError) {
          console.error('❌ Error in drawFrame:', drawError);
          // Fallback: simple waveform
          ctx.strokeStyle = '#00ff00';
          ctx.lineWidth = 2;
          ctx.beginPath();
          for (let x = 0; x < width; x += 4) {
            const sampleIndex = Math.floor((x / width) * samplesPerFrame);
            const value = sampleIndex < leftData.length ? leftData[sampleIndex] : 128;
            const y = height / 2 + ((value - 128) / 128) * (height / 4);
            if (x === 0) {
              ctx.moveTo(x, y);
            } else {
              ctx.lineTo(x, y);
            }
          }
          ctx.stroke();
        }

        // Apply fade-in effect from intro (first fadeFrames of main content)
        if (introDuration > 0 && frameIndex < fadeFrames) {
          const fadeProgress = frameIndex / fadeFrames;
          // Draw a semi-transparent black overlay that fades out
          ctx.fillStyle = `rgba(10, 15, 10, ${1 - fadeProgress})`;
          ctx.fillRect(0, 0, width, height);
        }

        // Add frame info
        ctx.fillStyle = '#ffffff';
        ctx.font = '16px monospace';
        ctx.fillText(`Frame ${introFrames + frameIndex + 1}/${totalFrames}`, 20, 30);
        ctx.fillText(`Time: ${(frameIndex / fps).toFixed(1)}s`, 20, 50);

        const progress = 30 + Math.round((frameIndex / mainFrames) * 60);
        setState(prev => ({ ...prev, progress }));

        if (frameIndex % Math.max(1, Math.floor(mainFrames / 10)) === 0) {
          console.log(`📸 Frame ${frameIndex + 1}/${mainFrames} (${progress}%) - Time: ${(frameIndex / fps).toFixed(1)}s`);
        }

        // Frame timing
        await new Promise(resolve => setTimeout(resolve, 1000 / fps));
      }

      setState(prev => ({ ...prev, progress: 90 }));

      console.log('⏹️ Stopping recorder...');
      recorder.stop();
      try {
        recordingAudioSource.stop();
        recordingAudioContext.close();
      } catch (e) {
        console.warn('Error stopping audio:', e);
      }

      // Wait for completion
      await new Promise<void>((resolve) => {
        const checkInterval = setInterval(() => {
          if (recorder.state === 'inactive') {
            clearInterval(checkInterval);
            resolve();
          }
        }, 100);
      });

      if (chunks.length === 0) {
        throw new Error('No video chunks recorded');
      }

      const videoBlob = new Blob(chunks, { type: selectedCodec });
      console.log(`✅ Video created: ${(videoBlob.size / 1024 / 1024).toFixed(2)} MB`);

      setState({ isExporting: false, progress: 100, error: null, stage: 'complete', fps: 0 });

      return videoBlob;
    } catch (error) {
      console.error('❌ Export failed:', error);
      setState({ isExporting: false, progress: 0, error: error.message || 'Export failed', stage: 'idle', fps: 0 });
      return null;
    }
  }, []);

  return {
    ...state,
    generateVideoWithAudio,
    cancelExport,
    downloadBlob,
  };
};