/**
 * OpenAI Provider
 *
 * Implements LLMProvider interface for OpenAI's GPT models.
 * Uses openai SDK for API calls.
 */
import { MODEL_PRICING } from '../types.js';
import { consoleLogger } from '../../../utils/logger.js';
import { extractJson, extractRequiredFieldsFromSchema } from '../../../utils/llm-json-extractor.js';
export class OpenAIProvider {
    constructor(config) {
        this.name = 'openai';
        this.apiKey = config.apiKey;
        this.defaultModel = config.model || 'gpt-5.3';
        this.maxTokens = config.maxTokens || 4096;
        this.temperature = config.temperature ?? 0.3;
        this.baseUrl = config.baseUrl || 'https://api.openai.com/v1';
        this.logger = config.logger || consoleLogger;
    }
    async getClient() {
        if (!this.client) {
            // @ts-ignore - openai is an optional runtime dependency (may not be installed)
            const OpenAI = (await import('openai')).default;
            this.client = new OpenAI({
                apiKey: this.apiKey,
                baseURL: this.baseUrl,
            });
        }
        return this.client;
    }
    async analyze(prompt, options = {}) {
        const startTime = Date.now();
        const model = options.model || this.defaultModel;
        const client = await this.getClient();
        const messages = [];
        if (options.systemPrompt) {
            messages.push({ role: 'system', content: options.systemPrompt });
        }
        messages.push({ role: 'user', content: prompt });
        const response = await client.chat.completions.create({
            model,
            max_tokens: options.maxTokens || this.maxTokens,
            temperature: options.temperature ?? this.temperature,
            messages,
        });
        const content = response.choices[0]?.message?.content || '';
        const usage = {
            inputTokens: response.usage?.prompt_tokens || 0,
            outputTokens: response.usage?.completion_tokens || 0,
            totalTokens: response.usage?.total_tokens || 0,
        };
        return {
            content,
            usage,
            estimatedCost: this.estimateCost(usage.inputTokens, usage.outputTokens, model),
            model,
            durationMs: Date.now() - startTime,
            wasRetry: false,
        };
    }
    async analyzeStructured(prompt, options) {
        // OpenAI supports native JSON mode
        const client = await this.getClient();
        const model = options.model || this.defaultModel;
        const messages = [
            {
                role: 'system',
                content: `You are a helpful assistant that responds in JSON format matching this schema: ${JSON.stringify(options.schema)}`,
            },
            { role: 'user', content: prompt },
        ];
        const response = await client.chat.completions.create({
            model,
            max_tokens: options.maxTokens || this.maxTokens,
            temperature: options.temperature ?? 0.1,
            messages,
            response_format: { type: 'json_object' },
        });
        const content = response.choices[0]?.message?.content || '{}';
        const usage = {
            inputTokens: response.usage?.prompt_tokens || 0,
            outputTokens: response.usage?.completion_tokens || 0,
            totalTokens: response.usage?.total_tokens || 0,
        };
        // Extract required fields from schema for validation
        const requiredFields = extractRequiredFieldsFromSchema(options.schema);
        // Use robust JSON extraction for safety (native JSON mode should work, but belt + suspenders)
        const extraction = extractJson(content, { requiredFields });
        if (extraction.success && extraction.data) {
            return {
                data: extraction.data,
                usage,
                estimatedCost: this.estimateCost(usage.inputTokens, usage.outputTokens, model),
            };
        }
        throw new Error(`Failed to parse structured response: ${extraction.error}`);
    }
    estimateCost(inputTokens, outputTokens, model) {
        const modelId = model || this.defaultModel;
        const pricing = MODEL_PRICING[modelId] || { inputPer1M: 5, outputPer1M: 15 };
        return (inputTokens / 1000000) * pricing.inputPer1M +
            (outputTokens / 1000000) * pricing.outputPer1M;
    }
    async isAvailable() {
        try {
            const status = await this.getStatus();
            return status.available;
        }
        catch {
            return false;
        }
    }
    async getStatus() {
        const startTime = Date.now();
        try {
            const client = await this.getClient();
            await client.models.list();
            return { available: true, latencyMs: Date.now() - startTime };
        }
        catch (error) {
            return { available: false, error: error.message };
        }
    }
}
//# sourceMappingURL=openai-provider.js.map