import { ref, readonly, type Ref } from 'vue'

/**
 * Push-to-talk recorder wrapping the browser MediaRecorder API.
 *
 * Usage:
 *   const { recording, error, isSupported, startRecording, stopRecording } = useVoiceRecorder()
 *   await startRecording()
 *   const blob = await stopRecording()   // resolves with the recorded audio Blob
 */
export function useVoiceRecorder() {
  const recording = ref(false)
  const error = ref<string | null>(null)
  const isSupported = typeof MediaRecorder !== 'undefined' && !!navigator.mediaDevices?.getUserMedia

  let mediaRecorder: MediaRecorder | null = null
  let chunks: Blob[] = []
  const streamRef = ref<MediaStream | null>(null)
  let resolveStop: ((blob: Blob) => void) | null = null
  let rejectStop: ((err: Error) => void) | null = null

  async function startRecording(): Promise<void> {
    error.value = null
    if (!isSupported) {
      error.value = 'Audio recording is not supported in this browser'
      return
    }
    if (recording.value) return

    try {
      streamRef.value = await navigator.mediaDevices.getUserMedia({ audio: true })
    } catch (e) {
      error.value = 'Microphone access denied'
      return
    }

    chunks = []
    const mimeType = MediaRecorder.isTypeSupported('audio/webm;codecs=opus')
      ? 'audio/webm;codecs=opus'
      : MediaRecorder.isTypeSupported('audio/webm')
        ? 'audio/webm'
        : ''

    mediaRecorder = mimeType ? new MediaRecorder(streamRef.value!, { mimeType }) : new MediaRecorder(streamRef.value!)

    mediaRecorder.ondataavailable = (e) => {
      if (e.data.size > 0) chunks.push(e.data)
    }

    mediaRecorder.onstop = () => {
      const blob = new Blob(chunks, { type: mediaRecorder?.mimeType ?? 'audio/webm' })
      chunks = []
      streamRef.value?.getTracks().forEach((t) => t.stop())
      streamRef.value = null
      recording.value = false
      resolveStop?.(blob)
      resolveStop = null
      rejectStop = null
    }

    mediaRecorder.onerror = () => {
      recording.value = false
      error.value = 'Recording error'
      rejectStop?.(new Error('MediaRecorder error'))
      resolveStop = null
      rejectStop = null
    }

    mediaRecorder.start(100) // collect in 100ms chunks
    recording.value = true
  }

  function stopRecording(): Promise<Blob> {
    return new Promise((resolve, reject) => {
      if (!mediaRecorder || !recording.value) {
        reject(new Error('Not recording'))
        return
      }
      resolveStop = resolve
      rejectStop = reject
      mediaRecorder.stop()
    })
  }

  return {
    recording: readonly(recording),
    error: readonly(error),
    isSupported,
    startRecording,
    stopRecording,
    stream: readonly(streamRef) as Readonly<Ref<MediaStream | null>>,
  }
}
