import OpenAI from 'openai';
import { config } from '../config/index.js';
import { logger } from '../utils/logger.js';
import type { ChatCompletionMessageParam, ChatCompletionTool } from 'openai/resources/chat/completions';

/**
 * 消息角色类型
 */
export type MessageRole = 'system' | 'user' | 'assistant' | 'tool';

/**
 * 聊天消息接口
 */
export interface ChatMessage {
  role: MessageRole;
  content: string;
  name?: string;
  tool_call_id?: string;
}

/**
 * 聊天补全选项
 */
export interface ChatCompletionOptions {
  model?: string;
  messages: ChatMessage[];
  temperature?: number;
  maxTokens?: number;
  topP?: number;
  stream?: boolean;
  tools?: ChatCompletionTool[];
  toolChoice?: 'auto' | 'none' | { type: 'function'; function: { name: string } };
  responseFormat?: { type: 'text' | 'json_object' };
  stop?: string | string[];
  seed?: number;
}

/**
 * 流式响应回调
 */
export type StreamCallback = (chunk: string, done: boolean) => void;

/**
 * 嵌入向量选项
 */
export interface EmbeddingOptions {
  model?: string;
  input: string | string[];
  dimensions?: number;
}

/**
 * 视觉理解选项
 */
export interface VisionOptions {
  model?: string;
  prompt: string;
  imageUrl?: string;
  imageBase64?: string;
  maxTokens?: number;
}

/**
 * 函数定义
 */
export interface FunctionDefinition {
  name: string;
  description: string;
  parameters: Record<string, unknown>;
}

/**
 * 工具调用结果
 */
export interface ToolCallResult {
  id: string;
  function: {
    name: string;
    arguments: string;
  };
}

/**
 * AI 服务响应
 */
export interface AIResponse {
  content: string;
  toolCalls?: ToolCallResult[];
  usage?: {
    promptTokens: number;
    completionTokens: number;
    totalTokens: number;
  };
  finishReason?: string;
}

/**
 * 登录状态分析结果
 */
export interface LoginStatusAnalysis {
  isLoggedIn: boolean;
  hasVerification: boolean;
  verificationType?: 'captcha' | 'sms' | 'qrcode' | 'face' | 'slider' | 'other';
  verificationDescription?: string;
  pageDescription: string;
  suggestedAction?: string;
}

/**
 * 账号信息提取结果
 */
export interface AccountInfoExtraction {
  found: boolean;
  accountName?: string;
  accountId?: string;
  avatarDescription?: string;
  fansCount?: string;
  worksCount?: string;
  otherInfo?: string;
  navigationGuide?: string;
}

/**
 * 页面操作指导结果
 */
export interface PageOperationGuide {
  hasAction: boolean;
  actionType?: 'click' | 'input' | 'scroll' | 'wait' | 'navigate';
  targetDescription?: string;
  targetSelector?: string;
  targetPosition?: { x: number; y: number };
  inputText?: string;
  explanation: string;
}

/**
 * 发布状态分析结果
 */
export interface PublishStatusAnalysis {
  status: 'uploading' | 'processing' | 'success' | 'failed' | 'need_captcha' | 'need_action';
  captchaType?: 'image' | 'sms' | 'slider' | 'other';
  captchaDescription?: string;
  errorMessage?: string;
  nextAction?: {
    actionType: 'click' | 'input' | 'wait';
    targetDescription: string;
    targetSelector?: string;
  };
  pageDescription: string;
  confidence: number; // 0-100 表示 AI 对判断的信心程度
}

/**
 * 阿里云百炼千问大模型 AI 服务类
 * 
 * 支持功能：
 * - 聊天补全（Chat Completion）
 * - 流式输出（Streaming）
 * - 函数调用（Function Calling）
 * - 视觉理解（Vision）
 * - 文本嵌入（Embeddings）
 * - 多模型支持
 * - 自动重试机制
 */
export class QwenAIService {
  private client: OpenAI | null = null;
  private models: typeof config.ai.models;

  constructor() {
    this.models = config.ai.models;
    if (config.ai.apiKey) {
      this.client = new OpenAI({
        apiKey: config.ai.apiKey,
        baseURL: config.ai.baseUrl,
        timeout: config.ai.timeout,
        maxRetries: config.ai.maxRetries,
      });
      logger.info('QwenAIService initialized', { baseUrl: config.ai.baseUrl });
    } else {
      logger.warn('QwenAIService: API key not configured');
    }
  }

  /**
   * 检查 AI 服务是否可用
   */
  isAvailable(): boolean {
    return !!this.client;
  }

  /**
   * 获取可用模型列表
   */
  getAvailableModels(): typeof config.ai.models {
    return this.models;
  }

  /**
   * 确保服务可用
   */
  private ensureAvailable(): void {
    if (!this.client) {
      throw new Error('AI service not configured. Please set DASHSCOPE_API_KEY environment variable.');
    }
  }

  // ==================== 核心 API 方法 ====================

  /**
   * 聊天补全 - 基础方法
   * @param options 聊天选项
   * @returns AI 响应
   */
  async chatCompletion(options: ChatCompletionOptions): Promise<AIResponse> {
    this.ensureAvailable();

    const {
      model = this.models.chat,
      messages,
      temperature = 0.7,
      maxTokens = 2000,
      topP = 0.9,
      tools,
      toolChoice,
      responseFormat,
      stop,
      seed,
    } = options;

    const startTime = Date.now();
    const requestId = `chat_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;

    logger.info(`[AI] ========== Chat Completion Request ==========`);
    logger.info(`[AI] Request ID: ${requestId}`);
    logger.info(`[AI] Model: ${model}`);
    logger.info(`[AI] Messages: ${messages.length} 条`);
    logger.info(`[AI] Temperature: ${temperature}, MaxTokens: ${maxTokens}`);
    if (tools) logger.info(`[AI] Tools: ${tools.length} 个函数`);
    if (responseFormat) logger.info(`[AI] Response Format: ${responseFormat.type}`);

    try {
      const response = await this.client!.chat.completions.create({
        model,
        messages: messages as ChatCompletionMessageParam[],
        temperature,
        max_tokens: maxTokens,
        top_p: topP,
        tools,
        tool_choice: toolChoice,
        response_format: responseFormat,
        stop,
        seed,
      });

      const duration = Date.now() - startTime;
      const choice = response.choices[0];

      logger.info(`[AI] ========== Chat Completion Response ==========`);
      logger.info(`[AI] Request ID: ${requestId}`);
      logger.info(`[AI] Duration: ${duration}ms`);
      logger.info(`[AI] Finish Reason: ${choice?.finish_reason}`);
      if (response.usage) {
        logger.info(`[AI] Tokens - Prompt: ${response.usage.prompt_tokens}, Completion: ${response.usage.completion_tokens}, Total: ${response.usage.total_tokens}`);
      }
      logger.info(`[AI] Response Length: ${choice?.message?.content?.length || 0} 字符`);
      logger.info(`[AI] ==============================================`);

      return {
        content: choice?.message?.content || '',
        toolCalls: choice?.message?.tool_calls?.map(tc => ({
          id: tc.id,
          function: {
            name: tc.function.name,
            arguments: tc.function.arguments,
          },
        })),
        usage: response.usage ? {
          promptTokens: response.usage.prompt_tokens,
          completionTokens: response.usage.completion_tokens,
          totalTokens: response.usage.total_tokens,
        } : undefined,
        finishReason: choice?.finish_reason || undefined,
      };
    } catch (error) {
      const duration = Date.now() - startTime;
      logger.error(`[AI] ========== Chat Completion Error ==========`);
      logger.error(`[AI] Request ID: ${requestId}`);
      logger.error(`[AI] Duration: ${duration}ms`);
      logger.error(`[AI] Error:`, error);
      logger.error(`[AI] ============================================`);
      throw this.handleError(error);
    }
  }

  /**
   * 流式聊天补全
   * @param options 聊天选项
   * @param callback 流式回调
   * @returns 完整的响应内容
   */
  async chatCompletionStream(
    options: Omit<ChatCompletionOptions, 'stream'>,
    callback: StreamCallback
  ): Promise<string> {
    this.ensureAvailable();

    const {
      model = this.models.chat,
      messages,
      temperature = 0.7,
      maxTokens = 2000,
      topP = 0.9,
      stop,
    } = options;

    const startTime = Date.now();
    const requestId = `stream_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;

    logger.info(`[AI] ========== Stream Chat Request ==========`);
    logger.info(`[AI] Request ID: ${requestId}`);
    logger.info(`[AI] Model: ${model}`);
    logger.info(`[AI] Messages: ${messages.length} 条`);
    logger.info(`[AI] Temperature: ${temperature}, MaxTokens: ${maxTokens}`);

    try {
      const stream = await this.client!.chat.completions.create({
        model,
        messages: messages as ChatCompletionMessageParam[],
        temperature,
        max_tokens: maxTokens,
        top_p: topP,
        stop,
        stream: true,
      });

      let fullContent = '';

      for await (const chunk of stream) {
        const delta = chunk.choices[0]?.delta?.content || '';
        if (delta) {
          fullContent += delta;
          callback(delta, false);
        }
      }

      callback('', true);

      const duration = Date.now() - startTime;
      logger.info(`[AI] ========== Stream Chat Response ==========`);
      logger.info(`[AI] Request ID: ${requestId}`);
      logger.info(`[AI] Duration: ${duration}ms`);
      logger.info(`[AI] Response Length: ${fullContent.length} 字符`);
      logger.info(`[AI] =============================================`);

      return fullContent;
    } catch (error) {
      const duration = Date.now() - startTime;
      logger.error(`[AI] ========== Stream Chat Error ==========`);
      logger.error(`[AI] Request ID: ${requestId}`);
      logger.error(`[AI] Duration: ${duration}ms`);
      logger.error(`[AI] Error:`, error);
      logger.error(`[AI] =========================================`);
      throw this.handleError(error);
    }
  }

  /**
   * 简单对话 - 便捷方法
   * @param prompt 用户提示
   * @param systemPrompt 系统提示（可选）
   * @param model 模型（可选）
   * @returns AI 回复内容
   */
  async chat(prompt: string, systemPrompt?: string, model?: string): Promise<string> {
    const messages: ChatMessage[] = [];

    if (systemPrompt) {
      messages.push({ role: 'system', content: systemPrompt });
    }
    messages.push({ role: 'user', content: prompt });

    const response = await this.chatCompletion({
      model: model || this.models.chat,
      messages,
    });

    return response.content;
  }

  /**
   * 快速对话 - 使用快速模型
   */
  async quickChat(prompt: string, systemPrompt?: string): Promise<string> {
    return this.chat(prompt, systemPrompt, this.models.fast);
  }

  /**
   * 推理对话 - 使用推理模型（适合复杂逻辑问题）
   */
  async reasoningChat(prompt: string, systemPrompt?: string): Promise<string> {
    return this.chat(prompt, systemPrompt, this.models.reasoning);
  }

  /**
   * 代码生成/分析 - 使用代码模型
   */
  async codeChat(prompt: string, systemPrompt?: string): Promise<string> {
    const defaultCodeSystemPrompt = '你是一个专业的编程助手，擅长代码编写、分析和调试。请提供清晰、高效的代码解决方案。';
    return this.chat(prompt, systemPrompt || defaultCodeSystemPrompt, this.models.coder);
  }

  // ==================== 函数调用 ====================

  /**
   * 带函数调用的对话
   * @param messages 消息列表
   * @param functions 函数定义列表
   * @param toolChoice 工具选择策略
   */
  async chatWithFunctions(
    messages: ChatMessage[],
    functions: FunctionDefinition[],
    toolChoice: 'auto' | 'none' | string = 'auto'
  ): Promise<AIResponse> {
    const tools: ChatCompletionTool[] = functions.map(fn => ({
      type: 'function' as const,
      function: {
        name: fn.name,
        description: fn.description,
        parameters: fn.parameters,
      },
    }));

    const choice = toolChoice === 'auto' || toolChoice === 'none'
      ? toolChoice
      : { type: 'function' as const, function: { name: toolChoice } };

    return this.chatCompletion({
      messages,
      tools,
      toolChoice: choice,
    });
  }

  /**
   * 执行函数调用循环
   * @param messages 初始消息
   * @param functions 函数定义
   * @param functionExecutor 函数执行器
   * @param maxIterations 最大迭代次数
   */
  async runFunctionLoop(
    messages: ChatMessage[],
    functions: FunctionDefinition[],
    functionExecutor: (name: string, args: Record<string, unknown>) => Promise<string>,
    maxIterations: number = 10
  ): Promise<string> {
    const conversationMessages = [...messages];

    for (let i = 0; i < maxIterations; i++) {
      const response = await this.chatWithFunctions(conversationMessages, functions);

      if (!response.toolCalls || response.toolCalls.length === 0) {
        return response.content;
      }

      // 添加助手消息（包含工具调用）
      conversationMessages.push({
        role: 'assistant',
        content: response.content || '',
      });

      // 执行每个工具调用
      for (const toolCall of response.toolCalls) {
        try {
          const args = JSON.parse(toolCall.function.arguments);
          const result = await functionExecutor(toolCall.function.name, args);

          conversationMessages.push({
            role: 'tool',
            content: result,
            tool_call_id: toolCall.id,
          });
        } catch (error) {
          conversationMessages.push({
            role: 'tool',
            content: `Error: ${error instanceof Error ? error.message : 'Unknown error'}`,
            tool_call_id: toolCall.id,
          });
        }
      }
    }

    throw new Error('Function loop exceeded maximum iterations');
  }

  // ==================== 视觉理解 ====================

  /**
   * 图像理解
   * @param options 视觉选项
   */
  async analyzeImage(options: VisionOptions): Promise<string> {
    this.ensureAvailable();

    const { model = this.models.vision, prompt, imageUrl, imageBase64, maxTokens = 1000 } = options;

    if (!imageUrl && !imageBase64) {
      throw new Error('Either imageUrl or imageBase64 must be provided');
    }

    const imageContent = imageUrl
      ? { type: 'image_url' as const, image_url: { url: imageUrl } }
      : { type: 'image_url' as const, image_url: { url: `data:image/jpeg;base64,${imageBase64}` } };

    const startTime = Date.now();
    const requestId = `vision_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
    const imageSize = imageBase64 ? Math.round(imageBase64.length / 1024) : 0;

    logger.info(`[AI] ========== Vision Analysis Request ==========`);
    logger.info(`[AI] Request ID: ${requestId}`);
    logger.info(`[AI] Model: ${model}`);
    logger.info(`[AI] Image: ${imageUrl ? 'URL' : `Base64 (${imageSize}KB)`}`);
    logger.info(`[AI] Prompt: ${prompt.substring(0, 100)}${prompt.length > 100 ? '...' : ''}`);
    logger.info(`[AI] MaxTokens: ${maxTokens}`);

    try {
      const response = await this.client!.chat.completions.create({
        model,
        messages: [
          {
            role: 'user',
            content: [
              imageContent,
              { type: 'text', text: prompt },
            ],
          },
        ],
        max_tokens: maxTokens,
      });

      const duration = Date.now() - startTime;
      const content = response.choices[0]?.message?.content || '';

      logger.info(`[AI] ========== Vision Analysis Response ==========`);
      logger.info(`[AI] Request ID: ${requestId}`);
      logger.info(`[AI] Duration: ${duration}ms`);
      logger.info(`[AI] Finish Reason: ${response.choices[0]?.finish_reason}`);
      if (response.usage) {
        logger.info(`[AI] Tokens - Prompt: ${response.usage.prompt_tokens}, Completion: ${response.usage.completion_tokens}, Total: ${response.usage.total_tokens}`);
      }
      logger.info(`[AI] Response Length: ${content.length} 字符`);
      logger.info(`[AI] Response Preview: ${content.substring(0, 150)}${content.length > 150 ? '...' : ''}`);
      logger.info(`[AI] ================================================`);

      return content;
    } catch (error) {
      const duration = Date.now() - startTime;
      logger.error(`[AI] ========== Vision Analysis Error ==========`);
      logger.error(`[AI] Request ID: ${requestId}`);
      logger.error(`[AI] Duration: ${duration}ms`);
      logger.error(`[AI] Error:`, error);
      logger.error(`[AI] =============================================`);
      throw this.handleError(error);
    }
  }

  // ==================== 文本嵌入 ====================

  /**
   * 生成文本嵌入向量
   * @param options 嵌入选项
   */
  async createEmbedding(options: EmbeddingOptions): Promise<number[][]> {
    this.ensureAvailable();

    const { model = this.models.embedding, input, dimensions } = options;

    const startTime = Date.now();
    const requestId = `embed_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
    const inputCount = Array.isArray(input) ? input.length : 1;

    logger.info(`[AI] ========== Embedding Request ==========`);
    logger.info(`[AI] Request ID: ${requestId}`);
    logger.info(`[AI] Model: ${model}`);
    logger.info(`[AI] Input Count: ${inputCount}`);
    if (dimensions) logger.info(`[AI] Dimensions: ${dimensions}`);

    try {
      const response = await this.client!.embeddings.create({
        model,
        input,
        dimensions,
      });

      const duration = Date.now() - startTime;
      logger.info(`[AI] ========== Embedding Response ==========`);
      logger.info(`[AI] Request ID: ${requestId}`);
      logger.info(`[AI] Duration: ${duration}ms`);
      logger.info(`[AI] Vectors: ${response.data.length}`);
      logger.info(`[AI] Vector Dimension: ${response.data[0]?.embedding?.length || 0}`);
      logger.info(`[AI] ==========================================`);

      return response.data.map(item => item.embedding);
    } catch (error) {
      const duration = Date.now() - startTime;
      logger.error(`[AI] ========== Embedding Error ==========`);
      logger.error(`[AI] Request ID: ${requestId}`);
      logger.error(`[AI] Duration: ${duration}ms`);
      logger.error(`[AI] Error:`, error);
      logger.error(`[AI] ======================================`);
      throw this.handleError(error);
    }
  }

  /**
   * 计算文本相似度
   * @param text1 文本1
   * @param text2 文本2
   * @returns 相似度分数 (0-1)
   */
  async calculateSimilarity(text1: string, text2: string): Promise<number> {
    const embeddings = await this.createEmbedding({ input: [text1, text2] });
    return this.cosineSimilarity(embeddings[0], embeddings[1]);
  }

  // ==================== JSON 结构化输出 ====================

  /**
   * 生成 JSON 结构化响应
   * @param prompt 提示
   * @param schema JSON Schema 描述
   * @param systemPrompt 系统提示
   */
  async generateJSON<T = unknown>(
    prompt: string,
    schema: string,
    systemPrompt?: string
  ): Promise<T> {
    const systemMessage = systemPrompt ||
      `你是一个数据处理助手。请严格按照用户要求的JSON格式输出，不要添加任何额外的说明文字。
输出格式要求：${schema}`;

    const response = await this.chatCompletion({
      messages: [
        { role: 'system', content: systemMessage },
        { role: 'user', content: prompt },
      ],
      responseFormat: { type: 'json_object' },
      temperature: 0.3,
    });

    try {
      return JSON.parse(response.content) as T;
    } catch {
      // 尝试提取 JSON
      const jsonMatch = response.content.match(/\{[\s\S]*\}|\[[\s\S]*\]/);
      if (jsonMatch) {
        return JSON.parse(jsonMatch[0]) as T;
      }
      throw new Error('Failed to parse JSON response');
    }
  }

  // ==================== 业务场景方法 ====================

  /**
   * 生成视频标题
   */
  async generateTitle(params: {
    description: string;
    platform: string;
    maxLength?: number;
  }): Promise<string[]> {
    const { description, platform, maxLength = 50 } = params;

    const response = await this.chat(
      `请根据以下视频内容描述，生成5个不同风格的标题：\n${description}`,
      `你是一个专业的自媒体运营专家，擅长为${platform}平台创作吸引人的标题。标题长度不超过${maxLength}个字符。每个标题独占一行，不要添加序号。`
    );

    const titles = response
      .split('\n')
      .filter(line => line.trim())
      .map(line => line.replace(/^\d+[\.\、\)]\s*/, '').trim())
      .filter(title => title.length > 0 && title.length <= maxLength);

    return titles.slice(0, 5);
  }

  /**
   * 生成标签推荐
   */
  async generateTags(params: {
    title: string;
    description?: string;
    platform: string;
    maxTags?: number;
  }): Promise<string[]> {
    const { title, description, platform, maxTags = 5 } = params;

    const response = await this.chat(
      `请根据以下视频信息推荐${maxTags}个相关标签：\n标题：${title}\n${description ? `描述：${description}` : ''}`,
      `你是一个专业的自媒体运营专家，擅长为${platform}平台选择热门标签。每个标签不要带#号，用逗号分隔，只输出标签不要其他内容。`
    );

    const tags = response
      .split(/[,，\n]/)
      .map(tag => tag.trim().replace(/^#/, ''))
      .filter(tag => tag.length > 0);

    return tags.slice(0, maxTags);
  }

  /**
   * 优化内容描述
   */
  async optimizeDescription(params: {
    original: string;
    platform: string;
    maxLength?: number;
  }): Promise<string> {
    const { original, platform, maxLength = 500 } = params;

    const response = await this.chat(
      `请优化以下视频描述：\n${original}`,
      `你是一个专业的自媒体运营专家，擅长为${platform}平台优化视频描述。优化后的描述要吸引人、有互动性，长度不超过${maxLength}个字符。直接输出优化后的描述，不要添加其他说明。`
    );

    return response.slice(0, maxLength);
  }

  /**
   * 生成评论回复
   */
  async generateReply(params: {
    comment: string;
    authorName: string;
    context?: string;
    tone?: 'friendly' | 'professional' | 'humorous';
  }): Promise<string[]> {
    const { comment, authorName, context, tone = 'friendly' } = params;

    const toneDesc = {
      friendly: '友好亲切',
      professional: '专业正式',
      humorous: '幽默风趣',
    };

    const response = await this.chat(
      `粉丝"${authorName}"的评论：${comment}\n${context ? `视频内容：${context}` : ''}\n请生成3个不同的回复选项，每个回复独占一行。`,
      `你是一个自媒体账号运营人员，需要用${toneDesc[tone]}的语气回复粉丝评论。回复要简洁、有互动性，表达对粉丝的感谢。`
    );

    const replies = response
      .split('\n')
      .filter(line => line.trim())
      .map(line => line.replace(/^\d+[\.\、\)]\s*/, '').trim())
      .filter(reply => reply.length > 0);

    return replies.slice(0, 3);
  }

  /**
   * 推荐最佳发布时间
   */
  async recommendPublishTime(params: {
    platform: string;
    contentType: string;
    targetAudience?: string;
  }): Promise<{ time: string; reason: string }[]> {
    const { platform, contentType, targetAudience } = params;

    try {
      const result = await this.generateJSON<{ time: string; reason: string }[]>(
        `请为${platform}平台的${contentType}类型内容推荐3个最佳发布时间。${targetAudience ? `目标受众：${targetAudience}` : ''}`,
        '返回JSON数组，每项包含time(HH:mm格式)和reason字段',
        '你是一个数据分析专家，熟悉各自媒体平台的用户活跃规律。'
      );

      return Array.isArray(result) ? result : [];
    } catch {
      return [
        { time: '12:00', reason: '午休时间，用户活跃度高' },
        { time: '18:00', reason: '下班时间，通勤路上刷手机' },
        { time: '21:00', reason: '晚间黄金时段，用户放松娱乐' },
      ];
    }
  }

  /**
   * 内容审核
   */
  async moderateContent(content: string): Promise<{
    safe: boolean;
    categories: string[];
    suggestion: string;
  }> {
    try {
      return await this.generateJSON(
        `请审核以下内容是否合规：\n${content}`,
        '返回JSON对象，包含safe(布尔值)、categories(问题类别数组，如空则为[])、suggestion(修改建议)字段',
        '你是一个内容审核专家，需要检查内容是否包含：违法违规、色情低俗、暴力血腥、政治敏感、虚假信息等问题。'
      );
    } catch {
      return { safe: true, categories: [], suggestion: '' };
    }
  }

  /**
   * 内容摘要生成
   */
  async summarize(content: string, maxLength: number = 200): Promise<string> {
    return this.quickChat(
      `请将以下内容总结为不超过${maxLength}字的摘要：\n${content}`,
      '你是一个专业的文字编辑，擅长提炼核心内容。直接输出摘要，不要添加任何前缀。'
    );
  }

  /**
   * 文本翻译
   */
  async translate(
    text: string,
    targetLang: string = '英文',
    sourceLang?: string
  ): Promise<string> {
    const prompt = sourceLang
      ? `请将以下${sourceLang}文本翻译成${targetLang}：\n${text}`
      : `请将以下文本翻译成${targetLang}：\n${text}`;

    return this.chat(prompt, '你是一个专业翻译，请提供准确、自然的翻译。直接输出翻译结果。');
  }

  /**
   * 关键词提取
   */
  async extractKeywords(text: string, count: number = 5): Promise<string[]> {
    const response = await this.chat(
      `请从以下文本中提取${count}个关键词，用逗号分隔：\n${text}`,
      '你是一个文本分析专家。只输出关键词，不要其他内容。'
    );

    return response.split(/[,，]/).map(k => k.trim()).filter(k => k).slice(0, count);
  }

  // ==================== 登录页面分析 ====================

  /**
   * 分析登录页面状态
   * @param imageBase64 页面截图的 Base64 编码
   * @param platform 平台名称
   * @returns 登录状态分析结果
   */
  async analyzeLoginStatus(imageBase64: string, platform: string): Promise<LoginStatusAnalysis> {
    const prompt = `请分析这张${platform}平台的网页截图，判断以下内容：

1. 用户是否已经登录成功？（判断依据：是否能看到用户头像、用户名、个人中心入口、创作者后台等已登录状态的元素）
2. 页面上是否有验证码或其他二次验证？（如：图形验证码、滑块验证、短信验证码输入框、扫码验证、人脸识别提示等）
3. 如果有验证，是什么类型的验证？
4. 简要描述当前页面的状态

请严格按照以下JSON格式返回：
{
  "isLoggedIn": true或false,
  "hasVerification": true或false,
  "verificationType": "captcha"或"sms"或"qrcode"或"face"或"slider"或"other"或null,
  "verificationDescription": "验证的具体描述，如果没有验证则为null",
  "pageDescription": "当前页面状态的简要描述",
  "suggestedAction": "建议用户进行的操作，如果不需要则为null"
}`;

    try {
      const response = await this.analyzeImage({
        imageBase64,
        prompt,
        maxTokens: 500,
      });

      // 尝试解析 JSON
      const jsonMatch = response.match(/\{[\s\S]*\}/);
      if (jsonMatch) {
        const result = JSON.parse(jsonMatch[0]);
        return {
          isLoggedIn: Boolean(result.isLoggedIn),
          hasVerification: Boolean(result.hasVerification),
          verificationType: result.verificationType || undefined,
          verificationDescription: result.verificationDescription || undefined,
          pageDescription: result.pageDescription || '无法解析页面状态',
          suggestedAction: result.suggestedAction || undefined,
        };
      }

      // 如果无法解析 JSON，返回默认值
      return {
        isLoggedIn: false,
        hasVerification: false,
        pageDescription: response,
      };
    } catch (error) {
      logger.error('analyzeLoginStatus error:', error);
      return {
        isLoggedIn: false,
        hasVerification: false,
        pageDescription: '分析失败',
      };
    }
  }

  /**
   * 从页面截图中提取账号信息
   * @param imageBase64 页面截图的 Base64 编码
   * @param platform 平台名称
   * @returns 账号信息提取结果
   */
  async extractAccountInfo(imageBase64: string, platform: string): Promise<AccountInfoExtraction> {
    // 根据平台提供更具体的提示
    const platformHints: Record<string, string> = {
      baijiahao: `
百家号平台常见的账号信息位置：
- 页面左上角或侧边栏可能显示作者名称和头像
- 侧边栏"首页"或"个人中心"菜单可以进入账号信息页面
- 头部右上角可能有用户头像和下拉菜单
- 账号设置页面会显示完整的账号信息`,
      douyin: `
抖音平台常见的账号信息位置：
- 页面顶部或右上角显示用户头像和昵称
- 点击头像可以进入个人主页
- 侧边栏可能有"我的"或"个人中心"入口`,
      xiaohongshu: `
小红书平台常见的账号信息位置和格式：
- 页面右上角显示账号名称（昵称），通常在头像旁边
- 账号名称附近或下方会显示"小红书号:xxxxxxx"或"小红书号：xxxxxxx"，这是重要的账号ID
- 小红书号通常是一串字母数字组合，如"ABC123456"
- "粉丝 X" 或 "粉丝 X/500" 表示粉丝数量
- 侧边栏有"笔记管理"、"数据看板"等入口
- 如果看到类似ID格式的字符串（字母+数字组合），很可能就是小红书号`,
      kuaishou: `
快手平台常见的账号信息位置：
- 页面顶部显示用户信息
- 侧边栏有创作者中心入口`,
      weixin_video: `
视频号平台常见的账号信息位置和格式：
- 页面顶部显示账号名称（如"轻尘网络"）
- 账号名称下方通常显示"视频号ID:xxxxxxx"，这个ID是重要信息，请提取冒号后面的字符串
- "视频 X" 表示作品数量（X是数字）→ 填入 worksCount
- "关注者 X" 表示粉丝数量（X是数字）→ 填入 fansCount（注意：视频号用"关注者"表示粉丝）
- 侧边栏有账号设置入口
- 头像通常是圆形图片，显示在账号名称左侧`,
    };

    const platformHint = platformHints[platform] || '';

    const prompt = `请仔细分析这张${platform}平台的网页截图，尝试提取以下账号信息：

1. 账号名称/昵称（这是最重要的信息，请仔细查找页面上的用户名、作者名、昵称等）
2. 账号ID（如果可见）
3. 头像描述（如果可见）
4. 粉丝数量（如果可见）
5. 作品数量（如果可见）
6. 其他相关信息
${platformHint}

【重要提示 - 请务必仔细阅读】：
- 账号名称可能显示在页面顶部、侧边栏、头像旁边等位置
- 如果看到任何类似用户名或昵称的文字，请提取出来
- 即使只找到账号名称，也请返回 found: true

【关于数据统计 - 请特别注意区分】：
- "粉丝"/"粉丝数"/"followers"/"关注者" = 关注该账号的人数 → 填入 fansCount
- "关注"/"关注数"/"following" = 该账号关注的人数 → 这是关注数，不要填入任何字段
- "作品"/"作品数"/"视频"/"笔记"/"文章"/"posts"/"videos" = 该账号发布的内容数量 → 填入 worksCount
- "获赞"/"点赞"/"likes" = 获得的点赞数 → 不要填入作品数

【各平台特殊说明】：
- 视频号：页面上"关注者 X"中的X是粉丝数，"视频 X"中的X是作品数，"视频号ID:xxx"中冒号后的xxx是账号ID
- 抖音：页面上"抖音号:xxx"或"抖音号：xxx"中冒号后的字符串是账号ID
- 小红书：页面上"小红书号:xxx"或"小红书号：xxx"中冒号后的字符串是账号ID（通常是字母数字组合如ABC123456）

【关于弹窗遮挡】：
- 如果页面有弹窗、对话框、遮罩层等遮挡了主要内容，请在返回结果中说明
- 如果因为遮挡无法看清账号信息，请设置 found: false，并在 navigationGuide 中说明"页面有弹窗遮挡，请关闭弹窗后重试"

【常见错误 - 请避免】：
- ❌ 不要把"关注数"（following）当成"作品数"
- ❌ 不要把"获赞数"当成"作品数"
- ✅ 作品数通常标注为"作品"、"视频"、"笔记"等
- ✅ 视频号的"关注者"就是粉丝数
- 如果页面上没有明确显示作品数量，请返回 worksCount: null

如果当前页面确实没有显示任何账号信息，请告诉我应该如何操作才能看到账号信息。

请严格按照以下JSON格式返回：
{
  "found": true或false（是否找到账号信息，只要找到账号名称就算找到）,
  "accountName": "账号名称/昵称，如果找不到则为null",
  "accountId": "账号ID（如视频号ID:xxx中的xxx、抖音号:xxx中的xxx、小红书号:xxx中的xxx），如果找不到则为null",
  "avatarDescription": "头像描述，如果看不到则为null",
  "fansCount": "粉丝数量（纯数字，如'关注者 1'则填1，'粉丝 100'则填100），如果看不到则为null",
  "worksCount": "作品数量（纯数字，如'视频 4'则填4，'作品 10'则填10），如果看不到或不确定则为null",
  "otherInfo": "其他相关信息，如果没有则为null",
  "navigationGuide": "如果没找到账号信息，请描述具体的操作步骤（如：点击左侧菜单的'个人中心'），如果已找到则为null"
}`;

    try {
      const response = await this.analyzeImage({
        imageBase64,
        prompt,
        maxTokens: 600,
      });

      const jsonMatch = response.match(/\{[\s\S]*\}/);
      if (jsonMatch) {
        let jsonStr = jsonMatch[0];

        // 尝试修复常见的 JSON 格式问题
        try {
          // 1. 尝试直接解析
          const result = JSON.parse(jsonStr);
          return {
            found: Boolean(result.found),
            accountName: result.accountName || undefined,
            accountId: result.accountId || undefined,
            avatarDescription: result.avatarDescription || undefined,
            fansCount: result.fansCount || undefined,
            worksCount: result.worksCount || undefined,
            otherInfo: result.otherInfo || undefined,
            navigationGuide: result.navigationGuide || result.navigationSuggestion || undefined,
          };
        } catch {
          // 2. 修复单引号问题：将单引号替换为双引号（注意处理值中的单引号）
          // 先替换属性名的单引号
          jsonStr = jsonStr.replace(/'([^']+)':/g, '"$1":');
          // 替换值的单引号（排除已经是双引号的）
          jsonStr = jsonStr.replace(/:\s*'([^']*)'/g, ': "$1"');
          // 移除末尾多余的逗号
          jsonStr = jsonStr.replace(/,(\s*[}\]])/g, '$1');

          try {
            const result = JSON.parse(jsonStr);
            return {
              found: Boolean(result.found),
              accountName: result.accountName || undefined,
              accountId: result.accountId || undefined,
              avatarDescription: result.avatarDescription || undefined,
              fansCount: result.fansCount || undefined,
              worksCount: result.worksCount || undefined,
              otherInfo: result.otherInfo || undefined,
              navigationGuide: result.navigationGuide || result.navigationSuggestion || undefined,
            };
          } catch (innerError) {
            logger.error('extractAccountInfo JSON parse failed after fix attempt:', innerError);
            logger.debug('Original JSON:', jsonMatch[0]);
            logger.debug('Fixed JSON:', jsonStr);
          }
        }
      }

      return {
        found: false,
        navigationGuide: response,
      };
    } catch (error) {
      logger.error('extractAccountInfo error:', error);
      return {
        found: false,
        navigationGuide: '分析失败，请手动查看页面',
      };
    }
  }

  /**
   * 从页面截图和 HTML 中提取账号信息（增强版）
   * @param imageBase64 页面截图的 Base64 编码
   * @param html 页面 HTML 内容
   * @param platform 平台名称
   * @returns 账号信息提取结果
   */
  async extractAccountInfoWithHtml(
    imageBase64: string,
    html: string,
    platform: string
  ): Promise<AccountInfoExtraction> {
    // 截取 HTML 的关键部分（避免太长）
    const maxHtmlLength = 8000;
    let htmlSnippet = html;
    if (html.length > maxHtmlLength) {
      // 提取可能包含账号信息的部分
      const patterns = [
        /视频号ID[：:]\s*([a-zA-Z0-9_]+)/,
        /finder-uniq-id[^>]*>([^<]+)/,
        /data-clipboard-text="([^"]+)"/,
        /class="[^"]*nickname[^"]*"[^>]*>([^<]+)/,
        /class="[^"]*avatar[^"]*"/,
      ];
      
      // 找到包含关键信息的片段
      let relevantParts: string[] = [];
      for (const pattern of patterns) {
        const match = html.match(pattern);
        if (match) {
          const index = match.index || 0;
          const start = Math.max(0, index - 200);
          const end = Math.min(html.length, index + 500);
          relevantParts.push(html.substring(start, end));
        }
      }
      
      if (relevantParts.length > 0) {
        htmlSnippet = relevantParts.join('\n...\n');
      } else {
        htmlSnippet = html.substring(0, maxHtmlLength);
      }
    }

    const prompt = `请分析以下${platform}平台的网页截图和 HTML 代码，提取账号信息。

【HTML 代码片段】
\`\`\`html
${htmlSnippet}
\`\`\`

【提取目标】
1. **账号ID**（最重要！）：
   - 视频号：查找 "视频号ID:xxx" 或 HTML 中的 data-clipboard-text 属性、finder-uniq-id 元素
   - 示例：sphjl99GV2W1GgN（这种字母数字组合就是视频号ID）
2. 账号名称/昵称
3. 粉丝数量（视频号显示为"关注者 X"）
4. 作品数量（视频号显示为"视频 X"）

【特别注意】
- 视频号ID 通常是一串字母数字组合，如 "sphjl99GV2W1GgN"
- 在 HTML 中可能出现在 data-clipboard-text 属性中
- 或者在 class 为 finder-uniq-id 的元素内

请严格按照以下 JSON 格式返回：
{
  "found": true,
  "accountId": "视频号ID（如 sphjl99GV2W1GgN，不要加前缀）",
  "accountName": "账号名称",
  "fansCount": "粉丝数（纯数字）",
  "worksCount": "作品数（纯数字）"
}`;

    try {
      const response = await this.analyzeImage({
        imageBase64,
        prompt,
        maxTokens: 500,
      });

      logger.info(`[AIService] extractAccountInfoWithHtml response:`, response);

      const jsonMatch = response.match(/\{[\s\S]*\}/);
      if (jsonMatch) {
        try {
          const result = JSON.parse(jsonMatch[0]);
          return {
            found: Boolean(result.found),
            accountName: result.accountName || undefined,
            accountId: result.accountId || undefined,
            avatarDescription: result.avatarDescription || undefined,
            fansCount: result.fansCount || undefined,
            worksCount: result.worksCount || undefined,
            otherInfo: result.otherInfo || undefined,
            navigationGuide: result.navigationGuide || undefined,
          };
        } catch (parseError) {
          logger.error('extractAccountInfoWithHtml JSON parse error:', parseError);
        }
      }

      return { found: false };
    } catch (error) {
      logger.error('extractAccountInfoWithHtml error:', error);
      return { found: false };
    }
  }

  /**
   * 获取页面操作指导
   * @param imageBase64 页面截图的 Base64 编码
   * @param platform 平台名称
   * @param goal 操作目标（如："获取账号信息"、"完成登录"）
   * @returns 页面操作指导
   */
  async getPageOperationGuide(
    imageBase64: string,
    platform: string,
    goal: string
  ): Promise<PageOperationGuide> {
    // 根据目标添加特定的识别提示
    let additionalHints = '';
    if (goal.includes('上传') || goal.includes('upload')) {
      additionalHints = `
【重要】关于视频上传入口的识别提示：
- 百家号：上传区域通常是一个虚线边框的大区域，包含云朵图标和"点击上传或将文件拖动入此区域"文字，整个虚线框区域都是可点击的上传入口
- 微信视频号：
  * 如果在首页，需要找"发表视频"按钮（通常是橙色/红色按钮）
  * 如果在发布页面（标题显示"发表动态"），左侧会有一个带"+"号的矩形上传区域，下方有格式说明文字（如"上传时长8小时内...MP4/H.264格式"），点击这个"+"号区域即可上传
- 小红书：上传区域通常有"上传视频"文字或拖拽区域
- 抖音：上传区域通常是一个带有"发布视频"或上传图标的区域
- 快手：找"上传视频"或拖拽上传区域
- B站：找"投稿"或上传视频按钮

如果页面不是发布页面，需要先找到进入发布页面的入口按钮（如"发表视频"、"发布"、"上传"、"投稿"等）。`;
    }

    const prompt = `请分析这张${platform}平台的网页截图，我的目标是：${goal}
${additionalHints}

请告诉我下一步应该进行什么操作。仔细观察页面上的所有可点击元素，包括：
- 带有虚线边框的拖拽上传区域（这是很常见的上传入口）
- 带有"上传"、"发布"、"发表"、"投稿"等文字的按钮
- 带有云朵、加号、上传箭头等图标的区域

如果需要点击某个元素，请尽可能提供：
1. 操作类型（点击、输入、滚动、等待、跳转）
2. 目标元素的描述（文字内容、位置描述、视觉特征）
3. 如果是点击操作，估计目标在截图中的大致位置（假设截图尺寸为 1920x1080，给出x,y坐标）
4. 如果是输入操作，需要输入什么内容

请严格按照以下JSON格式返回：
{
  "hasAction": true或false（是否需要执行操作）,
  "actionType": "click"或"input"或"scroll"或"wait"或"navigate"或null,
  "targetDescription": "目标元素的文字描述（如：虚线框上传区域、发表视频按钮等）",
  "targetSelector": "可能的CSS选择器，常见的有：[class*='upload'], [class*='drag'], button:has-text('发布'), button:has-text('上传') 等",
  "targetPosition": {"x": 数字, "y": 数字} 或 null,
  "inputText": "需要输入的文字，如果不需要输入则为null",
  "explanation": "操作说明和原因"
}`;

    try {
      const response = await this.analyzeImage({
        imageBase64,
        prompt,
        maxTokens: 500,
      });

      const jsonMatch = response.match(/\{[\s\S]*\}/);
      if (jsonMatch) {
        const result = JSON.parse(jsonMatch[0]);
        return {
          hasAction: Boolean(result.hasAction),
          actionType: result.actionType || undefined,
          targetDescription: result.targetDescription || undefined,
          targetSelector: result.targetSelector || undefined,
          targetPosition: result.targetPosition || undefined,
          inputText: result.inputText || undefined,
          explanation: result.explanation || '无法解析操作指导',
        };
      }

      return {
        hasAction: false,
        explanation: response,
      };
    } catch (error) {
      logger.error('getPageOperationGuide error:', error);
      return {
        hasAction: false,
        explanation: '分析失败',
      };
    }
  }

  /**
   * 分析视频上传进度
   * @param imageBase64 页面截图的base64
   * @param platform 平台名称
   * @returns 上传进度分析结果
   */
  async analyzeUploadProgress(
    imageBase64: string,
    platform: string
  ): Promise<{
    isUploading: boolean;
    isComplete: boolean;
    isFailed: boolean;
    progress: number | null;
    statusDescription: string;
  }> {
    const prompt = `请分析这张${platform}平台的网页截图，判断视频上传的状态。

请仔细观察页面上是否有以下元素：
1. 上传进度条（通常显示百分比，如 "50%"、"上传中 75%" 等）
2. 上传完成标志（如 "上传成功"、"✓"、绿色勾选图标、"100%"、视频预览画面）
3. 上传失败标志（如 "上传失败"、"重试"、红色错误提示）
4. 视频处理中的提示（如 "处理中"、"转码中"）

请严格按照以下JSON格式返回：
{
  "isUploading": true或false（是否正在上传中，有进度条显示但未完成）,
  "isComplete": true或false（是否上传完成，进度达到100%或显示成功标志）,
  "isFailed": true或false（是否上传失败）,
  "progress": 数字或null（当前上传进度百分比，如果能识别到的话，范围0-100）,
  "statusDescription": "当前状态的文字描述"
}`;

    try {
      const response = await this.analyzeImage({
        imageBase64,
        prompt,
        maxTokens: 300,
      });

      const jsonMatch = response.match(/\{[\s\S]*\}/);
      if (jsonMatch) {
        const result = JSON.parse(jsonMatch[0]);
        return {
          isUploading: Boolean(result.isUploading),
          isComplete: Boolean(result.isComplete),
          isFailed: Boolean(result.isFailed),
          progress: typeof result.progress === 'number' ? result.progress : null,
          statusDescription: result.statusDescription || '未知状态',
        };
      }

      return {
        isUploading: false,
        isComplete: false,
        isFailed: false,
        progress: null,
        statusDescription: response,
      };
    } catch (error) {
      logger.error('analyzeUploadProgress error:', error);
      return {
        isUploading: false,
        isComplete: false,
        isFailed: false,
        progress: null,
        statusDescription: '分析失败',
      };
    }
  }

  /**
   * 分析点击发布按钮后的发布进度
   * @param imageBase64 页面截图的base64
   * @param platform 平台名称
   * @returns 发布进度分析结果
   */
  async analyzePublishProgress(
    imageBase64: string,
    platform: string
  ): Promise<{
    isPublishing: boolean;
    isComplete: boolean;
    isFailed: boolean;
    progress: number | null;
    needAction: boolean;
    actionDescription: string | null;
    statusDescription: string;
  }> {
    const prompt = `请分析这张${platform}平台的网页截图，判断点击发布按钮后的发布状态。

请仔细观察页面上是否有以下元素：
1. 发布/上传进度条（通常显示百分比，如 "发布中 50%"、"上传中 79%"、"正在发布..."、进度圈等）
2. 后台上传提示（如 "作品上传中，请勿关闭页面"、"上传完成后将自动发布" + 百分比进度）
3. 发布成功标志（如 "发布成功"、"已发布"、绿色勾选图标）
4. 发布失败标志（如 "发布失败"、"重试"、红色错误提示）
5. 需要处理的弹窗（如确认弹窗、验证码弹窗、协议确认等）
6. 正在处理中的提示（如 "视频处理中"、"审核中"、loading动画）

【重要】特别注意：
- 抖音等平台在点击发布后可能会跳转到作品管理页面，但页面右下角会有一个小的上传进度框显示"作品上传中，请勿关闭页面 XX%"
- 只要页面上有任何上传/发布进度条（无论在页面哪个位置），都应该认为发布尚未完成
- 只有当进度达到100%且没有任何进度提示时，才算发布完成

请严格按照以下JSON格式返回：
{
  "isPublishing": true或false（是否正在发布/上传中，只要有进度条或loading显示就返回true）,
  "isComplete": true或false（是否完全完成，进度100%且无任何上传提示才返回true）,
  "isFailed": true或false（是否发布失败）,
  "progress": 数字或null（当前进度百分比，仔细查找页面上的百分比数字，范围0-100）,
  "needAction": true或false（是否需要用户处理某些操作，如确认弹窗）,
  "actionDescription": "需要执行的操作描述，如果不需要操作则为null",
  "statusDescription": "当前状态的文字描述（包括进度信息）"
}`;

    try {
      const response = await this.analyzeImage({
        imageBase64,
        prompt,
        maxTokens: 400,
      });

      const jsonMatch = response.match(/\{[\s\S]*\}/);
      if (jsonMatch) {
        const result = JSON.parse(jsonMatch[0]);
        return {
          isPublishing: Boolean(result.isPublishing),
          isComplete: Boolean(result.isComplete),
          isFailed: Boolean(result.isFailed),
          progress: typeof result.progress === 'number' ? result.progress : null,
          needAction: Boolean(result.needAction),
          actionDescription: result.actionDescription || null,
          statusDescription: result.statusDescription || '未知状态',
        };
      }

      return {
        isPublishing: false,
        isComplete: false,
        isFailed: false,
        progress: null,
        needAction: false,
        actionDescription: null,
        statusDescription: response,
      };
    } catch (error) {
      logger.error('analyzePublishProgress error:', error);
      return {
        isPublishing: false,
        isComplete: false,
        isFailed: false,
        progress: null,
        needAction: false,
        actionDescription: null,
        statusDescription: '分析失败',
      };
    }
  }

  /**
   * 通过 HTML 分析页面并返回操作指导
   * @param html 页面 HTML 内容
   * @param platform 平台名称
   * @param goal 操作目标
   * @returns 页面操作指导（包含精确的 CSS 选择器）
   */
  async analyzeHtmlForOperation(
    html: string,
    platform: string,
    goal: string
  ): Promise<PageOperationGuide> {
    this.ensureAvailable();

    // 简化 HTML，移除不必要的内容，保留关键元素
    const simplifiedHtml = this.simplifyHtml(html);

    const prompt = `你是一个网页自动化助手。我正在${platform}平台上操作，目标是：${goal}

以下是当前页面的HTML结构（已简化）：
\`\`\`html
${simplifiedHtml}
\`\`\`

请分析这个页面，告诉我下一步应该进行什么操作来达成目标。

要求：
1. 识别页面当前状态（是否已登录、是否有验证码、是否有弹窗等）
2. 找出需要操作的目标元素（按钮、链接、输入框等）
3. 提供精确的 CSS 选择器来定位该元素
4. 选择器要尽可能唯一和稳定（优先使用 id、data-* 属性、唯一 class）

请严格按照以下JSON格式返回：
{
  "hasAction": true或false,
  "actionType": "click" | "input" | "scroll" | "wait" | null,
  "targetSelector": "精确的CSS选择器，如 #login-btn 或 button[data-action='login'] 或 .login-button",
  "targetDescription": "目标元素的描述",
  "inputText": "如果是输入操作，需要输入的内容，否则为null",
  "explanation": "操作说明和当前页面状态分析"
}

【重要】CSS选择器格式要求：
- 必须使用标准CSS选择器语法，不要使用jQuery语法
- 不要使用 :contains()（这是jQuery语法，不是标准CSS）
- 如果需要按文本匹配，使用 :has-text("文本") 格式，例如：button:has-text("发布")
- 优先使用 id 选择器，其次是 data-* 属性，再次是唯一的 class
- 如果有多个相似元素，使用更具体的选择器（如 :first-child, :nth-child(n)）
- 如果页面已完成目标（如已登录成功），返回 hasAction: false`;

    try {
      const response = await this.chat(prompt, undefined, this.models.chat);

      const jsonMatch = response.match(/\{[\s\S]*\}/);
      if (jsonMatch) {
        const result = JSON.parse(jsonMatch[0]);
        return {
          hasAction: Boolean(result.hasAction),
          actionType: result.actionType || undefined,
          targetDescription: result.targetDescription || undefined,
          targetSelector: result.targetSelector || undefined,
          inputText: result.inputText || undefined,
          explanation: result.explanation || '无法解析操作指导',
        };
      }

      return {
        hasAction: false,
        explanation: response,
      };
    } catch (error) {
      logger.error('analyzeHtmlForOperation error:', error);
      return {
        hasAction: false,
        explanation: '分析失败',
      };
    }
  }

  /**
   * 简化 HTML，移除不必要的内容
   */
  private simplifyHtml(html: string): string {
    let simplified = html;

    // 移除 script 标签及内容
    simplified = simplified.replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, '');

    // 移除 style 标签及内容
    simplified = simplified.replace(/<style\b[^<]*(?:(?!<\/style>)<[^<]*)*<\/style>/gi, '');

    // 移除 HTML 注释
    simplified = simplified.replace(/<!--[\s\S]*?-->/g, '');

    // 移除 svg 内容（保留标签）
    simplified = simplified.replace(/<svg[^>]*>[\s\S]*?<\/svg>/gi, '<svg></svg>');

    // 移除 noscript
    simplified = simplified.replace(/<noscript\b[^<]*(?:(?!<\/noscript>)<[^<]*)*<\/noscript>/gi, '');

    // 移除 data:image 等 base64 内容
    simplified = simplified.replace(/data:[^"'\s]+/g, 'data:...');

    // 移除过长的属性值（如内联样式）
    simplified = simplified.replace(/style="[^"]{100,}"/gi, 'style="..."');

    // 压缩连续空白
    simplified = simplified.replace(/\s+/g, ' ');

    // 限制总长度（避免 token 超限）
    const maxLength = 30000;
    if (simplified.length > maxLength) {
      // 尝试保留 body 部分
      const bodyMatch = simplified.match(/<body[^>]*>([\s\S]*)<\/body>/i);
      if (bodyMatch) {
        simplified = bodyMatch[1];
      }
      // 如果还是太长，截断
      if (simplified.length > maxLength) {
        simplified = simplified.substring(0, maxLength) + '\n... (HTML 已截断)';
      }
    }

    return simplified.trim();
  }

  // ==================== 发布辅助 ====================

  /**
   * 分析发布页面状态
   * @param imageBase64 页面截图的 Base64 编码
   * @param platform 平台名称
   * @returns 发布状态分析结果
   */
  async analyzePublishStatus(imageBase64: string, platform: string): Promise<PublishStatusAnalysis> {
    const prompt = `请分析这张${platform}平台视频发布页面的截图，判断当前的发布状态。

请仔细观察页面，判断：
1. 是否正在上传/处理视频（显示进度条、loading 等）
2. 是否发布成功（显示成功提示、跳转到作品列表等）
3. 是否发布失败（显示错误提示）
4. 是否需要输入验证码（图片验证码、滑块验证、短信验证码等）
5. 是否需要进行其他操作才能继续发布（如点击发布按钮、确认信息等）

请严格按照以下JSON格式返回：
{
  "status": "uploading" 或 "processing" 或 "success" 或 "failed" 或 "need_captcha" 或 "need_action",
  "captchaType": "image" 或 "sms" 或 "slider" 或 "other" 或 null（仅当 status 为 need_captcha 时填写）,
  "captchaDescription": "验证码的具体描述（如：请输入图片中的4位数字）",
  "errorMessage": "错误信息（仅当 status 为 failed 时填写）",
  "nextAction": {
    "actionType": "click" 或 "input" 或 "wait",
    "targetDescription": "需要操作的目标描述",
    "targetSelector": "目标元素的CSS选择器（如果能推断的话）"
  } 或 null,
  "pageDescription": "当前页面状态的详细描述",
  "confidence": 0-100 之间的数字，表示你对这个判断的信心程度
}

注意：
- 如果看到"发布成功"、"上传完成"等字样，status 应为 "success"
- 如果看到验证码输入框、滑块验证等，status 应为 "need_captcha"
- 如果页面显示发布按钮但还未点击，status 应为 "need_action"
- 如果页面正在加载或显示进度，status 应为 "uploading" 或 "processing"`;

    try {
      const response = await this.analyzeImage({
        imageBase64,
        prompt,
        maxTokens: 600,
      });

      const jsonMatch = response.match(/\{[\s\S]*\}/);
      if (jsonMatch) {
        const result = JSON.parse(jsonMatch[0]);
        return {
          status: result.status || 'need_action',
          captchaType: result.captchaType || undefined,
          captchaDescription: result.captchaDescription || undefined,
          errorMessage: result.errorMessage || undefined,
          nextAction: result.nextAction || undefined,
          pageDescription: result.pageDescription || '无法解析页面状态',
          confidence: result.confidence || 50,
        };
      }

      return {
        status: 'need_action',
        pageDescription: response,
        confidence: 30,
      };
    } catch (error) {
      logger.error('analyzePublishStatus error:', error);
      return {
        status: 'need_action',
        pageDescription: '分析失败',
        confidence: 0,
      };
    }
  }

  /**
   * 分析发布页面 HTML 并获取操作指导
   * @param html 页面 HTML 内容
   * @param platform 平台名称
   * @param currentStatus 当前发布状态
   * @returns 操作指导
   */
  async analyzePublishPageHtml(
    html: string,
    platform: string,
    currentStatus: string
  ): Promise<PageOperationGuide> {
    const simplifiedHtml = this.simplifyHtml(html);

    const prompt = `你是一个自动化发布助手。我正在${platform}平台上发布视频，当前状态是：${currentStatus}

以下是当前页面的HTML结构（已简化）：
\`\`\`html
${simplifiedHtml}
\`\`\`

请分析这个页面，告诉我下一步应该进行什么操作来完成发布。

要求：
1. 如果需要点击"发布"按钮，找到正确的发布按钮
2. 如果需要输入验证码，找到验证码输入框
3. 如果需要确认/关闭弹窗，找到相应按钮
4. 提供精确的 CSS 选择器来定位目标元素

请严格按照以下JSON格式返回：
{
  "hasAction": true或false,
  "actionType": "click" | "input" | "wait" | null,
  "targetSelector": "精确的CSS选择器",
  "targetDescription": "目标元素的描述",
  "inputText": "如果是输入操作，需要输入的内容，否则为null",
  "explanation": "操作说明"
}`;

    try {
      const response = await this.chat(prompt, undefined, this.models.chat);

      const jsonMatch = response.match(/\{[\s\S]*\}/);
      if (jsonMatch) {
        const result = JSON.parse(jsonMatch[0]);
        return {
          hasAction: Boolean(result.hasAction),
          actionType: result.actionType || undefined,
          targetDescription: result.targetDescription || undefined,
          targetSelector: result.targetSelector || undefined,
          inputText: result.inputText || undefined,
          explanation: result.explanation || '无法解析操作指导',
        };
      }

      return {
        hasAction: false,
        explanation: response,
      };
    } catch (error) {
      logger.error('analyzePublishPageHtml error:', error);
      return {
        hasAction: false,
        explanation: '分析失败',
      };
    }
  }

  // ==================== 工具方法 ====================

  /**
   * 计算余弦相似度
   */
  private cosineSimilarity(vec1: number[], vec2: number[]): number {
    if (vec1.length !== vec2.length) {
      throw new Error('Vectors must have the same length');
    }

    let dotProduct = 0;
    let norm1 = 0;
    let norm2 = 0;

    for (let i = 0; i < vec1.length; i++) {
      dotProduct += vec1[i] * vec2[i];
      norm1 += vec1[i] * vec1[i];
      norm2 += vec2[i] * vec2[i];
    }

    return dotProduct / (Math.sqrt(norm1) * Math.sqrt(norm2));
  }

  /**
   * 错误处理
   */
  private handleError(error: unknown): Error {
    if (error instanceof OpenAI.APIError) {
      const status = error.status;
      const message = error.message;

      switch (status) {
        case 400:
          return new Error(`请求参数错误: ${message}`);
        case 401:
          return new Error('API Key 无效或已过期');
        case 403:
          return new Error('没有权限访问该模型');
        case 404:
          return new Error('请求的模型不存在');
        case 429:
          return new Error('请求频率超限，请稍后重试');
        case 500:
          return new Error('服务器内部错误，请稍后重试');
        default:
          return new Error(`API 错误 (${status}): ${message}`);
      }
    }

    if (error instanceof Error) {
      return error;
    }

    return new Error('未知错误');
  }
}

// 导出单例实例
export const aiService = new QwenAIService();

// 为了向后兼容，也导出别名
export const AIService = QwenAIService;
