/**
 * Pure geometry for the round avatar cropper (KAR-665). The UI shows an image
 * inside a square viewport (rendered round via CSS), pannable + zoomable; these
 * helpers map the viewport back to a source square for the output canvas.
 */

/** Scale at which the image just covers a square viewport (zoom = 1). */
export function coverScale(imgW: number, imgH: number, viewport: number): number {
  return viewport / Math.min(imgW, imgH)
}

/**
 * Clamp a pan offset (display px) so the displayed image never shows a gap.
 * `displayed` is the on-screen size along the axis; slack is split both ways.
 */
export function clampOffset(offset: number, displayed: number, viewport: number): number {
  const slack = displayed - viewport
  if (slack <= 0) return 0
  const max = slack / 2
  return Math.max(-max, Math.min(max, offset))
}

export interface CropInput {
  imgW: number
  imgH: number
  viewport: number
  zoom: number
  offsetX: number
  offsetY: number
}

export interface SourceSquare {
  sx: number
  sy: number
  size: number
}

/**
 * Map the square viewport back to a source-pixel square. Larger zoom → smaller
 * (tighter) source square. The square is clamped to stay within the image.
 */
export function sourceSquare({ imgW, imgH, viewport, zoom, offsetX, offsetY }: CropInput): SourceSquare {
  const s = coverScale(imgW, imgH, viewport) * zoom
  const size = Math.min(viewport / s, imgW, imgH)

  const baseX = (imgW - size) / 2
  const baseY = (imgH - size) / 2
  const sx = clampToBounds(baseX - offsetX / s, imgW - size)
  const sy = clampToBounds(baseY - offsetY / s, imgH - size)

  return { sx, sy, size }
}

function clampToBounds(value: number, maxStart: number): number {
  if (maxStart <= 0) return 0
  return Math.max(0, Math.min(maxStart, value))
}
