// src/services/llm.service.js
// LLM 서비스 - 멀티 프로바이더 지원

const axios = require('axios');
const { LLMProvider } = require('../models');
const logger = require('../config/logger.config');

class LLMService {
  constructor() {
    this.providers = [];
    this.initialized = false;
  }

  /**
   * 서비스 초기화
   */
  async initialize() {
    if (this.initialized) return;

    try {
      await this.loadProviders();
      this.initialized = true;
      logger.info('✅ LLM 서비스 초기화 완료');
    } catch (error) {
      logger.error('❌ LLM 서비스 초기화 실패:', error);
      // 초기화 실패 시 기본 프로바이더 사용
      this.providers = this.getDefaultProviders();
      this.initialized = true;
    }
  }

  /**
   * 데이터베이스에서 프로바이더 로드
   */
  async loadProviders() {
    try {
      const providers = await LLMProvider.getHealthyProviders();
      
      if (providers.length === 0) {
        logger.warn('⚠️ 활성 프로바이더가 없습니다. 기본 프로바이더 사용');
        this.providers = this.getDefaultProviders();
      } else {
        this.providers = providers.map((p) => ({
          id: p.id,
          name: p.name,
          endpoint: p.endpoint,
          apiKey: p.apiKey,
          modelName: p.modelName,
          priority: p.priority,
          maxTokens: p.maxTokens,
          temperature: p.temperature,
          timeoutMs: p.timeoutMs,
          costPer1kInputTokens: parseFloat(p.costPer1kInputTokens) || 0,
          costPer1kOutputTokens: parseFloat(p.costPer1kOutputTokens) || 0,
          isHealthy: p.isHealthy,
          config: p.config,
        }));
      }

      logger.info(`📥 ${this.providers.length}개 프로바이더 로드됨`);
    } catch (error) {
      logger.error('프로바이더 로드 실패:', error);
      throw error;
    }
  }

  /**
   * 기본 프로바이더 설정 (환경 변수 기반)
   */
  getDefaultProviders() {
    const providers = [];

    // Gemini
    if (process.env.GEMINI_API_KEY) {
      providers.push({
        id: 'default-gemini',
        name: 'gemini',
        apiKey: process.env.GEMINI_API_KEY,
        modelName: process.env.GEMINI_MODEL || 'gemini-2.0-flash',
        priority: 1,
        maxTokens: 8192,
        temperature: 0.7,
        timeoutMs: 60000,
        costPer1kInputTokens: 0.00025,
        costPer1kOutputTokens: 0.001,
        isHealthy: true,
      });
    }

    // OpenAI
    if (process.env.OPENAI_API_KEY) {
      providers.push({
        id: 'default-openai',
        name: 'openai',
        endpoint: 'https://api.openai.com/v1/chat/completions',
        apiKey: process.env.OPENAI_API_KEY,
        modelName: process.env.OPENAI_MODEL || 'gpt-4o-mini',
        priority: 2,
        maxTokens: 4096,
        temperature: 0.7,
        timeoutMs: 60000,
        costPer1kInputTokens: 0.00015,
        costPer1kOutputTokens: 0.0006,
        isHealthy: true,
      });
    }

    // Claude
    if (process.env.CLAUDE_API_KEY) {
      providers.push({
        id: 'default-claude',
        name: 'claude',
        endpoint: 'https://api.anthropic.com/v1/messages',
        apiKey: process.env.CLAUDE_API_KEY,
        modelName: process.env.CLAUDE_MODEL || 'claude-3-haiku-20240307',
        priority: 3,
        maxTokens: 4096,
        temperature: 0.7,
        timeoutMs: 60000,
        costPer1kInputTokens: 0.00025,
        costPer1kOutputTokens: 0.00125,
        isHealthy: true,
      });
    }

    return providers;
  }

  /**
   * 채팅 API 호출 (자동 fallback)
   */
  async chat(params) {
    const {
      model,
      messages,
      temperature = 0.7,
      maxTokens = 4096,
      userId,
      apiKeyId,
    } = params;

    // 초기화 확인
    if (!this.initialized) {
      await this.initialize();
    }

    const startTime = Date.now();
    let lastError = null;

    // 요청된 모델에 맞는 프로바이더 찾기
    const requestedProvider = this.providers.find(
      (p) => p.modelName === model || p.name === model
    );

    // 우선순위 순으로 프로바이더 정렬
    const sortedProviders = requestedProvider
      ? [requestedProvider, ...this.providers.filter((p) => p !== requestedProvider)]
      : this.providers;

    // 프로바이더 순회 (fallback)
    for (const provider of sortedProviders) {
      if (!provider.isHealthy) {
        logger.warn(`⚠️ ${provider.name} 건강하지 않음, 건너뜀`);
        continue;
      }

      try {
        logger.info(`🚀 ${provider.name} (${provider.modelName}) 시도 중...`);

        const result = await this.callProvider(provider, {
          messages,
          maxTokens: maxTokens || provider.maxTokens,
          temperature: temperature || provider.temperature,
        });

        const responseTime = Date.now() - startTime;

        // 비용 계산
        const cost = this.calculateCost(
          result.usage.promptTokens,
          result.usage.completionTokens,
          provider.costPer1kInputTokens,
          provider.costPer1kOutputTokens
        );

        logger.info(
          `✅ ${provider.name} 성공 (${responseTime}ms, ${result.usage.totalTokens} tokens)`
        );

        return {
          text: result.text,
          provider: provider.name,
          providerId: provider.id,
          model: provider.modelName,
          usage: result.usage,
          responseTime,
          cost,
        };
      } catch (error) {
        logger.error(`❌ ${provider.name} 실패:`, error.message);
        lastError = error;

        // 다음 프로바이더로 fallback
        continue;
      }
    }

    // 모든 프로바이더 실패
    throw new Error(
      `모든 LLM 프로바이더가 실패했습니다: ${lastError?.message || '알 수 없는 오류'}`
    );
  }

  /**
   * 개별 프로바이더 호출
   */
  async callProvider(provider, { messages, maxTokens, temperature }) {
    const timeout = provider.timeoutMs || 60000;

    switch (provider.name) {
      case 'gemini':
        return this.callGemini(provider, { messages, maxTokens, temperature });
      case 'openai':
        return this.callOpenAI(provider, { messages, maxTokens, temperature, timeout });
      case 'claude':
        return this.callClaude(provider, { messages, maxTokens, temperature, timeout });
      default:
        throw new Error(`지원하지 않는 프로바이더: ${provider.name}`);
    }
  }

  /**
   * Gemini API 호출
   */
  async callGemini(provider, { messages, maxTokens, temperature }) {
    const { GoogleGenAI } = require('@google/genai');
    
    const ai = new GoogleGenAI({ apiKey: provider.apiKey });

    // 메시지 변환 (OpenAI 형식 -> Gemini 형식)
    const contents = messages.map((msg) => ({
      role: msg.role === 'assistant' ? 'model' : 'user',
      parts: [{ text: msg.content }],
    }));

    // system 메시지 처리
    const systemMessage = messages.find((m) => m.role === 'system');
    const systemInstruction = systemMessage ? systemMessage.content : undefined;

    const config = {
      maxOutputTokens: maxTokens,
      temperature,
    };

    const result = await ai.models.generateContent({
      model: provider.modelName,
      contents: contents.filter((c) => c.role !== 'system'),
      systemInstruction,
      config,
    });

    // 응답 텍스트 추출
    let text = '';
    if (result.candidates?.[0]?.content?.parts) {
      text = result.candidates[0].content.parts
        .filter((p) => p.text)
        .map((p) => p.text)
        .join('\n');
    }

    const usage = result.usageMetadata || {};
    const promptTokens = usage.promptTokenCount ?? 0;
    const completionTokens = usage.candidatesTokenCount ?? 0;

    return {
      text,
      usage: {
        promptTokens,
        completionTokens,
        totalTokens: promptTokens + completionTokens,
      },
    };
  }

  /**
   * OpenAI API 호출
   */
  async callOpenAI(provider, { messages, maxTokens, temperature, timeout }) {
    const response = await axios.post(
      provider.endpoint,
      {
        model: provider.modelName,
        messages,
        max_tokens: maxTokens,
        temperature,
      },
      {
        timeout,
        headers: {
          'Content-Type': 'application/json',
          Authorization: `Bearer ${provider.apiKey}`,
        },
      }
    );

    return {
      text: response.data.choices[0].message.content,
      usage: {
        promptTokens: response.data.usage.prompt_tokens,
        completionTokens: response.data.usage.completion_tokens,
        totalTokens: response.data.usage.total_tokens,
      },
    };
  }

  /**
   * Claude API 호출
   */
  async callClaude(provider, { messages, maxTokens, temperature, timeout }) {
    // system 메시지 분리
    const systemMessage = messages.find((m) => m.role === 'system');
    const otherMessages = messages.filter((m) => m.role !== 'system');

    const response = await axios.post(
      provider.endpoint,
      {
        model: provider.modelName,
        messages: otherMessages,
        system: systemMessage?.content,
        max_tokens: maxTokens,
        temperature,
      },
      {
        timeout,
        headers: {
          'Content-Type': 'application/json',
          'x-api-key': provider.apiKey,
          'anthropic-version': '2023-06-01',
        },
      }
    );

    return {
      text: response.data.content[0].text,
      usage: {
        promptTokens: response.data.usage.input_tokens,
        completionTokens: response.data.usage.output_tokens,
        totalTokens:
          response.data.usage.input_tokens + response.data.usage.output_tokens,
      },
    };
  }

  /**
   * 스트리밍 채팅 (제너레이터)
   */
  async *chatStream(params) {
    // 현재는 간단한 구현 (전체 응답 후 청크로 분할)
    // 실제 스트리밍은 각 프로바이더의 스트리밍 API 사용 필요
    const result = await this.chat(params);
    
    // 텍스트를 청크로 분할하여 전송
    const chunkSize = 10;
    for (let i = 0; i < result.text.length; i += chunkSize) {
      yield {
        text: result.text.slice(i, i + chunkSize),
        done: i + chunkSize >= result.text.length,
      };
    }
  }

  /**
   * 비용 계산
   */
  calculateCost(promptTokens, completionTokens, inputCost, outputCost) {
    const inputTotal = (promptTokens / 1000) * inputCost;
    const outputTotal = (completionTokens / 1000) * outputCost;
    return parseFloat((inputTotal + outputTotal).toFixed(6));
  }
}

// 싱글톤 인스턴스
const llmService = new LLMService();

module.exports = llmService;
