import { randomUUID } from "crypto";
import { readFileSync } from "fs";
import { extname } from "path";
import { logger } from "./globalLogger.js";
/**
 * Convert image file path to base64 format
 * @param imagePath Image file path
 * @returns base64 format image data URL
 */
export const convertImageToBase64 = (imagePath) => {
    try {
        const imageBuffer = readFileSync(imagePath);
        const ext = extname(imagePath).toLowerCase().substring(1);
        // Determine MIME type based on file extension
        let mimeType = "image/png"; // Default
        switch (ext) {
            case "jpg":
            case "jpeg":
                mimeType = "image/jpeg";
                break;
            case "png":
                mimeType = "image/png";
                break;
            case "gif":
                mimeType = "image/gif";
                break;
            case "webp":
                mimeType = "image/webp";
                break;
            case "bmp":
                mimeType = "image/bmp";
                break;
            default:
                mimeType = "image/png";
        }
        const base64String = imageBuffer.toString("base64");
        return `data:${mimeType};base64,${base64String}`;
    }
    catch (error) {
        logger.error(`Failed to convert image to base64: ${imagePath}`, error);
        // Return an error placeholder or throw error
        return `data:image/png;base64,`; // Empty base64, avoid program crash
    }
};
export const generateMessageId = () => `msg-${randomUUID()}`;
// Add user message
export const addUserMessageToMessages = ({ messages, content, customCommandContent, images, source, id, isMeta, }) => {
    const blocks = [];
    // Create text block with optional source and customCommandContent
    const textBlock = {
        type: "text",
        content,
        stage: "end",
        ...(customCommandContent && { customCommandContent }),
        ...(source && { source }),
    };
    blocks.push(textBlock);
    // If there are images, add image block
    if (images && images.length > 0) {
        const imageUrls = images.map((img) => img.path);
        blocks.push({
            type: "image",
            imageUrls,
        });
    }
    const userMessage = {
        id: id || generateMessageId(),
        role: "user",
        blocks,
        ...(isMeta !== undefined && { isMeta }),
    };
    return [...messages, userMessage];
};
/**
 * Update a user message's content by its ID.
 */
export const updateUserMessageInMessages = (messages, id, params) => {
    return messages.map((msg) => {
        if (msg.id === id && msg.role === "user") {
            const newBlocks = msg.blocks.map((block) => {
                if (block.type === "text") {
                    return {
                        ...block,
                        ...(params.content !== undefined && { content: params.content }),
                        ...(params.customCommandContent !== undefined && {
                            customCommandContent: params.customCommandContent,
                        }),
                        ...(params.source !== undefined && { source: params.source }),
                    };
                }
                return block;
            });
            return {
                ...msg,
                blocks: newBlocks,
                ...(params.isMeta !== undefined && { isMeta: params.isMeta }),
            };
        }
        return msg;
    });
};
// Add assistant message (support one-time addition of answer and tool calls)
export const addAssistantMessageToMessages = (messages, content, toolCalls, usage, additionalFields) => {
    const blocks = [];
    // If there's answer content, add text block
    if (content) {
        blocks.push({ type: "text", content: content });
    }
    // If there are tool calls, add tool blocks
    if (toolCalls && toolCalls.length > 0) {
        toolCalls.forEach((toolCall) => {
            blocks.push({
                type: "tool",
                parameters: toolCall.function.arguments || "",
                result: "",
                id: toolCall.id || "",
                name: toolCall.function?.name || "",
                stage: "start",
            });
        });
    }
    const initialAssistantMessage = {
        id: generateMessageId(),
        role: "assistant",
        blocks,
        usage, // Include usage data if provided
        ...(additionalFields ? { additionalFields: { ...additionalFields } } : {}),
    };
    return [...messages, initialAssistantMessage];
};
/**
 * Add a tool block to a specific message by ID.
 */
export const addToolBlockToMessageInMessages = (messages, messageId, params) => {
    const toolBlockId = randomUUID();
    const newMessages = messages.map((msg) => {
        if (msg.id === messageId) {
            return {
                ...msg,
                blocks: [
                    ...msg.blocks,
                    {
                        type: "tool",
                        id: toolBlockId,
                        name: params.name || "unknown",
                        parameters: params.parameters || "",
                        result: params.result || "",
                        stage: "start",
                        ...params,
                    },
                ],
            };
        }
        return msg;
    });
    return { messages: newMessages, toolBlockId };
};
// Update Tool Block of the last assistant or user message
export const updateToolBlockInMessage = ({ messages, id, messageId, parameters, result, success, error, stage, name, shortResult, startLineNumber, images, compactParams, parametersChunk, isManuallyBackgrounded, }) => {
    const newMessages = [...messages];
    // If messageId is provided, target that specific message
    if (messageId) {
        const messageIndex = newMessages.findIndex((msg) => msg.id === messageId);
        if (messageIndex !== -1) {
            const toolBlockIndex = newMessages[messageIndex].blocks.findIndex((block) => block.type === "tool" && block.id === id);
            if (toolBlockIndex !== -1) {
                const toolBlock = newMessages[messageIndex].blocks[toolBlockIndex];
                if (toolBlock.type === "tool") {
                    if (parameters !== undefined)
                        toolBlock.parameters = parameters;
                    if (result !== undefined)
                        toolBlock.result = result;
                    if (shortResult !== undefined)
                        toolBlock.shortResult = shortResult;
                    if (startLineNumber !== undefined)
                        toolBlock.startLineNumber = startLineNumber;
                    if (images !== undefined)
                        toolBlock.images = images;
                    if (success !== undefined)
                        toolBlock.success = success;
                    if (error !== undefined)
                        toolBlock.error = error;
                    if (stage !== undefined)
                        toolBlock.stage = stage;
                    if (compactParams !== undefined)
                        toolBlock.compactParams = compactParams;
                    if (parametersChunk !== undefined)
                        toolBlock.parametersChunk = parametersChunk;
                    if (isManuallyBackgrounded !== undefined)
                        toolBlock.isManuallyBackgrounded = isManuallyBackgrounded;
                }
            }
        }
        return newMessages;
    }
    // Find the last assistant or user message
    for (let i = newMessages.length - 1; i >= 0; i--) {
        if (newMessages[i].role === "assistant" || newMessages[i].role === "user") {
            const toolBlockIndex = newMessages[i].blocks.findIndex((block) => block.type === "tool" && block.id === id);
            if (toolBlockIndex !== -1) {
                const toolBlock = newMessages[i].blocks[toolBlockIndex];
                if (toolBlock.type === "tool") {
                    if (parameters !== undefined)
                        toolBlock.parameters = parameters;
                    if (result !== undefined)
                        toolBlock.result = result;
                    if (shortResult !== undefined)
                        toolBlock.shortResult = shortResult;
                    if (startLineNumber !== undefined)
                        toolBlock.startLineNumber = startLineNumber;
                    if (images !== undefined)
                        toolBlock.images = images; // Add image data update
                    if (success !== undefined)
                        toolBlock.success = success;
                    if (error !== undefined)
                        toolBlock.error = error;
                    if (stage !== undefined)
                        toolBlock.stage = stage;
                    if (compactParams !== undefined)
                        toolBlock.compactParams = compactParams;
                    if (parametersChunk !== undefined)
                        toolBlock.parametersChunk = parametersChunk;
                    if (isManuallyBackgrounded !== undefined)
                        toolBlock.isManuallyBackgrounded = isManuallyBackgrounded;
                }
                break; // Found and updated, stop searching
            }
            else if (newMessages[i].role === "assistant") {
                // If existing block not found in assistant message, create new one
                // This handles cases where we're streaming tool parameters before execution
                newMessages[i].blocks.push({
                    type: "tool",
                    parameters: parameters,
                    result: result || "",
                    shortResult: shortResult,
                    startLineNumber: startLineNumber,
                    images: images, // Add image data
                    id: id,
                    name: name || "unknown",
                    success: success,
                    error: error,
                    stage: stage ?? "start",
                    compactParams: compactParams,
                    parametersChunk: parametersChunk,
                    isManuallyBackgrounded: isManuallyBackgrounded,
                });
                break; // Created and added, stop searching
            }
        }
    }
    return newMessages;
};
// Add Error Block to the last assistant message
export const addErrorBlockToMessage = ({ messages, error, }) => {
    const newMessages = [...messages];
    // Check if the last message is an assistant message
    const lastMessage = newMessages[newMessages.length - 1];
    if (lastMessage && lastMessage.role === "assistant") {
        // Create a new message object with the error block added
        newMessages[newMessages.length - 1] = {
            ...lastMessage,
            blocks: [
                ...lastMessage.blocks,
                {
                    type: "error",
                    content: error,
                },
            ],
        };
    }
    else {
        // If the last message is not an assistant message, create a new assistant message
        newMessages.push({
            id: generateMessageId(),
            role: "assistant",
            blocks: [
                {
                    type: "error",
                    content: error,
                },
            ],
        });
    }
    return newMessages;
};
// Add bang block to message list
export const addBangMessage = ({ messages, command, }) => {
    const outputMessage = {
        id: generateMessageId(),
        role: "user",
        blocks: [
            {
                type: "bang",
                command,
                output: "",
                stage: "running",
                exitCode: null,
            },
        ],
    };
    return [...messages, outputMessage];
};
// Update output content of bang block
export const updateBangInMessage = ({ messages, command, output, }) => {
    const newMessages = [...messages];
    // Find the last user message with a bang block for this command
    for (let i = newMessages.length - 1; i >= 0; i--) {
        const msg = newMessages[i];
        if (msg.role === "user") {
            const commandBlock = msg.blocks.find((block) => block.type === "bang" &&
                block.command === command &&
                block.stage === "running");
            if (commandBlock && commandBlock.type === "bang") {
                commandBlock.output = output.trim();
                break;
            }
        }
    }
    return newMessages;
};
// Complete bang execution, update exit status
export const completeBangInMessage = ({ messages, command, exitCode, output, }) => {
    const newMessages = [...messages];
    // Find the last user message with a bang block for this command
    for (let i = newMessages.length - 1; i >= 0; i--) {
        const msg = newMessages[i];
        if (msg.role === "user") {
            const commandBlock = msg.blocks.find((block) => block.type === "bang" &&
                block.command === command &&
                block.stage === "running");
            if (commandBlock && commandBlock.type === "bang") {
                commandBlock.stage = "end";
                commandBlock.exitCode = exitCode;
                if (output !== undefined) {
                    commandBlock.output = output.trim();
                }
                break;
            }
        }
    }
    return newMessages;
};
/**
 * Helper to count tool blocks in messages
 */
export function countToolBlocks(messages) {
    let toolCount = 0;
    messages.forEach((msg) => {
        msg.blocks.forEach((block) => {
            if (block.type === "tool") {
                toolCount++;
            }
        });
    });
    return toolCount;
}
/**
 * Remove the last user message from the conversation
 * Used for hook error handling when the user prompt needs to be erased
 */
export const removeLastUserMessage = (messages) => {
    const newMessages = [...messages];
    for (let i = newMessages.length - 1; i >= 0; i--) {
        if (newMessages[i].role === "user") {
            newMessages.splice(i, 1);
            break;
        }
    }
    return newMessages;
};
/**
 * Efficiently clone a message for UI freezing.
 * Clones the message object and its blocks array, but reuses string references.
 */
export function cloneMessage(message) {
    return {
        ...message,
        blocks: message.blocks.map((block) => {
            const clonedBlock = { ...block };
            // Deep clone arrays/objects within blocks if they exist
            if (clonedBlock.type === "tool" && clonedBlock.images) {
                clonedBlock.images = clonedBlock.images.map((img) => ({ ...img }));
            }
            else if (clonedBlock.type === "image" && clonedBlock.imageUrls) {
                clonedBlock.imageUrls = [...clonedBlock.imageUrls];
            }
            else if (clonedBlock.type === "file_history" && clonedBlock.snapshots) {
                clonedBlock.snapshots = clonedBlock.snapshots.map((s) => ({ ...s }));
            }
            return clonedBlock;
        }),
        // Clone additionalFields if it exists
        ...(message.additionalFields
            ? { additionalFields: { ...message.additionalFields } }
            : {}),
    };
}
/**
 * Helper to format tool and token summary
 */
export function formatToolTokenSummary(toolCount, tokens) {
    if (toolCount === 0) {
        return "";
    }
    let summary = `(${toolCount} tools`;
    if (tokens > 0) {
        summary += ` | ${tokens.toLocaleString()} tokens`;
    }
    summary += ")";
    return summary;
}
/**
 * Extracts displayable text from a Message object, handling various block types.
 * Returns the first available content block.
 */
export function getMessageContent(message) {
    // Find first available content block
    const textBlock = message.blocks.find((block) => block.type === "text");
    if (textBlock && "content" in textBlock) {
        return textBlock.content;
    }
    const bangBlock = message.blocks.find((block) => block.type === "bang");
    if (bangBlock && "command" in bangBlock) {
        return `!${bangBlock.command}`;
    }
    const compressBlock = message.blocks.find((block) => block.type === "compress");
    if (compressBlock && "content" in compressBlock) {
        return compressBlock.content;
    }
    return "";
}
export const addNotificationMessageToMessages = ({ messages, taskId, taskType, status, summary, outputFile, }) => {
    const block = {
        type: "task_notification",
        taskId,
        taskType,
        status,
        summary,
        ...(outputFile !== undefined && { outputFile }),
    };
    const notificationMessage = {
        id: generateMessageId(),
        role: "user",
        blocks: [block],
    };
    return [...messages, notificationMessage];
};
