/**
 * Unified server-side image optimization pipeline.
 *
 * Every image upload across the site flows through this module: PR firm
 * featured images, contributor uploads, forum post attachments, author
 * avatars, banner creatives, anything else. Single source of truth so
 * every surface produces the same multi-format ladder and stays under
 * sane size caps.
 *
 * Output per input:
 *   - AVIF  (best compression, all current browsers)
 *   - WebP  (universal fallback)
 *   - JPEG  (final fallback for very old clients, preserved if input was JPEG)
 *   - thumbnail (320px, JPEG) for in-feed cards
 *
 * Quality: q=82 visually lossless for photos. Compression by default keeps
 * transparency on PNGs.
 *
 * Resize policy: longest-side cap of 4096px (prevents 12MP DSLR exports from
 * sitting un-resized on our CDN). Caller can override `maxSide`.
 *
 * Returns Buffers for the caller to upload to Supabase Storage.
 */
import sharp from "sharp";

export interface OptimizeOptions {
  /** Longest side in pixels — default 4096. */
  maxSide?: number;
  /** Compression quality 1-100 — default 82 (perceptually lossless for photos). */
  quality?: number;
  /** Skip thumbnail generation when caller doesn't need it. */
  thumbnail?: boolean;
  /** Skip AVIF (slow encode) when we only need WebP. */
  avif?: boolean;
}

export interface OptimizedImage {
  width: number;
  height: number;
  format: string;            // original input format
  hasAlpha: boolean;
  avif?: Buffer;
  webp: Buffer;
  jpeg?: Buffer;
  png?: Buffer;
  thumbnail?: Buffer;        // always JPEG, 320px wide
  bytes: {
    original: number;
    avif?: number;
    webp: number;
    jpeg?: number;
    png?: number;
    thumbnail?: number;
  };
}

export async function optimizeImage(
  input: Buffer,
  opts: OptimizeOptions = {},
): Promise<OptimizedImage> {
  const maxSide = opts.maxSide ?? 4096;
  const quality = opts.quality ?? 82;
  const wantAvif = opts.avif !== false;
  const wantThumb = opts.thumbnail !== false;

  // Read metadata up-front so we know what we're dealing with.
  const meta = await sharp(input, { failOn: "none" }).metadata();
  const inputFormat = (meta.format || "unknown").toLowerCase();
  const hasAlpha = !!meta.hasAlpha;
  const inputWidth = meta.width || 0;
  const inputHeight = meta.height || 0;

  // Build a pipeline that's rotation-aware (EXIF) and resized when needed.
  function pipeline() {
    let p = sharp(input, { failOn: "none" }).rotate();
    if (inputWidth > maxSide || inputHeight > maxSide) {
      p = p.resize({ width: maxSide, height: maxSide, fit: "inside", withoutEnlargement: true });
    }
    return p;
  }

  const out: OptimizedImage = {
    width: inputWidth,
    height: inputHeight,
    format: inputFormat,
    hasAlpha,
    webp: Buffer.alloc(0),
    bytes: { original: input.byteLength, webp: 0 },
  };

  // ── WebP (always; universal modern fallback) ──────────────────────────────
  out.webp = await pipeline()
    .webp({ quality, effort: 4, alphaQuality: hasAlpha ? quality : undefined })
    .toBuffer();
  out.bytes.webp = out.webp.byteLength;

  // ── AVIF (better compression, slower encode) ──────────────────────────────
  if (wantAvif) {
    try {
      out.avif = await pipeline()
        .avif({ quality, effort: 4 })
        .toBuffer();
      out.bytes.avif = out.avif.byteLength;
    } catch {
      // libvips without AVIF support — skip, WebP covers us
    }
  }

  // ── Preserve original format for legacy fallback ──────────────────────────
  if (hasAlpha) {
    out.png = await pipeline()
      .png({ compressionLevel: 9, adaptiveFiltering: true })
      .toBuffer();
    out.bytes.png = out.png.byteLength;
  } else {
    out.jpeg = await pipeline()
      .jpeg({ quality, progressive: true, mozjpeg: true })
      .toBuffer();
    out.bytes.jpeg = out.jpeg.byteLength;
  }

  // ── Thumbnail (320px wide JPEG, no transparency — feed/card use only) ─────
  if (wantThumb) {
    out.thumbnail = await sharp(input, { failOn: "none" })
      .rotate()
      .resize({ width: 320, height: 240, fit: "cover", position: "centre" })
      .jpeg({ quality: 80, progressive: true, mozjpeg: true })
      .toBuffer();
    out.bytes.thumbnail = out.thumbnail.byteLength;
  }

  return out;
}

/** Convenience: only need a single optimized buffer in best modern format. */
export async function optimizeToWebp(input: Buffer, opts: OptimizeOptions = {}) {
  const r = await optimizeImage(input, { ...opts, avif: false, thumbnail: false });
  return { buffer: r.webp, width: r.width, height: r.height, bytes: r.bytes };
}
