/**
 * OpenAI Provider - Handles embedding generation
 */

export const EMBEDDING_MODEL = 'text-embedding-3-small';
export const EMBEDDING_DIMENSIONS = 1536;
const MAX_BATCH_SIZE = 2048;

export class OpenAIProvider {
  constructor({ apiKey, baseUrl = 'https://api.openai.com/v1', timeoutMs = 60000 }) {
    if (!apiKey) {
      throw new Error('OpenAI API key is required');
    }
    this.apiKey = apiKey;
    this.baseUrl = baseUrl;
    this.timeoutMs = timeoutMs;
  }

  async embed(input, options = {}) {
    const texts = Array.isArray(input) ? input : [input];
    const model = options.model || EMBEDDING_MODEL;
    const dimensions = options.dimensions || EMBEDDING_DIMENSIONS;
    const timeoutMs = options.timeoutMs || this.timeoutMs;

    if (texts.length > MAX_BATCH_SIZE) {
      throw new Error(`Batch size ${texts.length} exceeds max of ${MAX_BATCH_SIZE}`);
    }

    const started = Date.now();
    const cleanedTexts = texts.map((t) =>
      (t || '').replace(/\n+/g, ' ').trim().substring(0, 8000)
    );

    const body = { input: cleanedTexts, model, encoding_format: 'float' };
    if (model.includes('embedding-3')) body.dimensions = dimensions;

    const response = await this._makeRequest('embeddings', body, timeoutMs);
    const sortedData = response.data.sort((a, b) => a.index - b.index);

    return {
      embeddings: sortedData.map((item) => item.embedding),
      usage: {
        promptTokens: response.usage?.prompt_tokens || 0,
        totalTokens: response.usage?.total_tokens || 0,
      },
      model: response.model || model,
      latencyMs: Date.now() - started,
    };
  }

  async *embedBatchChunked(texts, options = {}) {
    const batchSize = Math.min(options.batchSize || 100, MAX_BATCH_SIZE);
    for (let i = 0; i < texts.length; i += batchSize) {
      const chunk = texts.slice(i, i + batchSize);
      const result = await this.embed(chunk, options);
      yield {
        embeddings: result.embeddings,
        startIndex: i,
        endIndex: i + chunk.length,
        usage: result.usage,
        model: result.model,
        latencyMs: result.latencyMs,
      };
    }
  }

  async _makeRequest(endpoint, body, timeoutMs) {
    const controller = new AbortController();
    const timeout = setTimeout(() => controller.abort(), timeoutMs);
    try {
      const response = await fetch(`${this.baseUrl}/${endpoint}`, {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
          'Authorization': `Bearer ${this.apiKey}`,
        },
        body: JSON.stringify(body),
        signal: controller.signal,
      });
      if (!response.ok) {
        const error = await response.json().catch(() => ({}));
        throw new Error(error.error?.message || `OpenAI API error: ${response.status}`);
      }
      return response.json();
    } finally {
      clearTimeout(timeout);
    }
  }
}
