/**
 * LLM Adapter Factory
 * Creates LLM adapter instances based on model configuration
 */

import type { LLMAdapter } from '../../types'
import type { ModelConfig, OpenRouterOptions, GoogleOptions, AnthropicOptions } from '../../model-info'
import { OpenAIAdapter, isOpenAIModel } from './openai'
import { OpenRouterAdapter, isOpenRouterModel } from './openrouter'
import { GoogleAdapter, isGoogleModel } from './google'
import { AnthropicAdapter, isAnthropicModel } from './anthropic'
import { MockAdapter, isMockModel } from '../mock-adapter'
import { UniversalLLMAdapter } from '../universal-adapter'
import type { AIUsageCollector } from '../../../../../operations/observability/ai-usage-collector'
import type { AICallContext, BaseAICall } from '../../../../../operations/observability/types'

/**
 * Resolve API key at runtime
 * Handles the case where env vars weren't loaded at module import time
 * (e.g., in Docker when dotenv runs after module imports)
 *
 * @param configApiKey - API key from config (may be placeholder)
 * @param provider - Provider name to determine which env var to check
 * @returns Resolved API key
 */
function resolveApiKey(configApiKey: string, provider: string): string {
  // If the config has a real API key (not a placeholder), use it
  const isPlaceholder =
    !configApiKey ||
    configApiKey.startsWith('placeholder-') ||
    configApiKey.includes('placeholder') ||
    configApiKey.includes('test-api-key')
  if (!isPlaceholder) {
    return configApiKey
  }

  // Resolve from environment at runtime based on provider
  const envVarMap: Record<string, string | undefined> = {
    anthropic: process.env.ANTHROPIC_API_KEY,
    openai: process.env.OPENAI_API_KEY,
    openrouter: process.env.OPENROUTER_API_KEY,
    google: process.env.GOOGLE_AI_STUDIO_KEY
  }

  const envKey = envVarMap[provider]
  if (envKey) {
    return envKey
  }

  // Fallback to config value (will fail at API call time with auth error)
  return configApiKey
}

/**
 * Create base adapter based on model configuration
 * Routes to the appropriate provider-specific adapter
 *
 * @param config - Model configuration from resource definition
 * @returns Base LLM adapter instance (without observability wrapper)
 */
function createBaseAdapter(config: ModelConfig): LLMAdapter {
  const { model, provider, modelOptions } = config
  const apiKey = resolveApiKey(config.apiKey, provider)

  // OpenAI models
  if (isOpenAIModel(model, provider)) {
    return new OpenAIAdapter({
      apiKey,
      model,
      modelOptions // Flat structure - no vendor namespace needed
    })
  }

  // OpenRouter models (access to 400+ models via single API)
  if (isOpenRouterModel(model)) {
    return new OpenRouterAdapter({
      apiKey,
      model,
      modelOptions: modelOptions as OpenRouterOptions
    })
  }

  // Google models (direct SDK access via @google/genai)
  if (isGoogleModel(model, provider)) {
    return new GoogleAdapter({
      apiKey,
      model,
      modelOptions: modelOptions as GoogleOptions
    })
  }

  // Anthropic models (direct SDK access via @anthropic-ai/sdk)
  if (isAnthropicModel(model, provider)) {
    return new AnthropicAdapter({
      apiKey,
      model,
      modelOptions: modelOptions as AnthropicOptions
    })
  }

  // Mock provider (for testing)
  if (isMockModel(model, provider)) {
    return new MockAdapter()
  }

  throw new Error(`Unsupported model: ${model} with provider: ${provider}`)
}

/**
 * Create LLM adapter based on model configuration
 * Creates a stateless adapter instance for the execution
 * Automatically wraps with UniversalLLMAdapter for reliability and observability
 *
 * @param config - Model configuration from resource definition (includes model identifier)
 * @param aiUsageCollector - Optional collector for tracking AI usage
 * @param callType - Optional call type discriminator for observability
 * @param context - Optional typed context for observability
 * @returns Stateless LLM adapter instance (wrapped with UniversalLLMAdapter)
 */
export function createLLMAdapter(
  config: ModelConfig,
  aiUsageCollector?: AIUsageCollector,
  callType?: BaseAICall['callType'],
  context?: AICallContext
): LLMAdapter {
  const baseAdapter = createBaseAdapter(config)

  // Wrap with UniversalLLMAdapter for automatic retry, circuit breaker, and observability
  return new UniversalLLMAdapter(baseAdapter, config.model, aiUsageCollector, callType, context)
}
