/**
 * OpenAI Agent streaming helpers.
 *
 * These utilities are OpenAI specific, so they live inside the OpenAI plugin bundle
 * and can be imported by consumers who choose to use the bundled chat agent helpers.
 */
const safeSerialize = (value) => {
    if (value === undefined || value === null)
        return '';
    if (typeof value === 'string')
        return value;
    try {
        return JSON.stringify(value);
    }
    catch (error) {
        console.error('Failed to serialize value', error);
        return String(value);
    }
};
export const streamOpenAIResponse = async (response, handlers) => {
    const stream = response.toStream();
    let messageOutputHandled = false;
    const functionCallBuffers = new Map();
    for await (const event of stream) {
        console.log('Stream event:', event.type, event);
        if (event.type === 'raw_model_stream_event') {
            const data = event.data;
            const dataType = typeof data.type === 'string' ? data.type : undefined;
            if (dataType === 'model' && data.event && typeof data.event === 'object') {
                const rawEvent = data.event;
                const eventType = typeof rawEvent.type === 'string' ? rawEvent.type : undefined;
                if (eventType && rawEvent.item && typeof rawEvent.item === 'object') {
                    const isReasoningStart = eventType === 'response.output_item.added' || eventType === 'response.output_item.created';
                    const isReasoningDone = eventType === 'response.output_item.done';
                    const itemType = rawEvent.item.type;
                    if ((isReasoningStart || isReasoningDone) && itemType === 'reasoning') {
                        const itemRecord = rawEvent.item;
                        const now = Date.now();
                        const reasoningId = typeof itemRecord.id === 'string' && itemRecord.id.length > 0
                            ? itemRecord.id
                            : `reasoning-${now}`;
                        const summary = itemRecord.summary;
                        const phase = isReasoningDone ? 'completed' : 'started';
                        if (handlers.onReasoningEvent) {
                            const payload = {
                                id: reasoningId,
                                phase,
                                summary,
                                rawEvent: data,
                                timestamp: now
                            };
                            await handlers.onReasoningEvent(payload);
                        }
                        continue;
                    }
                    if (eventType.startsWith('response.output_item.') && itemType === 'message' && handlers.onMessageEvent) {
                        const itemRecord = rawEvent.item;
                        const now = Date.now();
                        const messageId = typeof itemRecord.id === 'string' && itemRecord.id.length > 0
                            ? itemRecord.id
                            : `message-${now}`;
                        const role = typeof itemRecord.role === 'string' && itemRecord.role.length > 0
                            ? itemRecord.role
                            : undefined;
                        const status = typeof itemRecord.status === 'string' && itemRecord.status.length > 0
                            ? itemRecord.status
                            : undefined;
                        const content = rawEvent.delta ?? itemRecord.content ?? rawEvent.content;
                        const phase = eventType.endsWith('.done') ? 'completed' : 'started';
                        const payload = {
                            id: messageId,
                            role,
                            status,
                            phase,
                            content,
                            rawEvent: data,
                            timestamp: now
                        };
                        await handlers.onMessageEvent(payload);
                        continue;
                    }
                }
                if (eventType && (eventType.startsWith('response.mcp_call') || eventType.startsWith('response.mcp_list_tools'))) {
                    const eventGroup = eventType.startsWith('response.mcp_call') ? 'call' : 'list_tools';
                    const callIdFromEvent = typeof rawEvent.item_id === 'string' && rawEvent.item_id.length > 0
                        ? rawEvent.item_id
                        : typeof rawEvent.id === 'string' && rawEvent.id.length > 0
                            ? rawEvent.id
                            : undefined;
                    const nameCandidates = [
                        rawEvent.tool_name,
                        rawEvent.toolName,
                        rawEvent.name,
                        eventGroup === 'call' ? 'mcp_call' : 'mcp_list_tools'
                    ];
                    const toolName = nameCandidates.find((candidate) => typeof candidate === 'string' && candidate.length > 0)
                        ?? (eventGroup === 'call' ? 'mcp_call' : 'mcp_list_tools');
                    const callId = callIdFromEvent ?? `${toolName}-${Date.now()}`;
                    const existingRecord = functionCallBuffers.get(callId);
                    const argsBaseline = existingRecord?.args ?? '';
                    let nextArgs = argsBaseline;
                    if (eventType.endsWith('.arguments.delta')) {
                        const deltaPayload = rawEvent.delta;
                        const deltaText = typeof deltaPayload === 'string'
                            ? deltaPayload
                            : safeSerialize(deltaPayload);
                        nextArgs = argsBaseline + deltaText;
                    }
                    else if (rawEvent.arguments !== undefined && nextArgs.length === 0) {
                        nextArgs = safeSerialize(rawEvent.arguments);
                    }
                    functionCallBuffers.set(callId, {
                        name: toolName,
                        args: nextArgs
                    });
                    const status = eventType.endsWith('.completed')
                        ? 'completed'
                        : eventType.endsWith('.failed')
                            ? 'failed'
                            : 'in_progress';
                    let errorMessage;
                    const rawError = rawEvent.error;
                    if (typeof rawError === 'string' && rawError.length > 0) {
                        errorMessage = rawError;
                    }
                    else if (rawError && typeof rawError === 'object') {
                        const errorRecord = rawError;
                        if (typeof errorRecord.message === 'string' && errorRecord.message.length > 0) {
                            errorMessage = errorRecord.message;
                        }
                        else if (typeof errorRecord.detail === 'string' && errorRecord.detail.length > 0) {
                            errorMessage = errorRecord.detail;
                        }
                        else if (typeof errorRecord.code === 'string' && errorRecord.code.length > 0) {
                            errorMessage = errorRecord.code;
                        }
                    }
                    if (!errorMessage && typeof rawEvent.dependency === 'string' && rawEvent.dependency.length > 0) {
                        errorMessage = `Dependency failed: ${rawEvent.dependency}`;
                    }
                    await handlers.onToolCall(toolName, nextArgs, {
                        callId,
                        status,
                        ...(errorMessage ? { error: errorMessage } : {}),
                        timestamp: Date.now(),
                        rawItem: data
                    });
                    continue;
                }
            }
            if (dataType && dataType.includes('function_call')) {
                const providerData = (data.providerData ?? {});
                const nameFromProvider = typeof providerData.name === 'string' && providerData.name.length > 0
                    ? providerData.name
                    : undefined;
                const nameFromData = typeof data.name === 'string' && data.name.length > 0
                    ? data.name
                    : undefined;
                const functionName = nameFromProvider ?? nameFromData ?? 'unknown_tool';
                const callIdCandidates = [
                    providerData.call_id,
                    providerData.callId,
                    providerData.id,
                    data.call_id,
                    data.callId,
                    data.id
                ];
                const callId = callIdCandidates.find((candidate) => typeof candidate === 'string' && candidate.length > 0);
                const bufferKey = callId ?? functionName;
                const extractArgsFragment = () => {
                    const delta = data.delta;
                    if (typeof delta === 'string')
                        return delta;
                    if (delta && typeof delta === 'object' && typeof delta.arguments === 'string') {
                        return delta.arguments ?? '';
                    }
                    if (typeof data.arguments === 'string')
                        return data.arguments;
                    if (data.arguments && typeof data.arguments === 'object') {
                        return safeSerialize(data.arguments);
                    }
                    const deltaArguments = delta?.arguments;
                    if (typeof deltaArguments === 'string')
                        return deltaArguments;
                    if (deltaArguments && typeof deltaArguments === 'object') {
                        return safeSerialize(deltaArguments);
                    }
                    return '';
                };
                const argsFragment = extractArgsFragment();
                const previousRecord = functionCallBuffers.get(bufferKey);
                const previousArgs = previousRecord?.args ?? '';
                const nextArgs = argsFragment ? previousArgs + argsFragment : previousArgs;
                if (!previousRecord || argsFragment) {
                    functionCallBuffers.set(bufferKey, { name: functionName, args: nextArgs });
                    await handlers.onToolCall(functionName, nextArgs, {
                        callId: bufferKey,
                        status: 'in_progress',
                        timestamp: Date.now(),
                        rawItem: data
                    });
                }
                continue;
            }
            if (dataType === 'output_text_delta') {
                const delta = typeof data.delta === 'string' ? data.delta : undefined;
                if (delta) {
                    await handlers.onOutputTextDelta(delta);
                    continue;
                }
            }
        }
        if (event.type === 'run_item_stream_event') {
            if (event.name === 'message_output_created') {
                if (messageOutputHandled) {
                    continue;
                }
                messageOutputHandled = true;
                const message = event.item.toJSON().rawItem;
                if (!message) {
                    throw new Error('No message content found in event:' + JSON.stringify(event));
                }
                if (message.type === 'message' && message.role === 'assistant') {
                    const firstContent = message.content[0];
                    if (firstContent.type === 'output_text' && 'text' in firstContent) {
                        if (!response.finalOutput) {
                            // throw new Error('No final output found in response')
                        }
                        const finalOutput = response.finalOutput;
                        if (handlers.onFinalOutputAccessed) {
                            await handlers.onFinalOutputAccessed(finalOutput);
                        }
                        await handlers.onMessageOutputCreated(finalOutput);
                    }
                }
                continue;
            }
        }
        if (event.type === 'run_item_stream_event') {
            if (event.name === 'tool_called' || event.name === 'tool_approval_requested') {
                const rawItem = event.item.toJSON().rawItem;
                if (rawItem && typeof rawItem === 'object') {
                    const rawRecord = rawItem;
                    const rawError = rawRecord.error;
                    const errorMessage = typeof rawError === 'string'
                        ? rawError
                        : (typeof rawError === 'object' && rawError !== null && 'message' in rawError
                            ? String(rawError.message ?? '')
                            : undefined);
                    const rawName = typeof rawRecord.name === 'string' && rawRecord.name.length > 0
                        ? rawRecord.name
                        : undefined;
                    const eventName = typeof event.item.name === 'string'
                        ? event.item.name
                        : undefined;
                    const name = rawName ?? eventName ?? 'unknown_tool';
                    const callIdFromRaw = typeof rawRecord.callId === 'string' && rawRecord.callId.length > 0
                        ? rawRecord.callId
                        : undefined;
                    const callIdFromId = typeof rawRecord.id === 'string' && rawRecord.id.length > 0
                        ? rawRecord.id
                        : undefined;
                    const callIdFromEvent = typeof event.item.id === 'string'
                        ? event.item.id
                        : undefined;
                    const callId = callIdFromRaw ?? callIdFromId ?? callIdFromEvent ?? `${name}-${Date.now()}`;
                    const argsSource = rawRecord.arguments ?? rawRecord.input ?? rawRecord.providerData;
                    const argsValue = safeSerialize(argsSource);
                    const metadata = {
                        callId,
                        status: typeof rawRecord.status === 'string' ? rawRecord.status : undefined,
                        error: errorMessage,
                        timestamp: Date.now(),
                        rawItem
                    };
                    await handlers.onToolCall(name, argsValue, metadata);
                }
                continue;
            }
            if (event.name === 'tool_output') {
                const rawItem = event.item.toJSON().rawItem;
                if (rawItem && rawItem.type === 'function_call_result') {
                    const rawError = rawItem.error;
                    const errorMessage = typeof rawError === 'string'
                        ? rawError
                        : (typeof rawError === 'object' && rawError !== null && 'message' in rawError
                            ? String(rawError.message ?? '')
                            : undefined);
                    const metadata = {
                        callId: rawItem.callId ?? rawItem.id ?? `${rawItem.name}-${Date.now()}`,
                        status: rawItem.status,
                        error: errorMessage,
                        timestamp: Date.now(),
                        rawItem
                    };
                    let outputValue = '';
                    if (typeof rawItem.output === 'string') {
                        outputValue = rawItem.output;
                    }
                    else if (rawItem.output?.type === 'text' && typeof rawItem.output.text === 'string') {
                        outputValue = rawItem.output.text;
                    }
                    else {
                        outputValue = JSON.stringify(rawItem.output ?? '');
                    }
                    await handlers.onToolOutput(rawItem.name ?? 'unknown_tool', outputValue, metadata);
                    continue;
                }
            }
            if (event.name && (event.name.includes('tool') || event.name.includes('function')) &&
                event.name !== 'tool_output') {
                const rawItem = event.item.toJSON().rawItem;
                if (rawItem && typeof rawItem === 'object') {
                    const rawRecord = rawItem;
                    const name = typeof rawRecord.name === 'string' ? rawRecord.name : 'unknown_tool';
                    const callId = typeof rawRecord.callId === 'string' ? rawRecord.callId :
                        typeof rawRecord.id === 'string' ? rawRecord.id :
                            `${name}-${Date.now()}`;
                    await handlers.onToolCall(name, '', {
                        callId,
                        status: 'in_progress',
                        timestamp: Date.now(),
                        rawItem
                    });
                }
                continue;
            }
        }
    }
};
