import type { VoicePlatformAPI } from "./types"

/**
 * Captures microphone audio in the renderer, downsamples to 16 kHz mono PCM,
 * and streams chunks to the desktop main process for cloud STT.
 */
export function createRendererVoiceCapture(api: VoicePlatformAPI) {
  let stream: MediaStream | undefined
  let context: AudioContext | undefined
  let processor: ScriptProcessorNode | undefined
  let source: MediaStreamAudioSourceNode | undefined
  let active = false

  const stop = () => {
    active = false
    processor?.disconnect()
    source?.disconnect()
    processor = undefined
    source = undefined
    void context?.close()
    context = undefined
    stream?.getTracks().forEach((track) => track.stop())
    stream = undefined
  }

  const start = async () => {
    if (active) return
    active = true
    stream = await navigator.mediaDevices.getUserMedia({
      audio: {
        channelCount: 1,
        echoCancellation: true,
        noiseSuppression: true,
        autoGainControl: true,
      },
      video: false,
    })

    context = new AudioContext()
    source = context.createMediaStreamSource(stream)
    // ScriptProcessorNode is deprecated but widely available in Electron without AudioWorklet setup.
    processor = context.createScriptProcessor(4096, 1, 1)
    const inputRate = context.sampleRate

    processor.onaudioprocess = (event) => {
      if (!active) return
      const input = event.inputBuffer.getChannelData(0)
      const pcm = downsampleTo16k(input, inputRate)
      if (pcm.byteLength === 0) return
      void api.sendAudioChunk(pcm)
    }

    source.connect(processor)
    processor.connect(context.destination)
  }

  return { start, stop, get active() { return active } }
}

function downsampleTo16k(input: Float32Array, inputRate: number) {
  if (inputRate === 16000) return floatTo16BitPCM(input)
  const ratio = inputRate / 16000
  const length = Math.floor(input.length / ratio)
  const result = new Float32Array(length)
  let offsetResult = 0
  let offsetInput = 0
  while (offsetResult < length) {
    const next = Math.floor((offsetResult + 1) * ratio)
    let accum = 0
    let count = 0
    for (let i = offsetInput; i < next && i < input.length; i += 1) {
      accum += input[i]
      count += 1
    }
    result[offsetResult] = count ? accum / count : 0
    offsetResult += 1
    offsetInput = next
  }
  return floatTo16BitPCM(result)
}

function floatTo16BitPCM(input: Float32Array) {
  const buffer = new ArrayBuffer(input.length * 2)
  const view = new DataView(buffer)
  for (let i = 0; i < input.length; i += 1) {
    const sample = Math.max(-1, Math.min(1, input[i]))
    view.setInt16(i * 2, sample < 0 ? sample * 0x8000 : sample * 0x7fff, true)
  }
  return buffer
}
