import { MAX_IMAGE_BYTES } from '~/lib/security-constants'

function loadImage(src: string): Promise<HTMLImageElement> {
  return new Promise((resolve, reject) => {
    const img = new Image()
    img.onload = () => resolve(img)
    img.onerror = () => reject(new Error('Failed to load image'))
    img.src = src
  })
}

function toBlob(img: HTMLImageElement, w: number, h: number, type: string, quality: number): Promise<Blob> {
  return new Promise((resolve, reject) => {
    const canvas = document.createElement('canvas')
    canvas.width = w
    canvas.height = h
    const ctx = canvas.getContext('2d')!
    // White background before drawing so transparent PNGs don't produce black fills in JPEG output
    ctx.fillStyle = '#ffffff'
    ctx.fillRect(0, 0, w, h)
    ctx.drawImage(img, 0, 0, w, h)
    canvas.toBlob(
      (blob) => (blob ? resolve(blob) : reject(new Error('Canvas export failed'))),
      type,
      quality,
    )
  })
}

/**
 * Compress an image to fit within MAX_IMAGE_BYTES.
 * Draws through canvas (stripping EXIF), outputs JPEG for controllable compression.
 * Tries quality reduction first, then dimension scaling.
 * Returns the best result achievable; callers should check size if they need a hard guarantee.
 */
export async function compressImage(file: File): Promise<Blob> {
  const url = URL.createObjectURL(file)
  let img: HTMLImageElement
  try {
    img = await loadImage(url)
  } finally {
    URL.revokeObjectURL(url)
  }

  const { naturalWidth: w, naturalHeight: h } = img
  // Use JPEG for lossy compression regardless of input format (best size control)
  const outputType = 'image/jpeg'
  const qualities = [0.92, 0.80, 0.65, 0.50]
  const scales = [1, 0.75, 0.5, 0.25]

  let best: Blob | null = null
  for (const scale of scales) {
    const sw = Math.max(1, Math.round(w * scale))
    const sh = Math.max(1, Math.round(h * scale))
    for (const quality of qualities) {
      const blob = await toBlob(img, sw, sh, outputType, quality)
      if (!best || blob.size < best.size) best = blob
      if (blob.size <= MAX_IMAGE_BYTES) return blob
    }
  }

  // Return the smallest we achieved (upload path will surface an error if still too large)
  return best!
}
