import { ref, onUnmounted } from 'vue'

/**
 * 诊断日志说明：本文件的 `console.debug('[recorder] ...')` 用于排查"录不到声音"类问题
 * （mic track 状态 / AudioContext 状态 / 每 50 个 chunk 的 Float32 峰值 / 结束时整体统计）。
 * 默认在 Chrome DevTools 不显示——浏览器 Console 的 log level filter 切到 "Verbose"
 * （或 "All levels"）即可看到。Safari / Firefox 同理。
 */

export function useAudioRecorder() {
  const isRecording = ref(false)
  const permissionState = ref<PermissionState>('prompt')
  const recordingDuration = ref(0)
  const silenceDetected = ref(false)
  /** 实际硬件采样率，startRecording 后可读；用于 WebSocket 上报给后端 */
  const sampleRate = ref<number>(0)
  /** 每收到一块 PCM chunk 时触发（外部可订阅做流式上传） */
  const onChunk = ref<((pcm16: ArrayBuffer) => void) | null>(null)

  let audioContext: AudioContext | null = null
  let mediaStream: MediaStream | null = null
  let workletNode: AudioWorkletNode | null = null
  let pcmChunks: Float32Array[] = []
  let durationTimer: ReturnType<typeof setInterval> | null = null
  let silenceCheckTimer: ReturnType<typeof setInterval> | null = null
  let analyser: AnalyserNode | null = null

  // [DEBUG] 跨 start/stop 共享的录音诊断计数器
  let debugChunkCount = 0
  let debugPeakFloat = 0

  // Check permission state
  async function checkPermission() {
    try {
      const status = await navigator.permissions.query({ name: 'microphone' as PermissionName })
      permissionState.value = status.state
      status.onchange = () => { permissionState.value = status.state }
    } catch {
      // permissions API may not support microphone query in all browsers
    }
  }

  // Silence detection using AnalyserNode
  function startSilenceDetection(source: MediaStreamAudioSourceNode) {
    analyser = audioContext!.createAnalyser()
    analyser.fftSize = 512
    source.connect(analyser)

    const dataArray = new Uint8Array(analyser.frequencyBinCount)
    let silentFrames = 0
    const SILENCE_THRESHOLD = 10
    const FRAMES_FOR_5S = Math.ceil(5000 / 200)

    silenceCheckTimer = setInterval(() => {
      if (!analyser) return
      analyser.getByteFrequencyData(dataArray)
      const average = dataArray.reduce((sum, v) => sum + v, 0) / dataArray.length

      if (average < SILENCE_THRESHOLD) {
        silentFrames++
        if (silentFrames >= FRAMES_FOR_5S) {
          silenceDetected.value = true
        }
      } else {
        silentFrames = 0
        silenceDetected.value = false
      }
    }, 200)
  }

  function stopSilenceDetection() {
    silenceDetected.value = false
    if (silenceCheckTimer) { clearInterval(silenceCheckTimer); silenceCheckTimer = null }
    analyser = null
  }

  async function startRecording(): Promise<void> {
    pcmChunks = []
    silenceDetected.value = false

    // 获取麦克风
    try {
      mediaStream = await navigator.mediaDevices.getUserMedia({ audio: true })
      permissionState.value = 'granted'
    } catch (err: any) {
      if (err.name === 'NotAllowedError') {
        permissionState.value = 'denied'
      }
      throw err
    }

    // 创建 AudioContext（不指定 sampleRate，用硬件默认 — iOS Safari 不支持自定义采样率）
    audioContext = new AudioContext()
    sampleRate.value = audioContext.sampleRate
    const source = audioContext.createMediaStreamSource(mediaStream)

    // 诊断录音管线是否健康：mic track 状态 + AudioContext 状态
    console.debug('[recorder] startRecording', {
      contextState: audioContext.state,
      contextSampleRate: audioContext.sampleRate,
      tracks: mediaStream.getAudioTracks().map(t => ({
        label: t.label,
        enabled: t.enabled,
        muted: t.muted,
        readyState: t.readyState,
        settings: t.getSettings?.(),
      })),
    })

    // 有些浏览器 AudioContext 默认 suspended，必须 resume 才会 tick worklet
    if (audioContext.state === 'suspended') {
      await audioContext.resume()
      console.debug('[recorder] AudioContext resumed →', audioContext.state)
    }

    // 加载 AudioWorklet processor
    await audioContext.audioWorklet.addModule('/pcm-recorder-worklet.js')
    workletNode = new AudioWorkletNode(audioContext, 'pcm-recorder-processor')

    // 重置诊断计数器（很便宜，一直追踪；只有打印受 DevTools Verbose filter 控制）
    debugChunkCount = 0
    debugPeakFloat = 0

    // 收集 PCM 数据；同时（可选）通过 onChunk 把 int16 字节推给外部（WebSocket 流式上传）
    workletNode.port.onmessage = (e: MessageEvent<Float32Array>) => {
      pcmChunks.push(e.data)
      debugChunkCount++
      for (let i = 0; i < e.data.length; i++) {
        const a = Math.abs(e.data[i])
        if (a > debugPeakFloat) debugPeakFloat = a
      }
      // 每 50 个 chunk 打一次（≈ 128*50/48000 ≈ 133ms 一条，避免刷屏）
      if (debugChunkCount % 50 === 0) {
        console.debug(`[recorder] chunks=${debugChunkCount} peak_float=${debugPeakFloat.toFixed(4)}`)
      }
      const cb = onChunk.value
      if (cb) {
        const int16 = float32ToInt16(e.data)
        cb(int16.buffer as ArrayBuffer)
      }
    }

    source.connect(workletNode)
    workletNode.connect(audioContext.destination) // 需要连接才能驱动处理

    // 静音检测
    startSilenceDetection(source)

    isRecording.value = true
    recordingDuration.value = 0
    durationTimer = setInterval(() => { recordingDuration.value++ }, 1000)
  }

  async function stopRecording(): Promise<Blob> {
    if (!workletNode || !audioContext) {
      throw new Error('No active recording')
    }

    // 通知 worklet 停止
    workletNode.port.postMessage('stop')
    workletNode.disconnect()
    workletNode = null

    // 合并 PCM chunks 并按实际硬件采样率编码 WAV（后端按 WAV header 解析）
    const sampleRate = audioContext.sampleRate

    // 合并前打一条"整段录音的最终统计"—— peak_float ≈ 0 就是录音管线产零
    let totalSamples = 0
    for (const c of pcmChunks) totalSamples += c.length
    console.debug('[recorder] stopRecording final', {
      chunks: debugChunkCount,
      totalSamples,
      duration: (totalSamples / sampleRate).toFixed(2) + 's',
      peakFloat: debugPeakFloat.toFixed(6),
      contextState: audioContext.state,
      trackMuted: mediaStream?.getAudioTracks()[0]?.muted,
      trackEnabled: mediaStream?.getAudioTracks()[0]?.enabled,
    })

    const wavBlob = encodeWAV(pcmChunks, sampleRate)
    pcmChunks = []

    cleanup()
    return wavBlob
  }

  function cleanup() {
    isRecording.value = false
    recordingDuration.value = 0

    if (durationTimer) { clearInterval(durationTimer); durationTimer = null }
    stopSilenceDetection()

    if (audioContext) { audioContext.close().catch(() => {}); audioContext = null }
    if (mediaStream) {
      mediaStream.getTracks().forEach(t => t.stop())
      mediaStream = null
    }
  }

  checkPermission()

  onUnmounted(() => {
    cleanup()
  })

  return {
    isRecording,
    permissionState,
    recordingDuration,
    silenceDetected,
    sampleRate,
    onChunk,
    startRecording,
    stopRecording,
    cleanup,
  }
}

// ==================== Helpers ====================

function float32ToInt16(float32: Float32Array): Int16Array {
  const int16 = new Int16Array(float32.length)
  for (let i = 0; i < float32.length; i++) {
    const s = Math.max(-1, Math.min(1, float32[i]))
    int16[i] = s < 0 ? s * 0x8000 : s * 0x7FFF
  }
  return int16
}

// ==================== WAV 编码 ====================

function encodeWAV(chunks: Float32Array[], sampleRate: number): Blob {
  // 计算总长度
  let totalLength = 0
  for (const chunk of chunks) totalLength += chunk.length

  // 合并为单个 Float32Array
  const pcm = new Float32Array(totalLength)
  let offset = 0
  for (const chunk of chunks) {
    pcm.set(chunk, offset)
    offset += chunk.length
  }

  // Float32 → Int16
  const int16 = float32ToInt16(pcm)

  // 写 WAV header + data
  const numChannels = 1
  const bitsPerSample = 16
  const byteRate = sampleRate * numChannels * (bitsPerSample / 8)
  const blockAlign = numChannels * (bitsPerSample / 8)
  const dataSize = int16.length * (bitsPerSample / 8)

  const buffer = new ArrayBuffer(44 + dataSize)
  const view = new DataView(buffer)

  // RIFF header
  writeString(view, 0, 'RIFF')
  view.setUint32(4, 36 + dataSize, true)
  writeString(view, 8, 'WAVE')

  // fmt chunk
  writeString(view, 12, 'fmt ')
  view.setUint32(16, 16, true)           // chunk size
  view.setUint16(20, 1, true)            // PCM format
  view.setUint16(22, numChannels, true)
  view.setUint32(24, sampleRate, true)
  view.setUint32(28, byteRate, true)
  view.setUint16(32, blockAlign, true)
  view.setUint16(34, bitsPerSample, true)

  // data chunk
  writeString(view, 36, 'data')
  view.setUint32(40, dataSize, true)

  // PCM data
  const int16View = new Int16Array(buffer, 44)
  int16View.set(int16)

  return new Blob([buffer], { type: 'audio/wav' })
}

function writeString(view: DataView, offset: number, str: string) {
  for (let i = 0; i < str.length; i++) {
    view.setUint8(offset + i, str.charCodeAt(i))
  }
}
