'use client';

import { useCallback, useRef, useState } from 'react';
import { useRecorderStore } from '@/stores/recorder-store';
// @ts-expect-error - no types available for mic-recorder-to-mp3
import MicRecorder from 'mic-recorder-to-mp3';

interface UseAudioRecorderOptions {
  maxDuration?: number; // in seconds
  onMaxDurationReached?: () => void;
}

export function useAudioRecorder(options: UseAudioRecorderOptions = {}) {
  const { maxDuration = 120, onMaxDurationReached } = options;
  const mediaRecorderRef = useRef<any>(null); // Type for MicRecorder
  const timerRef = useRef<NodeJS.Timeout | null>(null);
  const analyserRef = useRef<AnalyserNode | null>(null);
  const animationFrameRef = useRef<number | null>(null);
  const [error, setError] = useState<string | null>(null);
  const [permissionGranted, setPermissionGranted] = useState<boolean | null>(null);

  const {
    state,
    startRecording: setRecording,
    stopRecording: setStopped,
    reset: resetStore,
    setDuration,
    setWaveformData,
  } = useRecorderStore();

  const updateWaveform = useCallback((analyser: AnalyserNode) => {
    const dataArray = new Uint8Array(analyser.frequencyBinCount);
    const update = () => {
      analyser.getByteTimeDomainData(dataArray);
      const normalized = Array.from(dataArray.slice(0, 64)).map(
        (v) => (v - 128) / 128
      );
      setWaveformData(normalized);
      animationFrameRef.current = requestAnimationFrame(update);
    };
    update();
  }, [setWaveformData]);

  const startRecording = useCallback(async () => {
    try {
      setError(null);
      
      const mediaRecorder = new MicRecorder({ bitRate: 128 });
      await mediaRecorder.start();

      setPermissionGranted(true);

      // Setup analyser for waveform using MicRecorder's internal context and source
      const audioContext = mediaRecorder.context;
      const source = mediaRecorder.microphone;
      const analyser = audioContext.createAnalyser();
      analyser.fftSize = 256;
      source.connect(analyser);
      analyserRef.current = analyser;

      mediaRecorderRef.current = mediaRecorder;
      setRecording();
      updateWaveform(analyser);

      // Duration timer
      let elapsed = 0;
      timerRef.current = setInterval(() => {
        elapsed += 1;
        setDuration(elapsed);
        if (elapsed >= maxDuration) {
          // Manually trigger stop when max duration reached
          if (mediaRecorderRef.current) {
            mediaRecorderRef.current.stop().getMp3().then(([, blob]: [Int8Array, Blob]) => {
              setStopped(blob);
              if (animationFrameRef.current) cancelAnimationFrame(animationFrameRef.current);
              if (timerRef.current) clearInterval(timerRef.current);
            });
          }
          onMaxDurationReached?.();
        }
      }, 1000);
    } catch (err) {
      setPermissionGranted(false);
      if (err instanceof DOMException && err.name === 'NotAllowedError') {
        setError('Izin mikrofon ditolak. Silakan aktifkan akses mikrofon di pengaturan browser.');
      } else {
        setError('Gagal memulai perekaman. Pastikan perangkat memiliki mikrofon.');
      }
    }
  }, [maxDuration, onMaxDurationReached, setRecording, setStopped, setDuration, updateWaveform]);

  const stopRecording = useCallback(() => {
    if (mediaRecorderRef.current) {
      mediaRecorderRef.current.stop().getMp3().then(([, blob]: [Int8Array, Blob]) => {
        setStopped(blob);
        if (animationFrameRef.current) cancelAnimationFrame(animationFrameRef.current);
        if (timerRef.current) clearInterval(timerRef.current);
      }).catch((e: any) => {
        console.error('Error stopping record:', e);
      });
      // clear reference so we don't call stop again
      mediaRecorderRef.current = null;
    }
  }, [setStopped]);

  const resetRecording = useCallback(() => {
    if (mediaRecorderRef.current) {
      mediaRecorderRef.current.stop();
      mediaRecorderRef.current = null;
    }
    if (timerRef.current) clearInterval(timerRef.current);
    if (animationFrameRef.current) cancelAnimationFrame(animationFrameRef.current);
    resetStore();
  }, [resetStore]);

  return {
    state,
    error,
    permissionGranted,
    startRecording,
    stopRecording,
    resetRecording,
  };
}
