// Client-side Audio Compression using Web Audio API
// Uses pure WAV compression as fallback when MP3 encoding fails
// Compresses audio to target 2-3MB for fast streaming

export interface CompressionOptions {
  targetSizeMB?: number
  maxSizeMB?: number
  minBitrate?: number
  maxBitrate?: number
}

export interface CompressionResult {
  blob: Blob
  originalSize: number
  compressedSize: number
  bitrate: number
  compressionRatio: number
  wasCompressed: boolean
}

// Decode audio file to PCM samples using Web Audio API
async function decodeAudioFile(
  file: File,
): Promise<{ leftChannel: Float32Array; rightChannel: Float32Array | null; sampleRate: number; duration: number }> {
  const arrayBuffer = await file.arrayBuffer()
  const audioContext = new (window.AudioContext || (window as any).webkitAudioContext)()

  try {
    const audioBuffer = await audioContext.decodeAudioData(arrayBuffer)
    const sampleRate = audioBuffer.sampleRate
    const duration = audioBuffer.duration

    const leftChannel = audioBuffer.getChannelData(0)
    const rightChannel = audioBuffer.numberOfChannels > 1 ? audioBuffer.getChannelData(1) : null

    return { leftChannel, rightChannel, sampleRate, duration }
  } finally {
    await audioContext.close()
  }
}

// Resample audio to target sample rate
function resample(samples: Float32Array, fromRate: number, toRate: number): Float32Array {
  if (fromRate === toRate) return samples

  const ratio = fromRate / toRate
  const newLength = Math.floor(samples.length / ratio)
  const result = new Float32Array(newLength)

  for (let i = 0; i < newLength; i++) {
    const srcIndex = i * ratio
    const srcIndexFloor = Math.floor(srcIndex)
    const srcIndexCeil = Math.min(srcIndexFloor + 1, samples.length - 1)
    const t = srcIndex - srcIndexFloor
    result[i] = samples[srcIndexFloor] * (1 - t) + samples[srcIndexCeil] * t
  }

  return result
}

// Create WAV file from PCM data
function createWavBlob(leftChannel: Float32Array, rightChannel: Float32Array | null, sampleRate: number): Blob {
  const numChannels = rightChannel ? 2 : 1
  const bitsPerSample = 16
  const bytesPerSample = bitsPerSample / 8
  const blockAlign = numChannels * bytesPerSample
  const byteRate = sampleRate * blockAlign
  const dataSize = leftChannel.length * blockAlign
  const headerSize = 44
  const totalSize = headerSize + dataSize

  const buffer = new ArrayBuffer(totalSize)
  const view = new DataView(buffer)

  // RIFF header
  writeString(view, 0, "RIFF")
  view.setUint32(4, totalSize - 8, true)
  writeString(view, 8, "WAVE")

  // fmt chunk
  writeString(view, 12, "fmt ")
  view.setUint32(16, 16, true) // chunk size
  view.setUint16(20, 1, true) // audio format (PCM)
  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)

  // Write audio data
  let offset = 44
  for (let i = 0; i < leftChannel.length; i++) {
    // Left channel
    const leftSample = Math.max(-1, Math.min(1, leftChannel[i]))
    view.setInt16(offset, leftSample < 0 ? leftSample * 0x8000 : leftSample * 0x7fff, true)
    offset += 2

    // Right channel (if stereo)
    if (rightChannel) {
      const rightSample = Math.max(-1, Math.min(1, rightChannel[i]))
      view.setInt16(offset, rightSample < 0 ? rightSample * 0x8000 : rightSample * 0x7fff, true)
      offset += 2
    }
  }

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

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

// Calculate optimal sample rate to achieve target file size
function calculateTargetSampleRate(
  duration: number,
  targetMB: number,
  channels: number,
  originalSampleRate: number,
): number {
  const targetBytes = targetMB * 1024 * 1024
  const bytesPerSample = 2 // 16-bit
  const headerSize = 44
  const availableDataBytes = targetBytes - headerSize

  // dataBytes = duration * sampleRate * channels * bytesPerSample
  // sampleRate = dataBytes / (duration * channels * bytesPerSample)
  const targetSampleRate = Math.floor(availableDataBytes / (duration * channels * bytesPerSample))

  // Common sample rates to choose from
  const commonRates = [8000, 11025, 16000, 22050, 32000, 44100, 48000]

  // Find the closest rate that's <= targetSampleRate and <= originalSampleRate
  let bestRate = 8000
  for (const rate of commonRates) {
    if (rate <= targetSampleRate && rate <= originalSampleRate) {
      bestRate = rate
    }
  }

  return bestRate
}

export async function compressAudioClient(
  file: File,
  options: CompressionOptions = {},
  onProgress?: (progress: number) => void,
): Promise<CompressionResult> {
  const originalSize = file.size
  const originalMB = originalSize / (1024 * 1024)
  const targetMB = options.targetSizeMB || 2
  const maxMB = options.maxSizeMB || 2.5

  console.log(`[Client Compression] Starting silent compression: ${originalMB.toFixed(2)}MB → ${targetMB}MB target`)
  onProgress?.(5)

  // If file is already under target, skip compression
  if (originalMB <= targetMB) {
    console.log(`[Client Compression] File already under ${targetMB}MB, skipping compression`)
    onProgress?.(100)
    return {
      blob: file,
      originalSize,
      compressedSize: originalSize,
      bitrate: 0,
      compressionRatio: 1,
      wasCompressed: false,
    }
  }

  try {
    // Decode audio file
    onProgress?.(10)
    const { leftChannel, rightChannel, sampleRate, duration } = await decodeAudioFile(file)
    const channels = rightChannel ? 2 : 1

    console.log(`[Client Compression] Decoded: ${duration.toFixed(1)}s, ${sampleRate}Hz, ${channels} channel(s)`)
    onProgress?.(30)

    // Calculate target sample rate to achieve desired file size
    const targetSampleRate = calculateTargetSampleRate(duration, targetMB, channels, sampleRate)
    console.log(`[Client Compression] Resampling ${sampleRate}Hz → ${targetSampleRate}Hz`)
    onProgress?.(40)

    // Resample both channels
    const resampledLeft = resample(leftChannel, sampleRate, targetSampleRate)
    const resampledRight = rightChannel ? resample(rightChannel, sampleRate, targetSampleRate) : null
    onProgress?.(70)

    // Create WAV file
    const compressedBlob = createWavBlob(resampledLeft, resampledRight, targetSampleRate)
    const compressedSize = compressedBlob.size
    const compressedMB = compressedSize / (1024 * 1024)
    const compressionRatio = originalSize / compressedSize

    console.log(
      `[Client Compression] Complete: ${originalMB.toFixed(2)}MB → ${compressedMB.toFixed(2)}MB (${compressionRatio.toFixed(2)}x)`,
    )
    onProgress?.(100)

    return {
      blob: compressedBlob,
      originalSize,
      compressedSize,
      bitrate: Math.round((compressedSize * 8) / duration / 1000),
      compressionRatio,
      wasCompressed: true,
    }
  } catch (error) {
    console.error("[Client Compression] Error during compression:", error)
    onProgress?.(100)

    // On error, return original file
    return {
      blob: file,
      originalSize,
      compressedSize: originalSize,
      bitrate: 0,
      compressionRatio: 1,
      wasCompressed: false,
    }
  }
}
