/** @odoo-module **/
/**
 * Image compression for file uploads on Odoo.
 *
 * Problem: On iPhone, selecting 4+ photos (5-15MB each) causes the
 * browser tab to crash because Odoo converts each to a base64 data URL
 * before uploading. 7 photos = 50-100MB of strings in memory.
 *
 * Solution: Intercept at the FileUploader level, compress each image
 * via Canvas BEFORE the data URL conversion. A 5MB photo becomes ~300KB.
 *
 * The FileUploader.onFileChange is completely overridden (not wrapped)
 * to avoid any DataTransfer API issues on iPhone Safari.
 */
import { AttachmentUploadService } from "@mail/core/common/attachment_upload_service";
import { FileUploader } from "@web/views/fields/file_handler";
import { patch } from "@web/core/utils/patch";
import { getDataURLFromFile } from "@web/core/utils/urls";
import { checkFileSize } from "@web/core/utils/files";

const IMAGE_TYPES = new Set([
    "image/jpeg", "image/png", "image/webp", "image/bmp",
    "image/heic", "image/heif",
]);
const MAX_DIMENSION = 1280; // Conservative for mobile memory
const JPEG_QUALITY = 0.80;
const SKIP_THRESHOLD = 500 * 1024; // 500KB

/**
 * Compress an image File via Canvas API.
 * Returns the original file if anything fails.
 */
function compressImageFile(file) {
    return new Promise((resolve) => {
        try {
            const img = new Image();
            const objectUrl = URL.createObjectURL(file);
            const cleanup = () => {
                try { URL.revokeObjectURL(objectUrl); } catch(e) {}
                try { img.src = ""; } catch(e) {}
            };
            const timeout = setTimeout(() => {
                cleanup();
                resolve(file); // Timeout fallback after 10s
            }, 10000);
            img.onload = () => {
                try {
                    clearTimeout(timeout);
                    let w = img.naturalWidth || img.width;
                    let h = img.naturalHeight || img.height;
                    if (w > MAX_DIMENSION || h > MAX_DIMENSION) {
                        const ratio = Math.min(MAX_DIMENSION / w, MAX_DIMENSION / h);
                        w = Math.round(w * ratio);
                        h = Math.round(h * ratio);
                    }
                    const canvas = document.createElement("canvas");
                    canvas.width = w;
                    canvas.height = h;
                    canvas.getContext("2d").drawImage(img, 0, 0, w, h);
                    canvas.toBlob(
                        (blob) => {
                            cleanup();
                            canvas.width = 0;
                            canvas.height = 0;
                            if (!blob) { resolve(file); return; }
                            const name = file.name.replace(/\.[^.]+$/, "") + ".jpg";
                            resolve(new File([blob], name, {
                                type: "image/jpeg",
                                lastModified: file.lastModified,
                            }));
                        },
                        "image/jpeg",
                        JPEG_QUALITY
                    );
                } catch (e) {
                    clearTimeout(timeout);
                    cleanup();
                    resolve(file);
                }
            };
            img.onerror = () => {
                clearTimeout(timeout);
                cleanup();
                resolve(file);
            };
            img.src = objectUrl;
        } catch (e) {
            resolve(file);
        }
    });
}

/**
 * Override FileUploader.onFileChange to compress images before
 * converting to data URLs. This prevents the massive memory spike
 * that crashes mobile browsers.
 *
 * We re-implement onFileChange instead of wrapping it to avoid
 * DataTransfer API issues on iPhone Safari.
 */
patch(FileUploader.prototype, {
    async onFileChange(ev) {
        const rawFiles = ev.target?.files;
        if (!rawFiles || !rawFiles.length) {
            return;
        }

        // Check if any file needs compression
        let hasLargeImages = false;
        for (const f of rawFiles) {
            if (IMAGE_TYPES.has(f.type) && f.size > SKIP_THRESHOLD) {
                hasLargeImages = true;
                break;
            }
        }

        // No large images -- use standard Odoo behavior
        if (!hasLargeImages) {
            return super.onFileChange(ev);
        }

        // Process files one at a time with compression
        const files = [...rawFiles].filter((f) => this.validFileType(f));
        const target = ev.target;

        for (const file of files) {
            let processedFile = file;

            // Compress large images
            if (IMAGE_TYPES.has(file.type) && file.size > SKIP_THRESHOLD) {
                try {
                    processedFile = await compressImageFile(file);
                } catch (e) {
                    processedFile = file; // fallback to original
                }
            }

            // Size check
            if (this.props.checkSize && !checkFileSize(processedFile.size, this.notification)) {
                continue;
            }

            this.state.isUploading = true;
            try {
                const data = await getDataURLFromFile(processedFile);
                if (!processedFile.size) {
                    this.notification.add(
                        `There was a problem while uploading: ${processedFile.name}`,
                        { type: "danger" }
                    );
                    continue;
                }
                await this.props.onUploaded({
                    name: processedFile.name,
                    size: processedFile.size,
                    type: processedFile.type,
                    data: data.split(",")[1],
                    objectUrl:
                        processedFile.type === "application/pdf"
                            ? URL.createObjectURL(processedFile)
                            : null,
                });
            } catch (e) {
                // Skip this file on error, continue with others
            } finally {
                this.state.isUploading = false;
            }
        }

        // Reset input so same file can be re-selected
        target.value = null;
        if (this.props.multiUpload && this.props.onUploadComplete) {
            this.props.onUploadComplete({});
        }
    },
});

/**
 * Safety net for drag-drop and paste uploads that bypass FileUploader.
 */
patch(AttachmentUploadService.prototype, {
    async upload(thread, composer, file, options) {
        if (file && IMAGE_TYPES.has(file.type) && file.size > SKIP_THRESHOLD) {
            try {
                file = await compressImageFile(file);
            } catch (e) {
                // Use original file
            }
        }
        return super.upload(thread, composer, file, options);
    },
});
