import { File as ExpoFile } from 'expo-file-system'
import { isServerPassthroughImage, selectUploadFormat, transcodeImage } from './transcode'
import type { ProcessOptions, RawAsset } from './types'

const DEFAULT_MAX_IMAGE_BYTES = 5 * 1024 * 1024
const DEFAULT_MAX_IMAGE_WIDTH = 4096
const DEFAULT_IMAGE_QUALITY = 0.85

function fileSizeOrZero(uri: string): number {
  try {
    const f = new ExpoFile(uri)
    return f.exists ? f.size : 0
  } catch {
    return 0
  }
}

/** Normalises a picked image for upload.
 *
 *  Two things happen, in priority order:
 *  1. Anything not in the destination instance's passthrough set (HEIC/HEIF/
 *     AVIF, plus WebP on a Pixelfed instance whose admin hasn't enabled it,
 *     etc.) is re-encoded. iOS Photos hands back HEIC by default and
 *     Mastodon's "Error processing thumbnail for upload" comes from libvips
 *     lacking libheif — converting client-side dodges that fragility.
 *  2. Allowlisted images that exceed `maxImageBytes` get resized down to
 *     `maxImageWidth` and re-encoded. Animated GIFs are passed through even
 *     when oversized — re-encoding would silently drop frames.
 *
 *  The output format is decided by `selectUploadFormat(supportedImageMimeTypes)`:
 *  WebP when the server publishes it (smaller files, native Mastodon support,
 *  Halflight admins can opt in), JPEG otherwise.
 */
export async function resizeImage(
  raw: RawAsset,
  opts: Pick<
    ProcessOptions,
    'maxImageBytes' | 'maxImageWidth' | 'imageQuality' | 'supportedImageMimeTypes'
  >
): Promise<RawAsset> {
  const maxBytes = opts.maxImageBytes ?? DEFAULT_MAX_IMAGE_BYTES
  const size = raw.size ?? fileSizeOrZero(raw.uri)
  const passthrough = isServerPassthroughImage(raw.mimeType, opts.supportedImageMimeTypes)
  const oversized = size > 0 && size > maxBytes
  const isAnimatedGif = raw.mimeType.toLowerCase() === 'image/gif'
  const needsResize = passthrough && oversized && !isAnimatedGif
  const needsTranscode = !passthrough

  if (!needsResize && !needsTranscode) return raw
  if (size === 0 && passthrough) {
    // Couldn't stat an allowlisted file — send as-is. The server's own size
    // limit is the safety net; transcoding when we don't even know the
    // source is intact risks turning a transient FS hiccup into corruption.
    return raw
  }

  try {
    const format = selectUploadFormat(opts.supportedImageMimeTypes)
    const saved = await transcodeImage(raw.uri, format, {
      resizeToWidth: needsResize
        ? (opts.maxImageWidth ?? DEFAULT_MAX_IMAGE_WIDTH)
        : undefined,
      quality: opts.imageQuality ?? DEFAULT_IMAGE_QUALITY,
    })
    return {
      ...raw,
      uri: saved.uri,
      mimeType: saved.mimeType,
      width: saved.width,
      height: saved.height,
      size: fileSizeOrZero(saved.uri) || undefined,
    }
  } catch {
    return raw
  }
}
