import { RunState } from '@openai/agents';
import { ProviderPlugin } from '../../lib/providers';
import { OpenAIChatAgent, setAttachmentToolDefinitionsResolver } from './chatAgent';
import { openaiModelDefinition } from './modelDefinition';
import { streamOpenAIResponse } from './streamResponse';
import { resumeOpenAIChat, runOpenAIChat } from './runner';
const normalizeThread = (input) => {
    const rawThread = input.metadata?.thread;
    if (!rawThread || !Array.isArray(rawThread)) {
        return undefined;
    }
    return rawThread;
};
const toStreamHandlers = (callbacks) => ({
    onOutputTextDelta: async (delta) => {
        await callbacks.onOutputTextDelta?.(delta);
    },
    onMessageOutputCreated: async (finalOutput) => {
        await callbacks.onMessageOutputCreated?.(finalOutput);
    },
    onFinalOutputAccessed: async (finalOutput) => {
        await callbacks.onFinalOutputAccessed?.(finalOutput);
    },
    onToolCall: async (name, args, metadata) => {
        await callbacks.onToolCall?.(name, args, metadata);
    },
    onToolOutput: async (name, output, metadata) => {
        await callbacks.onToolOutput?.(name, output, metadata);
    },
    onReasoningEvent: callbacks.onReasoningEvent
        ? async (event) => { await callbacks.onReasoningEvent?.(event); }
        : undefined,
    onMessageEvent: callbacks.onMessageEvent
        ? async (event) => { await callbacks.onMessageEvent?.(event); }
        : undefined
});
const buildRunResult = (response) => {
    const state = response.state;
    const interruptions = state?.getInterruptions?.() ?? response.interruptions ?? [];
    const finalOutput = typeof response.finalOutput === 'string' ? response.finalOutput : undefined;
    return {
        finalOutput,
        interruptions,
        state,
        rawResponse: response
    };
};
const assertOpenAIChatAgent = (agent) => {
    if (agent instanceof OpenAIChatAgent) {
        return agent;
    }
    throw new Error('OpenAI provider received incompatible agent instance');
};
const toContext = (context) => {
    if (!context || typeof context !== 'object') {
        return undefined;
    }
    return context;
};
const ensureAbortPair = (input) => {
    const abortController = input.abortController ?? new AbortController();
    const signal = input.signal ?? abortController.signal;
    return { abortController, signal };
};
const isAbortError = (error) => error instanceof Error && error.name === 'AbortError';
const createRunHandle = (executor, abortController, signal) => {
    const runPromise = executor();
    return {
        cancel: async () => {
            if (!signal.aborted) {
                abortController.abort();
            }
            try {
                await runPromise;
            }
            catch (error) {
                if (!isAbortError(error)) {
                    throw error;
                }
            }
        },
        completed: runPromise,
        signal
    };
};
const runOpenAI = async (action) => {
    const { response } = await action();
    return buildRunResult(response);
};
const openaiProvider = {
    metadata: {
        kind: 'openai',
        name: 'OpenAI',
        supportsTools: true,
        supportsApprovals: true,
        supportsStreaming: true,
        isImplemented: true
    },
    createAgent: (options) => new OpenAIChatAgent(options),
    startRun: async (agent, input, callbacks) => {
        const openaiAgent = assertOpenAIChatAgent(agent);
        const { abortController, signal } = ensureAbortPair(input);
        const handlers = toStreamHandlers(callbacks);
        const baseAgent = openaiAgent;
        const context = toContext(input.context);
        const thread = normalizeThread(input) ?? [];
        return createRunHandle(() => runOpenAI(() => runOpenAIChat({
            agent: baseAgent,
            thread,
            context,
            signal,
            handlers,
            chatOptions: openaiAgent.options
        })), abortController, signal);
    },
    resumeRun: async (agent, input, callbacks) => {
        const openaiAgent = assertOpenAIChatAgent(agent);
        const { abortController, signal } = ensureAbortPair(input);
        const handlers = toStreamHandlers(callbacks);
        const baseAgent = openaiAgent;
        const context = toContext(input.context);
        const state = input.state;
        return createRunHandle(() => runOpenAI(() => resumeOpenAIChat({
            agent: baseAgent,
            state,
            context,
            signal,
            handlers,
            chatOptions: openaiAgent.options
        })), abortController, signal);
    },
    deserializeRunState: async (agent, serialized) => {
        const openaiAgent = assertOpenAIChatAgent(agent);
        const baseAgent = openaiAgent;
        const state = await RunState.fromString(baseAgent, serialized);
        return state;
    }
    // resumeRun 
};
export class OpenAIProvider extends ProviderPlugin {
    constructor() {
        super(...arguments);
        Object.defineProperty(this, "id", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: 'openai'
        });
    }
    async createProvider() {
        return openaiProvider;
    }
    async getModelDefinition() {
        return openaiModelDefinition;
    }
    async getStreamHandler() {
        return async (response, callbacks) => {
            await streamOpenAIResponse(response, callbacks);
        };
    }
    configureAttachmentTools(resolve) {
        setAttachmentToolDefinitionsResolver(resolve);
    }
}
