// src/clients/openai.ts
import OpenAI from 'openai';
import { Message, CompletionOptions, AIError } from '../types';
import { BaseAIClient } from './base';

export class OpenAIClient extends BaseAIClient {
  private client: OpenAI;
  protected apiKey: string;

  constructor(apiKey: string) {
    super();
    this.apiKey = apiKey;
    this.client = new OpenAI({ apiKey });
  }

  async createCompletion(messages: Message[], options: CompletionOptions = {}): Promise<string> {
    try {
      const response = await this.client.chat.completions.create({
        model: options.model || 'gpt-3.5-turbo',
        messages: messages.map(msg => ({
          role: msg.role,
          content: msg.content,
        })),
        temperature: options.temperature ?? 0.7,
        max_tokens: options.maxTokens ?? 4096,
        top_p: options.topP,
        frequency_penalty: options.frequencyPenalty,
        presence_penalty: options.presencePenalty,
        response_format: options.responseFormat === 'json' ? { type: 'json_object' } : undefined,
        stop: options.stop,
        seed: options.seed,
      });

      return response.choices[0]?.message?.content || '';
    } catch (error: any) {
      // Handle OpenAI API errors
      if (error?.status === 401) {
        throw new AIError('Invalid API key', 'openai', 401, error);
      }
      if (error?.status === 429) {
        throw new AIError('Rate limit exceeded', 'openai', 429, error);
      }
      if (error?.status === 400) {
        // Handle specific OpenAI error types
        if (error.code === 'context_length_exceeded') {
          throw new AIError('Maximum context length exceeded', 'openai', 400, error);
        }
        if (error.code === 'invalid_api_key') {
          throw new AIError('Invalid API key', 'openai', 401, error);
        }
        throw new AIError(error.message || 'Bad request', 'openai', 400, error);
      }
      if (error?.status >= 500) {
        throw new AIError('Server error', 'openai', error.status, error);
      }
      // Handle timeout errors
      if (error.code === 'ETIMEDOUT' || error.code === 'ECONNABORTED') {
        throw new AIError('Request timeout', 'openai', 408, error);
      }
      throw new AIError(error.message || 'Unknown error', 'openai', undefined, error);
    }
  }

  async createEmbedding(text: string): Promise<number[]> {
    const response = await this.client.embeddings.create({
      model: 'text-embedding-ada-002',
      input: text,
    });

    return response.data[0].embedding;
  }
}
