import { useState, useCallback, useEffect, useRef } from 'react';
import { AudioUploader } from './AudioUploader';
import { OscilloscopeControls, LiveDisplaySettings } from './OscilloscopeControls';
import { OscilloscopeDisplay } from './OscilloscopeDisplay';
import { useAudioAnalyzer } from '@/hooks/useAudioAnalyzer';
import { useOscilloscopeRenderer } from '@/hooks/useOscilloscopeRenderer';
import { useVideoExporter } from '@/hooks/useVideoExporter';
import { useSettings } from '@/contexts/SettingsContext';
import { Pause, Play } from 'lucide-react';
import { Button } from '@/components/ui/button';
import { Slider } from '@/components/ui/slider';

// Format time in mm:ss format
const formatTime = (seconds: number): string => {
  const mins = Math.floor(seconds / 60);
  const secs = Math.floor(seconds % 60);
  return `${mins}:${secs.toString().padStart(2, '0')}`;
};

export function Oscilloscope() {
  const [mode, setMode] = useState<'combined' | 'separate' | 'all'>('combined');
  const [liveSettings, setLiveSettings] = useState<LiveDisplaySettings>({
    lineThickness: 2,
    showGrid: true,
    glowIntensity: 1,
    displayMode: 'combined',
    visualizationMode: 'waveform',
  });
  const [isMicActive, setIsMicActive] = useState(false);
  const [isPlaying, setIsPlaying] = useState(false);
  const [currentTime, setCurrentTime] = useState(0);
  const [playbackSpeed, setPlaybackSpeed] = useState(1);
  const [isLooping, setIsLooping] = useState(false);
  const [seekPosition, setSeekPosition] = useState(0);
  const [exportResolution, setExportResolution] = useState<'640x480' | '1280x720' | '1920x1080'>('1920x1080');
  const [exportFps, setExportFps] = useState<24 | 30 | 60>(60);
  const [exportQuality, setExportQuality] = useState<'low' | 'medium' | 'high'>('medium');
  const [showMicCalibration, setShowMicCalibration] = useState(false);
  const [micLevel, setMicLevel] = useState(0);
  const [micGain, setMicGain] = useState(1);
  const [micGainNode, setMicGainNode] = useState<GainNode | null>(null);
  const isMicActiveRef = useRef(false);
  
  // Audio playback refs
  const audioRef = useRef<HTMLAudioElement | null>(null);
  const audioUrlRef = useRef<string | null>(null);

  const { audioData, isLoading, fileName, originalFile, loadAudioFile, reset: resetAudio } = useAudioAnalyzer();
  const { isExporting, progress, exportedUrl, exportVideo, reset: resetExport } = useVideoExporter();
  const { playSound } = useSettings();

  // Update mic gain when it changes
  useEffect(() => {
    if (micGainNode) {
      micGainNode.gain.value = micGain;
    }
  }, [micGain, micGainNode]);

  // Real-time microphone input
  const [micStream, setMicStream] = useState<MediaStream | null>(null);
  const [micAnalyzer, setMicAnalyzer] = useState<AnalyserNode | null>(null);
  
  // Create audio element when file is loaded
  useEffect(() => {
    if (originalFile) {
      // Clean up previous audio URL
      if (audioUrlRef.current) {
        URL.revokeObjectURL(audioUrlRef.current);
      }
      
      // Create new audio element
      const url = URL.createObjectURL(originalFile);
      audioUrlRef.current = url;
      
      const audio = new Audio(url);
      audio.loop = isLooping;
      audio.playbackRate = playbackSpeed;
      
      audio.addEventListener('timeupdate', () => {
        setCurrentTime(audio.currentTime);
        if (audioData) {
          const position = audio.currentTime / audioData.duration;
          setSeekPosition(position);
        }
      });
      
      audio.addEventListener('ended', () => {
        if (!isLooping) {
          setIsPlaying(false);
          setCurrentTime(0);
          setSeekPosition(0);
        }
      });
      
      audioRef.current = audio;
      
      return () => {
        audio.pause();
        audio.src = '';
        if (audioUrlRef.current) {
          URL.revokeObjectURL(audioUrlRef.current);
          audioUrlRef.current = null;
        }
      };
    }
  }, [originalFile, audioData]);
  
  // Update audio playback state
  useEffect(() => {
    if (audioRef.current) {
      audioRef.current.loop = isLooping;
      audioRef.current.playbackRate = playbackSpeed;
    }
  }, [isLooping, playbackSpeed]);
  
  // Handle play/pause
  useEffect(() => {
    if (audioRef.current) {
      if (isPlaying) {
        audioRef.current.play().catch(console.error);
      } else {
        audioRef.current.pause();
      }
    }
  }, [isPlaying]);

  const handleFileSelect = useCallback((file: File) => {
    loadAudioFile(file);
    if (isMicActive) {
      setIsMicActive(false);
    }
  }, [loadAudioFile, isMicActive]);

  const toggleMic = useCallback(async () => {
    playSound('click');
    if (isMicActive) {
      // Stop microphone
      isMicActiveRef.current = false;
      if (micStream) {
        micStream.getTracks().forEach(track => track.stop());
        setMicStream(null);
      }
      setMicAnalyzer(null);
      setMicGainNode(null);
      setIsMicActive(false);
      setMicLevel(0);
      setShowMicCalibration(false);
      resetAudio();
    } else {
      // Start microphone
      try {
        const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
        setMicStream(stream);

        const audioContext = new AudioContext();
        const analyser = audioContext.createAnalyser();
        analyser.fftSize = 2048;
        analyser.smoothingTimeConstant = 0.8;

        const gainNode = audioContext.createGain();
        gainNode.gain.value = micGain;
        setMicGainNode(gainNode);

        const source = audioContext.createMediaStreamSource(stream);
        source.connect(gainNode);
        gainNode.connect(analyser);

        // Start monitoring mic levels
        isMicActiveRef.current = true;
        const dataArray = new Uint8Array(analyser.frequencyBinCount);
        const monitorLevels = () => {
          if (isMicActiveRef.current && analyser) {
            analyser.getByteFrequencyData(dataArray);
            const average = dataArray.reduce((a, b) => a + b) / dataArray.length;
            setMicLevel(average / 255); // Normalize to 0-1
            requestAnimationFrame(monitorLevels);
          }
        };
        monitorLevels();

        setMicAnalyzer(analyser);
        setIsMicActive(true);
        resetAudio(); // Clear any loaded file
      } catch (error) {
        console.error('Error accessing microphone:', error);
        alert('Could not access microphone. Please check permissions.');
      }
    }
  }, [isMicActive, micStream, playSound, resetAudio, micGain]);

  const handlePreview = useCallback(() => {
    playSound('click');
    setIsPlaying(!isPlaying);
  }, [isPlaying, playSound]);

  const handleSeek = useCallback((position: number) => {
    if (audioData && audioRef.current && position >= 0) {
      const newTime = position * audioData.duration;
      audioRef.current.currentTime = newTime;
      setCurrentTime(newTime);
      setSeekPosition(position);
    }
  }, [audioData]);

  const handlePlaybackSpeedChange = useCallback((speed: number) => {
    setPlaybackSpeed(speed);
  }, []);

  const handleLoopingChange = useCallback((looping: boolean) => {
    setIsLooping(looping);
  }, []);

  const handleExportResolutionChange = useCallback((resolution: string) => {
    setExportResolution(resolution as '640x480' | '1280x720' | '1920x1080');
  }, []);

  const handleExportFpsChange = useCallback((fps: number) => {
    setExportFps(fps as 24 | 30 | 60);
  }, []);

  const handleExportQualityChange = useCallback((quality: string) => {
    setExportQuality(quality as 'low' | 'medium' | 'high');
  }, []);

  const handleGenerate = useCallback(async () => {
    if (!audioData || !originalFile) return;

    const [width, height] = exportResolution.split('x').map(Number);

    await exportVideo(audioData, originalFile, {
      width,
      height,
      fps: exportFps,
      mode,
      audioFile: originalFile,
      quality: exportQuality,
    });
  }, [audioData, originalFile, exportVideo, mode, exportResolution, exportFps, exportQuality]);

  const handleReset = useCallback(() => {
    playSound('click');
    setIsPlaying(false);
    resetAudio();
    resetExport();
  }, [playSound, resetAudio, resetExport]);

  const canPreview = (audioData !== null || isMicActive) && !isExporting;
  const canGenerate = audioData !== null && originalFile !== null && !isExporting && !isLoading;

  return (
    <div className="space-y-6 max-w-7xl mx-auto">
      {/* Header */}
      <div className="text-center space-y-4">
        <h2 className="font-minecraft text-2xl md:text-3xl text-primary text-glow-strong">
          Audio Oscilloscope
        </h2>
        <p className="font-pixel text-foreground/80">
          Visualize audio waveforms in real-time
        </p>
      </div>

      {/* Main Content: Display + Controls Side by Side */}
      <div className="grid grid-cols-1 xl:grid-cols-[1fr_320px] gap-6">
        {/* Oscilloscope Display */}
        <div className="flex flex-col items-center gap-4 order-2 xl:order-1">
          <OscilloscopeDisplay
            audioData={audioData}
            micAnalyzer={micAnalyzer}
            mode={liveSettings.displayMode}
            isPlaying={isPlaying}
            playbackSpeed={playbackSpeed}
            isLooping={isLooping}
            audioElementRef={audioRef}
            onPlaybackEnd={() => {
              setIsPlaying(false);
              setCurrentTime(0);
              setSeekPosition(0);
            }}
            onSeek={handleSeek}
            liveSettings={liveSettings}
          />
          {/* Audio Uploader - below controls on desktop */}
          <div className="w-full mt-4">
            <AudioUploader
              onFileSelect={handleFileSelect}
              isLoading={isLoading}
              fileName={fileName}
            />
          </div>
 
          {/* Audio Playback Controls */}
          {audioData && originalFile && (
            <div className="w-full max-w-3xl space-y-2 px-4">
              {/* Play/Pause and Time Display */}
              <div className="flex items-center gap-4">
                <Button
                  onClick={handlePreview}
                  variant="outline"
                  size="sm"
                  className="font-crt border-primary/50 hover:bg-primary/10"
                  disabled={isExporting}
                >
                  {isPlaying ? <Pause size={16} /> : <Play size={16} />}
                </Button>
                
                <span className="font-mono-crt text-sm text-foreground/80 min-w-[80px]">
                  {formatTime(currentTime)} / {formatTime(audioData.duration)}
                </span>
                
                {/* Progress Bar */}
                <div className="flex-1">
                  <Slider
                    value={[seekPosition * 100]}
                    onValueChange={(value) => handleSeek(value[0] / 100)}
                    max={100}
                    step={0.1}
                    className="cursor-pointer"
                  />
                </div>
              </div>
            </div>
          )}
        </div>

        {/* Control Panel */}
        <div className="order-1 xl:order-2">
          <OscilloscopeControls
            mode={mode}
            onModeChange={setMode}
            canGenerate={canGenerate}
            isGenerating={isExporting}
            progress={progress}
            exportedUrl={exportedUrl}
            onGenerate={handleGenerate}
            onReset={handleReset}
            isPlaying={isPlaying}
            onPreview={handlePreview}
            canPreview={canPreview}
            playbackSpeed={playbackSpeed}
            onPlaybackSpeedChange={handlePlaybackSpeedChange}
            isLooping={isLooping}
            onLoopingChange={handleLoopingChange}
            exportResolution={exportResolution}
            onExportResolutionChange={handleExportResolutionChange}
            exportFps={exportFps}
            onExportFpsChange={handleExportFpsChange}
            exportQuality={exportQuality}
            onExportQualityChange={handleExportQualityChange}
            liveSettings={liveSettings}
            onLiveSettingsChange={setLiveSettings}
            isMicActive={isMicActive}
            onToggleMic={toggleMic}
          />
          
        </div>
      </div>

      {/* Microphone Calibration */}
      {showMicCalibration && isMicActive && (
        <div className="bg-card border border-border rounded-lg p-4 max-w-md mx-auto">
          <h3 className="font-crt text-lg text-primary text-glow mb-4">Microphone Calibration</h3>

          <div className="space-y-4">
            {/* Level Indicator */}
            <div>
              <div className="flex justify-between text-sm font-mono-crt mb-2">
                <span>Input Level</span>
                <span>{Math.round(micLevel * 100)}%</span>
              </div>
              <div className="w-full bg-secondary rounded-full h-3">
                <div
                  className="bg-primary h-3 rounded-full transition-all duration-100"
                  style={{ width: `${micLevel * 100}%` }}
                />
              </div>
              <div className="flex justify-between text-xs text-muted-foreground mt-1">
                <span>Quiet</span>
                <span className={micLevel > 0.7 ? 'text-red-400' : micLevel > 0.5 ? 'text-yellow-400' : 'text-green-400'}>
                  {micLevel > 0.7 ? 'Too Loud' : micLevel > 0.5 ? 'Good' : 'Too Quiet'}
                </span>
                <span>Loud</span>
              </div>
            </div>

            {/* Gain Control */}
            <div>
              <div className="flex justify-between text-sm font-mono-crt mb-2">
                <span>Gain</span>
                <span>{micGain.toFixed(1)}x</span>
              </div>
              <input
                type="range"
                min="0.1"
                max="3"
                step="0.1"
                value={micGain}
                onChange={(e) => setMicGain(Number(e.target.value))}
                className="w-full"
              />
            </div>

            <p className="text-xs text-muted-foreground font-mono-crt">
              Speak into your microphone and adjust gain until the level shows "Good" (green).
              The bar should peak around 50-70% when speaking normally.
            </p>
          </div>
        </div>
      )}
      {showMicCalibration && isMicActive && (
        <div className="bg-card border border-border rounded-lg p-4 max-w-md mx-auto">
          <h3 className="font-crt text-lg text-primary text-glow mb-4">Microphone Calibration</h3>

          <div className="space-y-4">
            {/* Level Indicator */}
            <div>
              <div className="flex justify-between text-sm font-mono-crt mb-2">
                <span>Input Level</span>
                <span>{Math.round(micLevel * 100)}%</span>
              </div>
              <div className="w-full bg-secondary rounded-full h-3">
                <div
                  className="bg-primary h-3 rounded-full transition-all duration-100"
                  style={{ width: `${micLevel * 100}%` }}
                />
              </div>
              <div className="flex justify-between text-xs text-muted-foreground mt-1">
                <span>Quiet</span>
                <span className={micLevel > 0.7 ? 'text-red-400' : micLevel > 0.5 ? 'text-yellow-400' : 'text-green-400'}>
                  {micLevel > 0.7 ? 'Too Loud' : micLevel > 0.5 ? 'Good' : 'Too Quiet'}
                </span>
                <span>Loud</span>
              </div>
            </div>

            {/* Gain Control */}
            <div>
              <div className="flex justify-between text-sm font-mono-crt mb-2">
                <span>Gain</span>
                <span>{micGain.toFixed(1)}x</span>
              </div>
              <input
                type="range"
                min="0.1"
                max="3"
                step="0.1"
                value={micGain}
                onChange={(e) => setMicGain(Number(e.target.value))}
                className="w-full"
              />
            </div>

            <p className="text-xs text-muted-foreground font-mono-crt">
              Speak into your microphone and adjust gain until the level shows "Good" (green).
              The bar should peak around 50-70% when speaking normally.
            </p>
          </div>
        </div>
      )}

      {/* Status Info */}
      {(isLoading || isExporting) && (
        <div className="text-center text-muted-foreground font-mono-crt text-sm">
          {isLoading && "Loading audio..."}
          {isExporting && `Exporting video... ${progress}%`}
        </div>
      )}
    </div>
  );
}
