import { Capabilities } from "./Capabilities.js";
import { handleOpenAIError } from "./Errors.js";
import { ModelRegistry } from "../../models/ModelRegistry.js";
import { buildUrl } from "./utils.js";
import { logger } from "../../utils/logger.js";
import { fetchWithTimeout } from "../../utils/fetch.js";
import { mapSystemMessages } from "../utils.js";
import { enforceStrictSchema } from "../../schema/strict.js";
export class OpenAIChat {
    providerOrUrl;
    apiKey;
    baseUrl;
    constructor(providerOrUrl, apiKey) {
        this.providerOrUrl = providerOrUrl;
        this.apiKey = apiKey;
        this.baseUrl = typeof providerOrUrl === "string" ? providerOrUrl : providerOrUrl.apiBase();
    }
    async execute(request) {
        const temperature = Capabilities.normalizeTemperature(request.temperature, request.model);
        const isMainOpenAI = this.baseUrl.includes("api.openai.com");
        const supportsDeveloperRole = isMainOpenAI &&
            (typeof this.providerOrUrl === "string"
                ? Capabilities.supportsDeveloperRole(request.model)
                : this.providerOrUrl.capabilities?.supportsDeveloperRole(request.model));
        const { model, messages, tools, temperature: _, max_tokens, response_format, thinking, headers: _headers, requestTimeout: _requestTimeout, signal, prediction, ...rest } = request;
        const mappedMessages = mapSystemMessages(messages, !!supportsDeveloperRole);
        const body = {
            model,
            messages: mappedMessages,
            ...rest
        };
        if (temperature !== undefined && temperature !== null)
            body.temperature = temperature;
        if (max_tokens) {
            if (Capabilities.needsMaxCompletionTokens(request.model)) {
                body.max_completion_tokens = max_tokens;
            }
            else {
                body.max_tokens = max_tokens;
            }
        }
        if (tools) {
            body.tools = tools.map((tool) => {
                if (tool.function.strict === true) {
                    return {
                        ...tool,
                        function: {
                            ...tool.function,
                            parameters: enforceStrictSchema(tool.function.parameters)
                        }
                    };
                }
                return tool;
            });
        }
        if (prediction) {
            body.prediction = {
                type: "content",
                content: prediction
            };
        }
        if (response_format) {
            if (response_format.type === "json_schema" &&
                response_format.json_schema?.schema &&
                response_format.json_schema.strict === true) {
                body.response_format = {
                    type: "json_schema",
                    json_schema: {
                        ...response_format.json_schema,
                        schema: enforceStrictSchema(response_format.json_schema.schema)
                    }
                };
            }
            else {
                body.response_format = response_format;
            }
        }
        if (thinking?.effort && thinking.effort !== "none") {
            body.reasoning_effort = thinking.effort;
        }
        const url = buildUrl(this.baseUrl, "/chat/completions");
        logger.logRequest("OpenAI", "POST", url, body);
        const response = await fetchWithTimeout(url, {
            method: "POST",
            headers: {
                Authorization: `Bearer ${this.apiKey}`,
                "Content-Type": "application/json",
                ...request.headers
            },
            body: JSON.stringify(body),
            signal: signal
        }, request.requestTimeout);
        if (!response.ok) {
            await handleOpenAIError(response, request.model);
        }
        const json = (await response.json());
        logger.logResponse("OpenAI", response.status, response.statusText, json);
        const message = json.choices[0]?.message;
        const content = message?.content ?? null;
        const tool_calls = message?.tool_calls;
        const reasoningText = message?.reasoning_content || null;
        const usage = json.usage
            ? {
                input_tokens: json.usage.prompt_tokens,
                output_tokens: json.usage.completion_tokens,
                total_tokens: json.usage.total_tokens,
                cached_tokens: json.usage.prompt_tokens_details?.cached_tokens,
                reasoning_tokens: json.usage.completion_tokens_details?.reasoning_tokens
            }
            : undefined;
        const thinkingResult = reasoningText || usage?.reasoning_tokens
            ? {
                text: reasoningText || undefined,
                tokens: usage?.reasoning_tokens
            }
            : undefined;
        if (!content && !tool_calls) {
            throw new Error("OpenAI returned empty response");
        }
        const calculatedUsage = usage ? ModelRegistry.calculateCost(usage, model, "openai") : undefined;
        return {
            content,
            tool_calls,
            usage: calculatedUsage,
            thinking: thinkingResult,
            reasoning: reasoningText
        };
    }
}
