import { useRef, useState } from 'react';

import { millisecondsToMinutes } from 'date-fns';

import useHuSnackbar from '@material-hu/components/design-system/Snackbar';

import { MAX_RECORDING_DURATION } from 'src/pages/dashboard/Conversations/constants';
import { AudioStatus } from 'src/pages/dashboard/Conversations/types';
import { useLokaliseTranslation } from 'src/utils/i18n';

const TIME_SLICE = 10;
const TIMER_SYNC_BUFFER = 100;

const useAudioRecorder = () => {
  const [stateAudio, setStateAudio] = useState<AudioStatus>(AudioStatus.Idle);
  const [audioUrl, setAudioUrl] = useState<string | null>(null);

  const mediaRecorderRef = useRef<MediaRecorder | null>(null);
  const streamRef = useRef<MediaStream | null>(null);
  const audioChunks = useRef<Blob[]>([]);
  const maxDurationTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(
    null,
  );

  const { enqueueSnackbar } = useHuSnackbar();
  const { t } = useLokaliseTranslation(['calls', 'chat']);

  const startRecording = async () => {
    if (!MediaRecorder.isTypeSupported('audio/webm')) {
      enqueueSnackbar({
        title: t('ERROR_LEAVE_CALL'),
        variant: 'error',
        autoHideDuration: 3000,
      });
    }

    const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
    const mediaRecorder = new MediaRecorder(stream, {
      mimeType: 'audio/webm',
    });
    mediaRecorderRef.current = mediaRecorder;

    streamRef.current = stream;

    audioChunks.current = [];

    mediaRecorder.ondataavailable = event => {
      if (event.data.size > 0) {
        audioChunks.current.push(event.data);
      }
    };

    mediaRecorder.onstop = () => {
      const audioBlob = new Blob(audioChunks.current, {
        type: 'audio/webm',
      });
      const url = URL.createObjectURL(audioBlob);
      setAudioUrl(url);
      audioChunks.current = [];
    };

    mediaRecorder.start(TIME_SLICE);
    setStateAudio(AudioStatus.Recording);
    setAudioUrl(null);

    maxDurationTimeoutRef.current = setTimeout(() => {
      mediaRecorderRef.current?.pause();
      buildAudio();
      setStateAudio(AudioStatus.Paused);
      enqueueSnackbar({
        title: t('chat:recording.max_limit', {
          max: millisecondsToMinutes(MAX_RECORDING_DURATION),
        }),
        variant: 'info',
      });
    }, MAX_RECORDING_DURATION + TIMER_SYNC_BUFFER);
  };

  const pauseRecording = () => {
    mediaRecorderRef.current?.pause();
    buildAudio();
    setStateAudio(AudioStatus.Paused);
  };

  const resumeRecording = () => {
    mediaRecorderRef.current?.resume();
    setStateAudio(AudioStatus.Recording);
  };

  const buildAudio = () => {
    const audioBlob = new Blob(audioChunks.current, {
      type: 'audio/webm',
    });
    const url = URL.createObjectURL(audioBlob);
    setAudioUrl(url);
  };

  const stopRecording = () => {
    if (maxDurationTimeoutRef.current) {
      clearTimeout(maxDurationTimeoutRef.current);
      maxDurationTimeoutRef.current = null;
    }
    setStateAudio(AudioStatus.Idle);
    mediaRecorderRef.current?.stop();
    streamRef.current?.getTracks().forEach(track => {
      track.stop();
    });
    return new Blob(audioChunks.current, {
      type: 'audio/webm',
    });
  };

  return {
    isIdle: stateAudio === AudioStatus.Idle,
    isRecording: stateAudio === AudioStatus.Recording,
    isPaused: stateAudio === AudioStatus.Paused,
    audioUrl,
    startRecording,
    pauseRecording,
    resumeRecording,
    stopRecording,
  };
};

export default useAudioRecorder;
