import { extractDataUrlComponents } from "../utils.js";
const FILE_EXT_MIME_TYPES = {
    png: "image/png",
    jpeg: "image/jpeg",
    jpg: "image/jpeg",
    webp: "image/webp",
    heic: "image/heic",
    heif: "image/heif"
};
const ACCEPTED_IMAGE_MIME_TYPES = Object.values(FILE_EXT_MIME_TYPES);
const getFileURLExtension = (url)=>{
    const pathname = new URL(url).pathname;
    const parts = pathname.split(".");
    return parts.length > 1 ? parts.pop()?.toLowerCase() || null : null;
};
const getFileURLMimeType = (url)=>{
    const ext = getFileURLExtension(url);
    return ext ? FILE_EXT_MIME_TYPES[ext] || null : null;
};
const getImageParts = (message)=>{
    if (message.image_url.url.startsWith("data:")) {
        const { mimeType, base64: data } = extractDataUrlComponents(message.image_url.url);
        if (!mimeType || !ACCEPTED_IMAGE_MIME_TYPES.includes(mimeType)) throw new Error(`Gemini only accepts the following mimeTypes: ${ACCEPTED_IMAGE_MIME_TYPES.join("\n")}`);
        return {
            inlineData: {
                mimeType,
                data
            }
        };
    }
    const mimeType = getFileURLMimeType(message.image_url.url);
    if (!mimeType || !ACCEPTED_IMAGE_MIME_TYPES.includes(mimeType)) throw new Error(`Gemini only accepts the following mimeTypes: ${ACCEPTED_IMAGE_MIME_TYPES.join("\n")}`);
    return {
        fileData: {
            mimeType,
            fileUri: message.image_url.url
        }
    };
};
export const getPartsText = (parts)=>{
    const textStrings = [];
    if (parts.length) {
        for (const part of parts){
            if (part.text) {
                textStrings.push(part.text);
            }
        }
    }
    if (textStrings.length > 0) {
        return textStrings.join("");
    } else {
        return "";
    }
};
/**
 * Returns all text found in all parts of first candidate.
 */ export const getText = (response)=>{
    if (response.candidates?.[0].content?.parts) {
        return getPartsText(response.candidates?.[0].content?.parts);
    }
    return "";
};
export const cleanParts = (message)=>{
    return {
        ...message,
        parts: message.parts.filter((part)=>part.text?.trim() || part.inlineData || part.fileData || part.functionCall)
    };
};
export const getChatContext = (params)=>{
    // Gemini doesn't allow:
    // 1. Consecutive messages from the same role
    // 2. Parts that have empty text
    const messages = GeminiHelper.mergeNeighboringSameRoleMessages(params.messages.map(GeminiHelper.chatMessageToGemini)).map(cleanParts);
    const history = messages.slice(0, -1);
    const message = messages[messages.length - 1].parts;
    return {
        history,
        message
    };
};
/**
 * Helper class providing utility functions for Gemini
 */ export class GeminiHelper {
    // Gemini only has user and model roles. Put the rest in user role.
    static ROLES_TO_GEMINI = {
        user: "user",
        system: "user",
        assistant: "model",
        memory: "user"
    };
    static ROLES_FROM_GEMINI = {
        user: "user",
        model: "assistant"
    };
    static mergeNeighboringSameRoleMessages(messages) {
        return messages.map(cleanParts).filter((message)=>message.parts.length).reduce((result, current, index, original)=>{
            if (index > 0 && original[index - 1].role === current.role) {
                result[result.length - 1].parts = [
                    ...result[result.length - 1].parts,
                    ...current.parts
                ];
            } else {
                result.push(current);
            }
            return result;
        }, []);
    }
    static messageContentToGeminiParts(content) {
        if (typeof content === "string") {
            return [
                {
                    text: content
                }
            ];
        }
        const parts = [];
        const imageContents = content.filter((i)=>i.type === "image_url");
        parts.push(...imageContents.map(getImageParts));
        const textContents = content.filter((i)=>i.type === "text");
        parts.push(...textContents.map((t)=>({
                text: t.text
            })));
        return parts;
    }
    static chatMessageToGemini(message) {
        return {
            role: GeminiHelper.ROLES_TO_GEMINI[message.role],
            parts: GeminiHelper.messageContentToGeminiParts(message.content)
        };
    }
}
