import { ImageFormat, parseImage, SamplingFilter } from "@nerf/native/image";
// 4.5MB - provides headroom below Anthropic's 5MB limit
const DEFAULT_MAX_BYTES = 4.5 * 1024 * 1024;
const DEFAULT_OPTIONS = {
    maxWidth: 2000,
    maxHeight: 2000,
    maxBytes: DEFAULT_MAX_BYTES,
    jpegQuality: 80,
};
/** Helper to pick the smaller of two buffers */
function pickSmaller(a, b) {
    return a.buffer.length <= b.buffer.length ? a : b;
}
/**
 * Resize an image to fit within the specified max dimensions and file size.
 * Returns the original image if it already fits within the limits.
 *
 * Uses the native Rust image module (N-API) for image processing.
 *
 * Strategy for staying under maxBytes:
 * 1. First resize to maxWidth/maxHeight
 * 2. Try both PNG and JPEG formats, pick the smaller one
 * 3. If still too large, try JPEG with decreasing quality
 * 4. If still too large, progressively reduce dimensions
 */
export async function resizeImage(img, options) {
    const opts = { ...DEFAULT_OPTIONS, ...options };
    const inputBuffer = Buffer.from(img.data, "base64");
    let image;
    try {
        image = await parseImage(new Uint8Array(inputBuffer));
    }
    catch {
        // Failed to decode image
        return {
            data: img.data,
            mimeType: img.mimeType,
            originalWidth: 0,
            originalHeight: 0,
            width: 0,
            height: 0,
            wasResized: false,
        };
    }
    try {
        const originalWidth = image.width;
        const originalHeight = image.height;
        const format = img.mimeType?.split("/")[1] ?? "png";
        // Check if already within all limits (dimensions AND size)
        const originalSize = inputBuffer.length;
        if (originalWidth <= opts.maxWidth && originalHeight <= opts.maxHeight && originalSize <= opts.maxBytes) {
            return {
                data: img.data,
                mimeType: img.mimeType ?? `image/${format}`,
                originalWidth,
                originalHeight,
                width: originalWidth,
                height: originalHeight,
                wasResized: false,
            };
        }
        // Calculate initial dimensions respecting max limits
        let targetWidth = originalWidth;
        let targetHeight = originalHeight;
        if (targetWidth > opts.maxWidth) {
            targetHeight = Math.round((targetHeight * opts.maxWidth) / targetWidth);
            targetWidth = opts.maxWidth;
        }
        if (targetHeight > opts.maxHeight) {
            targetWidth = Math.round((targetWidth * opts.maxHeight) / targetHeight);
            targetHeight = opts.maxHeight;
        }
        // Helper to resize and encode in both formats, returning the smaller one
        async function tryBothFormats(width, height, jpegQuality) {
            const resized = await image.resize(width, height, SamplingFilter.Lanczos3);
            const pngBytes = await resized.encode(ImageFormat.PNG, 100);
            const jpegBytes = await resized.encode(ImageFormat.JPEG, jpegQuality);
            const pngBuffer = new Uint8Array(pngBytes);
            const jpegBuffer = new Uint8Array(jpegBytes);
            return pickSmaller({ buffer: pngBuffer, mimeType: "image/png" }, { buffer: jpegBuffer, mimeType: "image/jpeg" });
        }
        // Try to produce an image under maxBytes
        const qualitySteps = [85, 70, 55, 40];
        const scaleSteps = [1.0, 0.75, 0.5, 0.35, 0.25];
        let best;
        let finalWidth = targetWidth;
        let finalHeight = targetHeight;
        // First attempt: resize to target dimensions, try both formats
        best = await tryBothFormats(targetWidth, targetHeight, opts.jpegQuality);
        if (best.buffer.length <= opts.maxBytes) {
            return {
                data: Buffer.from(best.buffer).toString("base64"),
                mimeType: best.mimeType,
                originalWidth,
                originalHeight,
                width: finalWidth,
                height: finalHeight,
                wasResized: true,
            };
        }
        // Still too large - try JPEG with decreasing quality
        for (const quality of qualitySteps) {
            best = await tryBothFormats(targetWidth, targetHeight, quality);
            if (best.buffer.length <= opts.maxBytes) {
                return {
                    data: Buffer.from(best.buffer).toString("base64"),
                    mimeType: best.mimeType,
                    originalWidth,
                    originalHeight,
                    width: finalWidth,
                    height: finalHeight,
                    wasResized: true,
                };
            }
        }
        // Still too large - reduce dimensions progressively
        for (const scale of scaleSteps) {
            finalWidth = Math.round(targetWidth * scale);
            finalHeight = Math.round(targetHeight * scale);
            if (finalWidth < 100 || finalHeight < 100) {
                break;
            }
            for (const quality of qualitySteps) {
                best = await tryBothFormats(finalWidth, finalHeight, quality);
                if (best.buffer.length <= opts.maxBytes) {
                    return {
                        data: Buffer.from(best.buffer).toString("base64"),
                        mimeType: best.mimeType,
                        originalWidth,
                        originalHeight,
                        width: finalWidth,
                        height: finalHeight,
                        wasResized: true,
                    };
                }
            }
        }
        // Last resort: return smallest version we produced
        return {
            data: Buffer.from(best.buffer).toString("base64"),
            mimeType: best.mimeType,
            originalWidth,
            originalHeight,
            width: finalWidth,
            height: finalHeight,
            wasResized: true,
        };
    }
    catch {
        // Failed to process image
        return {
            data: img.data,
            mimeType: img.mimeType,
            originalWidth: 0,
            originalHeight: 0,
            width: 0,
            height: 0,
            wasResized: false,
        };
    }
}
/**
 * Format a dimension note for resized images.
 * This helps the model understand the coordinate mapping.
 */
export function formatDimensionNote(result) {
    if (!result.wasResized) {
        return undefined;
    }
    const scale = result.originalWidth / result.width;
    return `[Image: original ${result.originalWidth}x${result.originalHeight}, displayed at ${result.width}x${result.height}. Multiply coordinates by ${scale.toFixed(2)} to map to original image.]`;
}
//# sourceMappingURL=image-resize.js.map