
import { raceTimeout } from '../../../../base/common/async.js';
import { decodeBase64, VSBuffer } from '../../../../base/common/buffer.js';
import { LRUCache } from '../../../../base/common/map.js';
import { createElement } from '../../../../base/browser/dom.js';
import { joinPath } from '../../../../base/common/resources.js';
import { CommandsRegistry } from '../../../../platform/commands/common/commands.js';

async function resizeImage(data, mimeType) {
    const isGif = mimeType === "image/gif";
    if (typeof data === "string") {
        data = convertStringToUInt8Array(data);
    }
    return ( new Promise((resolve, reject) => {
        const blob = ( new Blob([data], {
            type: mimeType
        }));
        const img = ( new Image());
        const url = URL.createObjectURL(blob);
        img.src = url;
        img.onload = () => {
            URL.revokeObjectURL(url);
            let {
                width,
                height
            } = img;
            if ((width <= 768 || height <= 768) && !isGif) {
                resolve(data);
                return;
            }
            if (width > 2048 || height > 2048) {
                const scaleFactor = 2048 / Math.max(width, height);
                width = Math.round(width * scaleFactor);
                height = Math.round(height * scaleFactor);
            }
            const scaleFactor = 768 / Math.min(width, height);
            width = Math.round(width * scaleFactor);
            height = Math.round(height * scaleFactor);
            const canvas = createElement("canvas");
            canvas.width = width;
            canvas.height = height;
            const ctx = canvas.getContext("2d");
            if (ctx) {
                ctx.drawImage(img, 0, 0, width, height);
                const jpegTypes = ["image/jpeg", "image/jpg"];
                const outputMimeType = mimeType && jpegTypes.includes(mimeType) ? "image/jpeg" : "image/png";
                canvas.toBlob(blob => {
                    if (blob) {
                        const reader = ( new FileReader());
                        reader.onload = () => {
                            resolve(( new Uint8Array(reader.result)));
                        };
                        reader.onerror = error => reject(error);
                        reader.readAsArrayBuffer(blob);
                    } else {
                        reject(( new Error("Failed to create blob from canvas")));
                    }
                }, outputMimeType);
            } else {
                reject(( new Error("Failed to get canvas context")));
            }
        };
        img.onerror = error => {
            URL.revokeObjectURL(url);
            reject(error);
        };
    }));
}
function createImageThumbnail(data, maxSize, targetWindow) {
    return ( new Promise(resolve => {
        const blob = ( new Blob([data]));
        const img = targetWindow.document.createElement("img");
        const url = URL.createObjectURL(blob);
        img.src = url;
        img.onload = () => {
            URL.revokeObjectURL(url);
            const {
                width,
                height
            } = img;
            const scaleFactor = Math.min(1, maxSize / Math.max(width, height));
            const targetWidth = Math.max(1, Math.round(width * scaleFactor));
            const targetHeight = Math.max(1, Math.round(height * scaleFactor));
            const canvas = targetWindow.document.createElement("canvas");
            canvas.width = targetWidth;
            canvas.height = targetHeight;
            const ctx = canvas.getContext("2d");
            if (!ctx) {
                resolve(undefined);
                return;
            }
            ctx.drawImage(img, 0, 0, targetWidth, targetHeight);
            const outputMimeType = data.length >= 3 && data[0] === 0xFF && data[1] === 0xD8 && data[2] === 0xFF ? "image/jpeg" : "image/png";
            canvas.toBlob(thumbnail => resolve(thumbnail ?? undefined), outputMimeType);
        };
        img.onerror = () => {
            URL.revokeObjectURL(url);
            resolve(undefined);
        };
    }));
}
const thumbnailCache = ( new LRUCache(50));
const THUMBNAIL_DECODE_TIMEOUT_MS = 10_000;
function getOrCreateImageThumbnail(cacheKey, data, maxSize, targetWindow) {
    const key = `${cacheKey}:${maxSize}:${data.byteLength}`;
    const cached = thumbnailCache.get(key);
    if (cached) {
        return cached;
    }
    const thumbnail = raceTimeout(
        createImageThumbnail(data, maxSize, targetWindow),
        THUMBNAIL_DECODE_TIMEOUT_MS
    ).then(blob => {
        if (!blob && thumbnailCache.peek(key) === thumbnail) {
            thumbnailCache.delete(key);
        }
        return blob;
    });
    thumbnailCache.set(key, thumbnail);
    return thumbnail;
}
function convertStringToUInt8Array(data) {
    const base64Data = data.includes(",") ? data.split(",")[1] : data;
    if (isValidBase64(base64Data)) {
        return decodeBase64(base64Data).buffer;
    }
    return ( new TextEncoder()).encode(data);
}
function isValidBase64(str) {
    return /^[A-Za-z0-9+/]*={0,2}$/.test(str) && (() => {
        try {
            atob(str);
            return true;
        } catch {
            return false;
        }
    })();
}
async function createFileForMedia(fileService, imagesFolder, dataTransfer, mimeType) {
    const exists = await fileService.exists(imagesFolder);
    if (!exists) {
        await fileService.createFolder(imagesFolder);
    }
    const ext = mimeType.split("/")[1] || "png";
    const filename = `image-${Date.now()}.${ext}`;
    const fileUri = joinPath(imagesFolder, filename);
    const buffer = VSBuffer.wrap(dataTransfer);
    await fileService.writeFile(fileUri, buffer);
    return fileUri;
}
async function cleanupOldImages(fileService, logService, imagesFolder) {
    const exists = await fileService.exists(imagesFolder);
    if (!exists) {
        return;
    }
    const duration = 7 * 24 * 60 * 60 * 1000;
    const files = await fileService.resolve(imagesFolder);
    if (!files.children) {
        return;
    }
    await Promise.all(( files.children.map(async file => {
        try {
            const timestamp = getTimestampFromFilename(file.name);
            if (timestamp && (Date.now() - timestamp > duration)) {
                await fileService.del(file.resource);
            }
        } catch (err) {
            logService.error("Failed to clean up old images", err);
        }
    })));
}
function getTimestampFromFilename(filename) {
    const match = filename.match(/image-(\d+)\./);
    if (match) {
        return parseInt(match[1], 10);
    }
    return undefined;
}
CommandsRegistry.registerCommand("_chat.resizeImage", async (_accessor, data, mimeType) => {
    if (data instanceof VSBuffer) {
        data = data.buffer;
    }
    return resizeImage(data, mimeType);
});
async function imageToHash(data) {
    const hashBuffer = await crypto.subtle.digest("SHA-256", data);
    const hashArray = Array.from(( new Uint8Array(hashBuffer)));
    return ( hashArray.map(b => ( b.toString(16)).padStart(2, "0"))).join("");
}

export { cleanupOldImages, convertStringToUInt8Array, createFileForMedia, getOrCreateImageThumbnail, imageToHash, resizeImage };
