/**
 * LLM Provider abstraction for multi-provider support via OpenRouter
 */

import type { BaseMessage } from "@langchain/core/messages"
import { ChatOpenAI } from "@langchain/openai"

export interface LLMConfig {
  temperature?: number
  maxTokens?: number
  topP?: number
  apiKey?: string
}

export interface LLMProvider {
  name: string
  generateResponse(messages: BaseMessage[], config?: LLMConfig): Promise<string>
  streamResponse(messages: BaseMessage[], config?: LLMConfig): AsyncIterable<string>
}

/**
 * OpenRouter provider - supports multiple LLM models through a single API
 */
export class OpenRouterProvider implements LLMProvider {
  name = 'openrouter'
  private modelName: string
  private apiKey: string
  private baseURL = 'https://openrouter.ai/api/v1'

  constructor(modelName: string, apiKey: string) {
    this.modelName = modelName
    this.apiKey = apiKey
  }

  async generateResponse(messages: BaseMessage[], config?: LLMConfig): Promise<string> {
    const llm = new ChatOpenAI({
      model: this.modelName,
      temperature: config?.temperature ?? 0.7,
      maxTokens: config?.maxTokens ?? 2048,
      topP: config?.topP ?? 1,
      apiKey: this.apiKey,
      configuration: {
        baseURL: this.baseURL,
      },
    })

    const response = await llm.invoke(messages)
    return response.content as string
  }

  async *streamResponse(messages: BaseMessage[], config?: LLMConfig): AsyncIterable<string> {
    const llm = new ChatOpenAI({
      model: this.modelName,
      temperature: config?.temperature ?? 0.7,
      maxTokens: config?.maxTokens ?? 2048,
      topP: config?.topP ?? 1,
      apiKey: this.apiKey,
      streaming: true,
      configuration: {
        baseURL: this.baseURL,
      },
    })

    const stream = await llm.stream(messages)
    for await (const chunk of stream) {
      if (chunk.content) {
        yield chunk.content as string
      }
    }
  }
}

/**
 * Common model configurations for OpenRouter
 */
export const OPENROUTER_MODELS = {
  // OpenAI
  'gpt-4o': 'openai/gpt-4o',
  'gpt-4o-mini': 'openai/gpt-4o-mini',
  'gpt-4-turbo': 'openai/gpt-4-turbo',
  
  // Anthropic
  'claude-3.5-sonnet': 'anthropic/claude-3.5-sonnet',
  'claude-3-opus': 'anthropic/claude-3-opus',
  'claude-3-haiku': 'anthropic/claude-3-haiku',
  
  // Google
  'gemini-pro': 'google/gemini-pro',
  'gemini-pro-1.5': 'google/gemini-pro-1.5',
  
  // Meta
  'llama-3.1-70b': 'meta-llama/llama-3.1-70b-instruct',
  'llama-3.1-8b': 'meta-llama/llama-3.1-8b-instruct',
  
  // Mistral
  'mistral-large': 'mistralai/mistral-large',
  'mistral-medium': 'mistralai/mistral-medium',
  
  // Other
  'deepseek-v3': 'deepseek/deepseek-chat',
  'qwen-2.5-72b': 'qwen/qwen-2.5-72b-instruct',
} as const

export type OpenRouterModelId = keyof typeof OPENROUTER_MODELS

/**
 * Create an LLM provider instance
 */
export function createLLMProvider(
  provider: 'openrouter',
  modelName: string,
  apiKey: string
): LLMProvider {
  if (provider === 'openrouter') {
    return new OpenRouterProvider(modelName, apiKey)
  }
  throw new Error(`Unsupported provider: ${provider}`)
}

