import { t } from "i18next";
import { useEffect, useRef, useState } from "react";

import { feedbackToast } from "@/utils/common";

const MAX_DURATION = 60;

export interface RecorderResult {
  file: File;
  duration: number;
}

export function useVoiceRecorder(onFinish: (result: RecorderResult) => void) {
  const [recording, setRecording] = useState(false);
  const [seconds, setSeconds] = useState(0);
  const recorderRef = useRef<MediaRecorder>();
  const chunksRef = useRef<Blob[]>([]);
  const timerRef = useRef<NodeJS.Timeout>();
  const secondsRef = useRef(0);
  const canceledRef = useRef(false);

  const clearTimer = () => {
    if (timerRef.current) {
      clearInterval(timerRef.current);
      timerRef.current = undefined;
    }
  };

  const stopTracks = () => {
    recorderRef.current?.stream.getTracks().forEach((track) => track.stop());
  };

  useEffect(() => {
    return () => {
      clearTimer();
      if (recorderRef.current && recorderRef.current.state !== "inactive") {
        canceledRef.current = true;
        recorderRef.current.stop();
      }
      stopTracks();
    };
  }, []);

  const start = async () => {
    if (recording) return;
    try {
      const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
      const mimeType = MediaRecorder.isTypeSupported("audio/webm;codecs=opus")
        ? "audio/webm;codecs=opus"
        : "audio/webm";
      const recorder = new MediaRecorder(stream, { mimeType });
      recorderRef.current = recorder;
      chunksRef.current = [];
      canceledRef.current = false;
      secondsRef.current = 0;
      setSeconds(0);

      recorder.ondataavailable = (e) => {
        if (e.data.size > 0) chunksRef.current.push(e.data);
      };
      recorder.onstop = () => {
        stopTracks();
        clearTimer();
        setRecording(false);
        if (canceledRef.current) return;
        const duration = secondsRef.current;
        if (duration < 1) {
          feedbackToast({ msg: t("toast.recordTooShort"), error: t("toast.recordTooShort") });
          return;
        }
        const blob = new Blob(chunksRef.current, { type: mimeType });
        const file = new File([blob], `voice-${Date.now()}.webm`, {
          type: mimeType,
        });
        onFinish({ file, duration });
      };

      recorder.start();
      setRecording(true);
      timerRef.current = setInterval(() => {
        secondsRef.current += 1;
        setSeconds(secondsRef.current);
        if (secondsRef.current >= MAX_DURATION) {
          recorderRef.current?.stop();
        }
      }, 1000);
    } catch (error) {
      feedbackToast({ msg: t("toast.noMicrophone"), error });
    }
  };

  const finish = () => {
    if (recorderRef.current?.state === "recording") {
      recorderRef.current.stop();
    }
  };

  const cancel = () => {
    canceledRef.current = true;
    if (recorderRef.current?.state === "recording") {
      recorderRef.current.stop();
    }
  };

  return {
    recording,
    seconds,
    start,
    finish,
    cancel,
  };
}
