/**
 * commerce blueprint · wiring seam for `@platform-modules/uploads`.
 *
 * Adapter-minimalism (CLAUDE.md §4): uploads is a SUBPATH-ONLY module (no barrel). This seam composes
 * its two TRUST-BOUNDARY gates — `/magic-bytes` + `/size-limit` — into one validate() the host calls
 * before accepting a PRODUCT IMAGE.
 *
 * HARD FLOOR (CLAUDE.md: trust-boundary validation never cut): a forged product image (an SVG/script
 * blob masquerading as `image/png`) or an oversized blob is rejected HERE, before the product is ever
 * listed or sold. The composition test's catalog-fail-closed case turns on this gate running first.
 *
 * (Structurally identical to the community blueprint's uploads seam — the trust-boundary shape is the
 * same across app classes; only the noun differs, post-image vs product-image. A two-blueprint
 * convergence signal, delivery-stack §7.)
 */
import { detectMimeFromMagicBytes, type DetectedImageMime } from '@platform-modules/uploads/magic-bytes'
import { assertSizeLimit } from '@platform-modules/uploads/size-limit'

export class RejectedImageError extends Error {
  readonly name = 'RejectedImageError'
  constructor(reason: string) {
    super(`product image rejected: ${reason}`)
  }
}

/** A product-image candidate — only the raw bytes; the client's claimed Content-Type is forgeable, so absent. */
export type ProductImage = { bytes: Uint8Array }

/**
 * Validate a product image at the trust boundary. Returns the SNIFFED mime (from the bytes, never a
 * client claim). Throws `SizeLimitExceededError` (from the module) over `maxBytes`, and
 * `RejectedImageError` when the header is not a permitted image. Size first (reject huge before sniff).
 */
export function validateProductImage(image: ProductImage, maxBytes: number): DetectedImageMime {
  assertSizeLimit(image.bytes.byteLength, maxBytes)
  const mime = detectMimeFromMagicBytes(image.bytes)
  if (mime === null) {
    throw new RejectedImageError('content does not match a permitted image type (possible spoof)')
  }
  return mime
}
