import { DEFAULT_EMBEDDING_MODEL } from "../constants.js";
export class SimpleOpenAIClient {
    apiKey;
    defaultModel;
    constructor(opts) {
        const apiKey = opts?.apiKey ?? process.env.OPENAI_API_KEY;
        if (!apiKey) {
            throw new Error("OPENAI_API_KEY not found. Pass apiKey option or set OPENAI_API_KEY environment variable.");
        }
        this.apiKey = apiKey;
        this.defaultModel = opts?.model ?? "gpt-4o-mini";
    }
    async text(config) {
        const model = config.model || this.defaultModel;
        const body = this.buildRequestBody(config, model);
        try {
            const response = await fetch("https://api.openai.com/v1/chat/completions", {
                method: "POST",
                headers: {
                    "Content-Type": "application/json",
                    "Authorization": `Bearer ${this.apiKey}`,
                },
                body: JSON.stringify(body),
                signal: config.abortSignal,
            });
            if (!response.ok) {
                const errorText = await response.text();
                return { success: false, error: `OpenAI API error (${response.status}): ${errorText}` };
            }
            const data = await response.json();
            const choice = data.choices?.[0];
            const output = choice?.message?.content || "";
            return {
                success: true,
                value: {
                    output,
                    toolCalls: this.extractToolCalls(choice),
                    usage: this.extractUsage(data),
                    cost: { inputCost: 0, outputCost: 0, totalCost: 0, currency: "USD" },
                    model,
                },
            };
        }
        catch (error) {
            const message = error instanceof Error ? error.message : String(error);
            return { success: false, error: message };
        }
    }
    async *textStream(config) {
        const result = await this.text(config);
        if (result.success) {
            yield { type: "done", result: result.value };
        }
        else {
            yield { type: "error", error: result.error };
        }
    }
    async embed(input, config) {
        const apiKey = config?.openAiApiKey ?? this.apiKey;
        const model = config?.model ?? DEFAULT_EMBEDDING_MODEL;
        const body = { model, input };
        if (config?.dimensions)
            body.dimensions = config.dimensions;
        try {
            const response = await fetch("https://api.openai.com/v1/embeddings", {
                method: "POST",
                headers: {
                    "Content-Type": "application/json",
                    Authorization: `Bearer ${apiKey}`,
                },
                body: JSON.stringify(body),
            });
            if (!response.ok) {
                const errorText = await response.text();
                return {
                    success: false,
                    error: `OpenAI embeddings error (${response.status}): ${errorText}`,
                };
            }
            const data = await response.json();
            const embeddings = (data.data || []).map((d) => d.embedding);
            return { success: true, value: { embeddings, model } };
        }
        catch (error) {
            const message = error instanceof Error ? error.message : String(error);
            return { success: false, error: message };
        }
    }
    buildRequestBody(config, model) {
        const messages = (config.messages || []).map((m) => {
            const json = typeof m.toJSON === "function" ? m.toJSON() : m;
            return {
                role: json.role,
                content: json.content,
                ...(json.tool_calls ? { tool_calls: json.tool_calls } : {}),
                ...(json.tool_call_id ? { tool_call_id: json.tool_call_id } : {}),
            };
        });
        const body = { model, messages };
        if (config.maxTokens)
            body.max_tokens = config.maxTokens;
        if (config.temperature !== undefined)
            body.temperature = config.temperature;
        if (config.tools && config.tools.length > 0) {
            body.tools = config.tools.map((t) => ({
                type: "function",
                function: {
                    name: t.name,
                    description: t.description || "",
                    parameters: t.schema.toJSONSchema(),
                },
            }));
        }
        if (config.responseFormat) {
            body.response_format = {
                type: "json_schema",
                json_schema: {
                    name: "response",
                    schema: config.responseFormat.toJSONSchema(),
                    strict: true,
                },
            };
        }
        return body;
    }
    extractToolCalls(choice) {
        return (choice?.message?.tool_calls || []).map((tc) => {
            let args = {};
            try {
                args = JSON.parse(tc.function.arguments);
            }
            catch { }
            return { id: tc.id, name: tc.function.name, arguments: args };
        });
    }
    extractUsage(data) {
        if (data.usage) {
            return {
                inputTokens: data.usage.prompt_tokens || 0,
                outputTokens: data.usage.completion_tokens || 0,
                cachedInputTokens: 0,
                totalTokens: data.usage.total_tokens || 0,
            };
        }
        return { inputTokens: 0, outputTokens: 0, cachedInputTokens: 0, totalTokens: 0 };
    }
}
