let _sharp = null;
async function getSharp() {
    if (!_sharp) {
        const mod = await import(/* webpackIgnore: true */ 'sharp');
        _sharp = (mod.default ?? mod);
    }
    return _sharp;
}
const DEFAULT_OPTIONS = {
    quality: 82,
    maxWidth: 4096,
    maxHeight: 4096,
    preserveFormats: ['image/svg+xml', 'image/gif'],
};
const OPTIMIZABLE_TYPES = new Set([
    'image/jpeg',
    'image/jpg',
    'image/png',
    'image/webp',
    'image/tiff',
    'image/avif',
    'image/bmp',
]);
/**
 * Optimizes an image: converts to WebP, resizes if too large,
 * extracts dimensions, and generates a tiny blur placeholder.
 * Non-image files and SVGs/GIFs are returned unchanged.
 */
export async function optimizeImage(input, originalFilename, mimeType, opts = {}) {
    const options = { ...DEFAULT_OPTIONS, ...opts };
    const inputBuffer = input instanceof Buffer
        ? input
        : Buffer.from(new Uint8Array(input instanceof ArrayBuffer ? input : input.buffer));
    const originalSize = inputBuffer.length;
    const sharp = await getSharp();
    if (!OPTIMIZABLE_TYPES.has(mimeType) || options.preserveFormats.includes(mimeType)) {
        let width = 0;
        let height = 0;
        try {
            const meta = await sharp(inputBuffer).metadata();
            width = meta.width ?? 0;
            height = meta.height ?? 0;
        }
        catch {
            // non-image or unrecognized — return as-is
        }
        return {
            buffer: inputBuffer,
            width,
            height,
            originalSize,
            optimizedSize: originalSize,
            savings: 0,
            format: 'original',
            mimeType,
            filename: originalFilename,
        };
    }
    let pipeline = sharp(inputBuffer, { animated: false }).rotate(); // auto-orient based on EXIF
    const metadata = await sharp(inputBuffer).metadata();
    let { width = 0, height = 0 } = metadata;
    if (width > options.maxWidth || height > options.maxHeight) {
        pipeline = pipeline.resize(options.maxWidth, options.maxHeight, {
            fit: 'inside',
            withoutEnlargement: true,
        });
        const ratio = Math.min(options.maxWidth / width, options.maxHeight / height);
        width = Math.round(width * ratio);
        height = Math.round(height * ratio);
    }
    const optimizedBuffer = await pipeline
        .webp({
        quality: options.quality,
        effort: 4, // balanced speed/compression (0-6)
        smartSubsample: true,
    })
        .toBuffer();
    const optimizedSize = optimizedBuffer.length;
    // If WebP is actually larger (rare, happens with tiny images), keep original
    if (optimizedSize >= originalSize && mimeType === 'image/webp') {
        return {
            buffer: inputBuffer,
            width: metadata.width ?? 0,
            height: metadata.height ?? 0,
            originalSize,
            optimizedSize: originalSize,
            savings: 0,
            format: 'original',
            mimeType,
            filename: originalFilename,
        };
    }
    const webpFilename = originalFilename.replace(/\.[^.]+$/, '.webp');
    let blurHash;
    try {
        const placeholder = await sharp(inputBuffer)
            .resize(20, 20, { fit: 'inside' })
            .webp({ quality: 20 })
            .toBuffer();
        blurHash = `data:image/webp;base64,${placeholder.toString('base64')}`;
    }
    catch {
        // blur generation is best-effort
    }
    return {
        buffer: optimizedBuffer,
        width,
        height,
        originalSize,
        optimizedSize,
        savings: Math.round((1 - optimizedSize / originalSize) * 100),
        format: 'webp',
        mimeType: 'image/webp',
        filename: webpFilename,
        blurHash,
    };
}
/**
 * Extracts image metadata without performing optimization.
 * Useful for non-destructive dimension/format reads.
 */
export async function getImageMetadata(input) {
    const buffer = input instanceof Buffer
        ? input
        : Buffer.from(new Uint8Array(input instanceof ArrayBuffer ? input : input.buffer));
    try {
        const sharp = await getSharp();
        const meta = await sharp(buffer).metadata();
        return {
            width: meta.width ?? null,
            height: meta.height ?? null,
            format: meta.format ?? null,
            hasAlpha: meta.hasAlpha ?? false,
            isAnimated: (meta.pages ?? 1) > 1,
            space: meta.space ?? null,
        };
    }
    catch {
        return null;
    }
}
export function formatBytes(bytes) {
    if (bytes < 1024)
        return `${bytes} B`;
    if (bytes < 1024 * 1024)
        return `${(bytes / 1024).toFixed(1)} KB`;
    return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
}
//# sourceMappingURL=optimize.js.map