/**
 * LLM provider factory.
 *
 * Creates an LLMProvider based on LLM_PROVIDER env var.
 * Auto-detects provider from endpoint/key env vars if not explicitly set.
 *
 * Provider selection priority:
 *   LLM_PROVIDER env var > auto-detect from endpoint/key env vars > openai
 *
 * To use a fallback chain (e.g. perplexity → openai → gemini), set:
 *   LLM_PROVIDER=fallback
 *   LLM_FALLBACK_ORDER=perplexity,openai,gemini  (default if unset)
 */

import { AzureOpenAIProvider } from './providers/azure-openai.js';
import { FallbackLLMProvider } from './providers/fallback.js';
import { GeminiProvider } from './providers/gemini.js';
import { MockLLMProvider } from './providers/mock.js';
import { OpenAIProvider } from './providers/openai.js';
import { PerplexityProvider } from './providers/perplexity.js';
import type { LLMProvider, LLMProviderType } from './types.js';

let _provider: LLMProvider | null = null;

/**
 * Resolve provider type from env vars.
 * Priority: LLM_PROVIDER > OPENAI_PROVIDER > auto-detect from keys/endpoints.
 */
function resolveProviderType(): LLMProviderType {
  const explicit = (process.env.LLM_PROVIDER || process.env.OPENAI_PROVIDER || '').toLowerCase();
  if (explicit === 'azure') return 'azure';
  if (explicit === 'openai') return 'openai';
  if (explicit === 'perplexity') return 'perplexity';
  if (explicit === 'gemini') return 'gemini';
  if (explicit === 'fallback') return 'fallback';
  if (explicit === 'mock') return 'mock';

  // Auto-detect from environment
  const azureEndpoint = process.env.AZURE_OPENAI_ENDPOINT ?? '';
  const baseUrl = process.env.OPENAI_BASE_URL ?? '';
  if (azureEndpoint.trim().length > 0) return 'azure';
  if (baseUrl.includes('.cognitive.microsoft.com') || baseUrl.includes('.openai.azure.com'))
    return 'azure';
  if (process.env.PERPLEXITY_API_KEY) return 'perplexity';
  if (process.env.GEMINI_API_KEY) return 'gemini';

  return 'openai';
}

/**
 * Get the singleton LLM provider.
 */
export function getLLM(): LLMProvider {
  if (!_provider) {
    _provider = createLLMProvider(resolveProviderType());
  }
  return _provider;
}

/**
 * Create an LLM provider by type.
 * For 'fallback', reads LLM_FALLBACK_ORDER env var (comma-separated provider names).
 */
export function createLLMProvider(type: LLMProviderType): LLMProvider {
  switch (type) {
    case 'azure':
      return new AzureOpenAIProvider();
    case 'openai':
      return new OpenAIProvider();
    case 'perplexity':
      return new PerplexityProvider();
    case 'gemini':
      return new GeminiProvider();
    case 'fallback': {
      const order = (process.env.LLM_FALLBACK_ORDER ?? 'perplexity,openai,gemini')
        .split(',')
        .map(s => s.trim() as LLMProviderType)
        .filter(name => name && name !== 'fallback'); // prevent infinite recursion
      if (order.length === 0) {
        throw new Error('LLM_FALLBACK_ORDER must contain at least one non-fallback provider');
      }
      return new FallbackLLMProvider(order.map(createLLMProvider));
    }
    case 'mock':
      return new MockLLMProvider();
    default:
      throw new Error(
        `Unknown LLM_PROVIDER: '${type}'. Valid: azure, openai, perplexity, gemini, fallback, mock`
      );
  }
}

/**
 * Set the singleton LLM provider (for testing).
 */
export function setLLM(provider: LLMProvider): void {
  _provider = provider;
}

/**
 * @internal
 */
export function _resetLLM(): void {
  _provider = null;
}
