import { useState } from 'react';
import styles from './styles.module.css';
import toast from 'react-hot-toast';
import RecorderControl from './record-controller';

interface VoiceRecorder {
  setInputMsg: (msg: string) => void;
  tapToSpeak: boolean;
  includeDiv?: boolean; 
  showVoiceRecorder?: boolean;
  delayBetweenDialogs?: number;
  handleVoiceRecorder?: () => void;
  styles?: object;
  primaryColor: string;
  primaryDarkColor: string;
}

const VoiceRecorder: React.FC<VoiceRecorder> = ({
  setInputMsg,
  tapToSpeak,
  includeDiv = false,
  showVoiceRecorder = true,
  delayBetweenDialogs = 3000,
  handleVoiceRecorder,
  styles: customStyles,
  primaryColor,
  primaryDarkColor,
}) => {
  const [mediaRecorder, setMediaRecorder] = useState<MediaRecorder | null>(null);
  const [isErrorClicked, setIsErrorClicked] = useState(false);
  const [recorderStatus, setRecorderStatus] = useState('idle');
  const [isRecording, setIsRecording] = useState(false);

  const voiceMinDecibels: number = -50;
  const dialogMaxLength: number = 60000; // 1 minute

  const startRecording = () => {
    if (!isRecording) {
      setIsRecording(true);
      record();
      if (handleVoiceRecorder) handleVoiceRecorder();
    }
  };

  const stopRecording = () => {
    if (isRecording) {
      if (mediaRecorder !== null) {
        mediaRecorder.stop();
        setIsRecording(false);
        setMediaRecorder(null);
      }
    }
  };

  function record() {
    navigator.mediaDevices.getUserMedia({ audio: true }).then((stream) => {
      const recorder = new MediaRecorder(stream);
      recorder.start();
      setMediaRecorder(recorder);

      const audioChunks: BlobPart[] = [];
      recorder.addEventListener('dataavailable', (event) => {
        audioChunks.push(event.data);
      });

      const audioContext = new AudioContext();
      const audioStreamSource = audioContext.createMediaStreamSource(stream);
      const analyser = audioContext.createAnalyser();
      analyser.minDecibels = voiceMinDecibels;
      audioStreamSource.connect(analyser);
      const bufferLength = analyser.frequencyBinCount;
      const domainData = new Uint8Array(bufferLength);

      let time: Date = new Date();
      let startTime: number = time.getTime();
      let lastDetectedTime: number = time.getTime();
      let anySoundDetected: boolean = false;

      const detectSound = () => {
        if (!isRecording) return;

        time = new Date();
        const currentTime = time.getTime();

        if (currentTime > startTime + dialogMaxLength) {
          recorder.stop();
          return;
        }

        if (anySoundDetected && currentTime > lastDetectedTime + delayBetweenDialogs) {
          recorder.stop();
          return;
        }

        analyser.getByteFrequencyData(domainData);
        for (let i = 0; i < bufferLength; i++) {
          if (domainData[i] > 0) {
            anySoundDetected = true;
            lastDetectedTime = currentTime;
          }
        }

        window.requestAnimationFrame(detectSound);
      };
      window.requestAnimationFrame(detectSound);

      recorder.addEventListener('stop', () => {
        stream.getTracks().forEach((track) => track.stop());
        if (!anySoundDetected) return;

        const audioBlob = new Blob(audioChunks, { type: 'audio/mp3' });
        makeComputeAPICall(audioBlob);
      });
    });
  }

  const makeComputeAPICall = async (blob: Blob) => {
    try {
      setRecorderStatus('processing');
      toast.success('Processing your recording...');
      if (blob) {
        // Simulate API call result
        setTimeout(() => {
          setInputMsg('Processed message');
          setRecorderStatus('idle');
        }, 2000);
      }
    } catch (error) {
      console.error(error);
      setRecorderStatus('error');
      toast.error('An error occurred while processing your recording.');
      setIsErrorClicked(false);
      setTimeout(() => {
        if (!isErrorClicked) {
          setRecorderStatus('idle');
        }
      }, 2500);
    }
  };

  if (!showVoiceRecorder) return null;

  return (
    <div>
      <div>
        {mediaRecorder && mediaRecorder.state === 'recording' ? (
          <div className={styles.center}>
            <RecorderControl
              status="recording"
              onClick={stopRecording}
              includeDiv={includeDiv}
              primaryColor={primaryColor}
              primaryDarkColor={primaryDarkColor}
            />
          </div>
        ) : (
          <div className={styles.center}>
            {recorderStatus === 'processing' ? (
              <div>
                <RecorderControl
                  status="processing"
                  onClick={() => {}}
                  primaryColor={primaryColor}
                  primaryDarkColor={primaryDarkColor}
                />
              </div>
            ) : recorderStatus === 'error' ? (
              <div>
                <RecorderControl
                  status="error"
                  onClick={() => {
                    setIsErrorClicked(true);
                    startRecording();
                  }}
                  includeDiv={includeDiv}
                  primaryColor={primaryColor}
                  primaryDarkColor={primaryDarkColor}
                />
              </div>
            ) : (
              <div>
                <RecorderControl
                  status="start"
                  onClick={() => {
                    setIsErrorClicked(true);
                    startRecording();
                  }}
                  includeDiv={includeDiv}
                  tapToSpeak={tapToSpeak}
                  primaryColor={primaryColor}
                  primaryDarkColor={primaryDarkColor}
                />
              </div>
            )}
          </div>
        )}
      </div>
    </div>
  );
};

export default VoiceRecorder;
