import { Injectable, Logger, OnModuleInit } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { ChatOpenAI } from '@langchain/openai';
import { BaseChatModel } from '@langchain/core/language_models/chat_models';
import {
  AIMessage,
  HumanMessage,
  SystemMessage,
  BaseMessage,
} from '@langchain/core/messages';
import {
  LLMProvider,
  ChatMessage,
  ChatOptions,
  ChatResponse,
  StreamCallbacks,
} from '../interfaces/llm-provider.interface';

/**
 * LangChain Provider
 * 
 * 使用 LangChain 统一管理多个 LLM 模型
 * 
 * 支持的模型:
 * - OpenAI (gpt-4, gpt-4-turbo, gpt-3.5-turbo)
 * - 通义千问 (qwen-plus, qwen-turbo, qwen-max) - 通过 OpenAI 兼容接口
 * 
 * 环境变量:
 * - LLM_PROVIDER: openai | qwen | mock (默认 auto)
 * - OPENAI_API_KEY: OpenAI API Key
 * - OPENAI_MODEL: OpenAI 模型名称
 * - OPENAI_BASE_URL: 自定义 API 地址
 * - DASHSCOPE_API_KEY: 通义千问 API Key
 * - QWEN_MODEL: 通义千问模型名称
 */
@Injectable()
export class LangChainProvider implements LLMProvider, OnModuleInit {
  private readonly logger = new Logger(LangChainProvider.name);
  private model: BaseChatModel | null = null;
  private isAvailableFlag = false;
  private providerType: 'openai' | 'qwen' | 'mock' = 'mock';
  private modelName: string = '';

  constructor(private readonly configService: ConfigService) {}

  async onModuleInit() {
    await this.initializeModel();
  }

  /**
   * 初始化 LLM 模型
   */
  private async initializeModel(): Promise<void> {
    const providerConfig = this.configService.get<string>('LLM_PROVIDER') || 'auto';
    const useMock = this.configService.get<string>('LLM_USE_MOCK') === 'true';

    if (useMock || providerConfig === 'mock') {
      this.providerType = 'mock';
      this.logger.log('LangChain Provider: 使用 Mock 模式');
      return;
    }

    try {
      // 尝试初始化通义千问
      if (providerConfig === 'qwen' || providerConfig === 'auto') {
        const dashscopeKey = this.configService.get<string>('DASHSCOPE_API_KEY');
        if (dashscopeKey) {
          const qwenModel = this.configService.get<string>('QWEN_MODEL') || 'qwen-plus';
          const qwenBaseUrl = this.configService.get<string>('QWEN_BASE_URL') || 
                             'https://dashscope.aliyuncs.com/compatible-mode/v1';
          
          // 使用 apiKey 而不是 openAIApiKey（LangChain 1.x 新 API）
          this.model = new ChatOpenAI({
            apiKey: dashscopeKey,
            model: qwenModel,
            configuration: {
              baseURL: qwenBaseUrl,
            },
            temperature: 0.7,
            maxTokens: 2048,
            streaming: true,
          });
          
          this.providerType = 'qwen';
          this.modelName = qwenModel;
          this.isAvailableFlag = true;
          this.logger.log(`LangChain Provider: 初始化通义千问成功 (模型: ${qwenModel})`);
          return;
        }
      }

      // 尝试初始化 OpenAI
      if (providerConfig === 'openai' || providerConfig === 'auto') {
        const openaiKey = this.configService.get<string>('OPENAI_API_KEY');
        if (openaiKey) {
          const openaiModel = this.configService.get<string>('OPENAI_MODEL') || 'gpt-4-turbo';
          const openaiBaseUrl = this.configService.get<string>('OPENAI_BASE_URL');
          
          this.model = new ChatOpenAI({
            apiKey: openaiKey,
            model: openaiModel,
            configuration: openaiBaseUrl ? { baseURL: openaiBaseUrl } : undefined,
            temperature: 0.7,
            maxTokens: 2048,
            streaming: true,
          });
          
          this.providerType = 'openai';
          this.modelName = openaiModel;
          this.isAvailableFlag = true;
          this.logger.log(`LangChain Provider: 初始化 OpenAI 成功 (模型: ${openaiModel})`);
          return;
        }
      }

      // 没有可用的 API Key，使用 mock
      this.providerType = 'mock';
      this.logger.warn('LangChain Provider: 无可用的 API Key，使用 Mock 模式');
    } catch (error) {
      this.logger.error('LangChain Provider 初始化失败:', error);
      this.providerType = 'mock';
    }
  }

  /**
   * 将 ChatMessage 转换为 LangChain BaseMessage
   */
  private convertToLangChainMessages(messages: ChatMessage[]): BaseMessage[] {
    return messages.map((msg) => {
      switch (msg.role) {
        case 'system':
          return new SystemMessage(msg.content);
        case 'assistant':
          return new AIMessage(msg.content);
        case 'user':
        default:
          return new HumanMessage(msg.content);
      }
    });
  }

  /**
   * 发送聊天消息并获取完整响应
   */
  async chat(messages: ChatMessage[], options?: ChatOptions): Promise<ChatResponse> {
    // Mock 模式
    if (this.providerType === 'mock' || !this.model) {
      return this.mockChat(messages);
    }

    const langchainMessages = this.convertToLangChainMessages(messages);

    try {
      // 如果指定了不同的模型，临时创建新的模型实例
      let modelToUse = this.model;
      if (options?.model && options.model !== this.modelName) {
        modelToUse = new ChatOpenAI({
          apiKey: this.providerType === 'qwen' 
            ? this.configService.get<string>('DASHSCOPE_API_KEY')
            : this.configService.get<string>('OPENAI_API_KEY'),
          model: options.model,
          configuration: this.providerType === 'qwen' 
            ? { baseURL: this.configService.get<string>('QWEN_BASE_URL') || 'https://dashscope.aliyuncs.com/compatible-mode/v1' }
            : undefined,
          temperature: options?.temperature ?? 0.7,
          maxTokens: options?.maxTokens ?? 2048,
        });
      }

      const response = await modelToUse.invoke(langchainMessages);

      const content = typeof response.content === 'string' 
        ? response.content 
        : JSON.stringify(response.content);

      // 提取 token 使用情况
      const usage = (response as any).usage_metadata || {};

      return {
        content,
        finishReason: 'stop',
        usage: {
          promptTokens: usage?.input_tokens || 0,
          completionTokens: usage?.output_tokens || 0,
          totalTokens: usage?.total_tokens || 0,
        },
      };
    } catch (error: any) {
      const errorMessage = error?.message || error?.toString() || 'Unknown error';
      const errorDetails = error?.response?.data || error?.cause || '';
      this.logger.error(`LangChain chat error: ${errorMessage}`, errorDetails);
      throw error;
    }
  }

  /**
   * 发送聊天消息并获取流式响应
   */
  async streamChat(
    messages: ChatMessage[],
    callbacks: StreamCallbacks,
    options?: ChatOptions,
  ): Promise<void> {
    // Mock 模式
    if (this.providerType === 'mock' || !this.model) {
      return this.mockStreamChat(messages, callbacks);
    }

    const langchainMessages = this.convertToLangChainMessages(messages);
    let fullContent = '';
    let totalUsage = { promptTokens: 0, completionTokens: 0, totalTokens: 0 };

    try {
      this.logger.log(`[Stream] 开始流式调用，消息数: ${langchainMessages.length}`);
      const stream = await this.model.stream(langchainMessages);

      let chunkCount = 0;
      for await (const chunk of stream) {
        chunkCount++;
        
        // 处理不同格式的 content
        let content = '';
        if (typeof chunk.content === 'string') {
          content = chunk.content;
        } else if (Array.isArray(chunk.content)) {
          // 某些模型返回数组格式
          content = chunk.content
            .filter((c: any) => c.type === 'text')
            .map((c: any) => c.text)
            .join('');
        }
        
        if (content) {
          fullContent += content;
          this.logger.log(`[Stream] Token: "${content.substring(0, 20)}..."`);
          callbacks.onToken(content);
        }

        // 更新 token 使用
        const usageMetadata = (chunk as any).usage_metadata;
        if (usageMetadata) {
          totalUsage = {
            promptTokens: usageMetadata.input_tokens || 0,
            completionTokens: usageMetadata.output_tokens || 0,
            totalTokens: usageMetadata.total_tokens || 0,
          };
        }
      }

      this.logger.log(`[Stream] 完成，chunk数: ${chunkCount}，内容长度: ${fullContent.length}`);
      callbacks.onComplete(fullContent, totalUsage);
    } catch (error: any) {
      const errorMessage = error?.message || error?.toString() || 'Unknown error';
      const errorDetails = error?.response?.data || error?.cause || '';
      this.logger.error(`LangChain stream error: ${errorMessage}`, errorDetails);
      callbacks.onError(error as Error);
    }
  }

  /**
   * 检查服务是否可用
   */
  async isAvailable(): Promise<boolean> {
    if (this.providerType === 'mock') {
      return true; // Mock 模式始终可用
    }
    return this.isAvailableFlag && this.model !== null;
  }

  /**
   * 获取当前 Provider 类型
   */
  getProviderType(): string {
    return this.providerType;
  }

  /**
   * 获取当前模型名称
   */
  getModelName(): string {
    return this.modelName || 'mock';
  }

  // ==================== Mock 实现 ====================

  private readonly mockResponses: Record<string, string> = {
    default: `感谢您的提问！作为企业智能助手，我可以帮助您解答 IT、HR、行政等方面的问题。请问有什么可以帮助您的？

我可以帮您：
- 🖥️ IT 问题：电脑故障、软件安装、网络问题
- 👥 HR 问题：请假流程、薪资查询、福利政策
- 📋 行政问题：报销流程、办公用品申请
- 📝 工单创建：如果问题无法解决，我可以帮您创建工单`,
    it: `关于 IT 相关问题，我可以帮您解答：
- 电脑故障排查
- 软件安装和配置
- 网络连接问题
- VPN 使用指南
- 密码重置流程

如果问题比较复杂，建议您创建 IT 工单，我们的技术团队会尽快处理。`,
    hr: `关于 HR 相关问题，以下是常见解答：
- **请假流程**: 登录 OA 系统 → 我的申请 → 请假申请
- **薪资查询**: 每月 10 号发放，可在工资条中查看明细
- **社保公积金**: 可联系 HR 部门查询详情

如需更多帮助，请联系 HR 部门或创建工单。`,
    admin: `关于行政相关问题，以下是常见解答：
- **报销流程**: 填写报销单 → 部门审批 → 财务审核
- **办公用品**: 每月第一周统一申请
- **会议室预订**: 使用 OA 系统的会议室预订功能

如有其他问题，请联系行政部门。`,
  };

  private async mockChat(messages: ChatMessage[]): Promise<ChatResponse> {
    await this.delay(500);
    
    const lastUserMessage = this.getLastUserMessage(messages);
    const response = this.generateMockResponse(lastUserMessage);

    return {
      content: response,
      finishReason: 'stop',
      usage: {
        promptTokens: 0,
        completionTokens: 0,
        totalTokens: 0,
      },
    };
  }

  private async mockStreamChat(
    messages: ChatMessage[],
    callbacks: StreamCallbacks,
  ): Promise<void> {
    const lastUserMessage = this.getLastUserMessage(messages);
    const response = this.generateMockResponse(lastUserMessage);
    
    // 模拟流式输出
    const tokens = response.split('');
    let fullContent = '';

    for (const token of tokens) {
      await this.delay(20);
      fullContent += token;
      callbacks.onToken(token);
    }

    callbacks.onComplete(fullContent, {
      promptTokens: 0,
      completionTokens: 0,
      totalTokens: 0,
    });
  }

  private getLastUserMessage(messages: ChatMessage[]): string {
    const userMessages = messages.filter((m) => m.role === 'user');
    return userMessages[userMessages.length - 1]?.content || '';
  }

  private generateMockResponse(userMessage: string): string {
    const lowerMessage = userMessage.toLowerCase();

    if (lowerMessage.includes('it') || 
        lowerMessage.includes('电脑') || 
        lowerMessage.includes('网络') ||
        lowerMessage.includes('软件') ||
        lowerMessage.includes('密码')) {
      return this.mockResponses.it;
    }

    if (lowerMessage.includes('hr') || 
        lowerMessage.includes('请假') || 
        lowerMessage.includes('薪资') ||
        lowerMessage.includes('工资') ||
        lowerMessage.includes('福利') ||
        lowerMessage.includes('社保')) {
      return this.mockResponses.hr;
    }

    if (lowerMessage.includes('行政') || 
        lowerMessage.includes('报销') || 
        lowerMessage.includes('办公') ||
        lowerMessage.includes('会议室')) {
      return this.mockResponses.admin;
    }

    return this.mockResponses.default;
  }

  private delay(ms: number): Promise<void> {
    return new Promise((resolve) => setTimeout(resolve, ms));
  }
}
