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

function audioBufferToWav(audioBuffer) {
  const numChannels = audioBuffer.numberOfChannels;
  const sampleRate = audioBuffer.sampleRate;
  const format = 1;
  const bitDepth = 16;

  const bytesPerSample = bitDepth / 8;
  const blockAlign = numChannels * bytesPerSample;
  const byteRate = sampleRate * blockAlign;

  const pcmChannels = [];
  for (let i = 0; i < numChannels; i++) {
    pcmChannels.push(audioBuffer.getChannelData(i));
  }

  const pcmDataLength = pcmChannels[0].length;
  const dataSize = pcmDataLength * numChannels * bytesPerSample;

  const interleavedPcm = new Int16Array(pcmDataLength * numChannels);
  let outputOffset = 0;
  for (let i = 0; i < pcmDataLength; i++) {
    for (let channel = 0; channel < numChannels; channel++) {
      let sample = pcmChannels[channel][i];
      sample = Math.max(-1, Math.min(1, sample));
      interleavedPcm[outputOffset++] =
        sample < 0 ? sample * 0x8000 : sample * 0x7fff;
    }
  }

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

  writeString(view, 0, "RIFF");
  view.setUint32(4, 36 + dataSize, true);
  writeString(view, 8, "WAVE");

  writeString(view, 12, "fmt ");
  view.setUint32(16, 16, true);
  view.setUint16(20, format, true);
  view.setUint16(22, numChannels, true);
  view.setUint32(24, sampleRate, true);
  view.setUint32(28, byteRate, true);
  view.setUint16(32, blockAlign, true);
  view.setUint16(34, bitDepth, true);

  writeString(view, 36, "data");
  view.setUint32(40, dataSize, true);

  new Int16Array(buffer, 44).set(interleavedPcm);

  return new Blob([view], { type: "audio/wav" });
}

export default async function convertBlobToWav(blob) {
  if (!blob || blob.size === 0) {
    throw new Error("Input blob is empty or null. Cannot convert to WAV.");
  }

  let audioContext;
  try {
    audioContext = new (window.AudioContext || window.webkitAudioContext)();
    const arrayBuffer = await blob.arrayBuffer();
    const audioBuffer = await audioContext.decodeAudioData(arrayBuffer);
    const wavBlob = audioBufferToWav(audioBuffer);

    return wavBlob;
  } catch (error) {
    console.error("Error during blob to WAV conversion:", error);
    throw new Error(`Failed to convert blob to WAV: ${error.message}`);
  } finally {
    if (audioContext && audioContext.state !== "closed") {
      await audioContext.close();
    }
  }
}
