// api-config-builder.js
// API配置构建模块

(function() {
  'use strict';

  // =====================
  // buildCustomApiConfig: 兼容自定义模型调用
  // =====================
  /**
   * 构建自定义 API 访问配置。
   * 该函数负责根据传入的参数，生成一个完整的 API 请求配置对象，
   * 包括请求端点、模型ID、请求头、请求体构建器、响应提取器以及流式支持等。
   *
   * 主要逻辑：
   * 1. 端点处理：如果 `window.modelDetector` 存在且能提供完整端点，则优先使用。
   *    否则，如果提供的 `customApiEndpoint` 不规范（不含 `/v1/` 或 `/v1` 结尾），
   *    会自动拼接 `/v1/chat/completions`。
   * 2. 请求格式自动推断：如果 `customRequestFormat` 为空且端点以 `/v1/chat/completions` 结尾，
   *    则自动设置为 `openai` 格式。
   * 3. 模型ID获取：如果 `window.modelDetector` 存在，则尝试获取当前选择的模型ID。
   * 4. 根据 `customRequestFormat` (如 'openai', 'anthropic', 'gemini' 等) 构建特定配置：
   *    - 设置认证头 (Authorization, x-api-key)。
   *    - 定义 `bodyBuilder` 用于构建非流式请求的请求体。
   *    - 定义 `streamBodyBuilder` 用于构建流式请求的请求体。
   *    - 定义 `responseExtractor` 用于从 API 响应中提取所需内容。
   *    - 设置 `streamSupport` 标记是否支持流式响应。
   *    - 为 Gemini 等特殊模型处理端点参数 (如 `alt=sse`)。
   * 5. 对于不支持的 `customRequestFormat`，会抛出错误。
   * 6. 最终返回构建好的 `config` 对象。
   *
   * @param {string} key API 密钥。
   * @param {string} customApiEndpoint 自定义 API 端点基础 URL。
   * @param {string} customModelId 自定义模型 ID。
   * @param {string} customRequestFormat 自定义请求格式 (例如 'openai', 'anthropic', 'gemini')。
   * @param {number} [temperature] 模型温度参数，控制生成文本的随机性。
   * @param {number} [max_tokens] 模型最大输出 token 数。
   * @returns {object} 构建好的 API 配置对象，包含 endpoint, modelName, headers, bodyBuilder, responseExtractor, streamSupport, streamBodyBuilder 等。
   * @throws {Error} 如果 customRequestFormat 不被支持。
   */

  // 辅助函数：如果 userContent 是数组，则提取其中的文本内容
  const extractTextFromUserContent = (userContent) => {
    if (Array.isArray(userContent)) {
      const textPart = userContent.find(part => part.type === 'text');
      return textPart ? textPart.text : '';
    }
    return userContent; // 假设已经是字符串
  };

  // 辅助函数：将 OpenAI 风格的 userContent 转换为 Gemini 格式
  const convertOpenAIToGeminiParts = (userContent) => {
    if (Array.isArray(userContent)) {
      return userContent.map(part => {
        if (part.type === 'text') {
          return { text: part.text };
        } else if (part.type === 'image_url' && part.image_url && part.image_url.url) {
          const base64Data = part.image_url.url.split(',')[1];
          if (!base64Data) return null;
          const mimeType = part.image_url.url.match(/^data:(image\/\w+);base64,/)?.[1] || 'image/jpeg';
          return { inlineData: { mimeType: mimeType, data: base64Data } };
        }
        return null;
      }).filter(p => p);
    }
    return [{ text: userContent }]; // 假设为字符串
  };

  // 辅助函数：将 OpenAI 风格的 userContent 转换为 Anthropic 格式
  const convertOpenAIToAnthropicContent = (userContent) => {
    if (Array.isArray(userContent)) {
      return userContent.map(part => {
        if (part.type === 'text') {
          return { type: 'text', text: part.text };
        } else if (part.type === 'image_url' && part.image_url && part.image_url.url) {
          const base64Data = part.image_url.url.split(',')[1];
          if (!base64Data) return null;
          const mediaType = part.image_url.url.match(/^data:(image\/\w+);base64,/)?.[1] || 'image/jpeg';
          return { type: 'image', source: { type: 'base64', media_type: mediaType, data: base64Data } };
        }
        return null;
      }).filter(p => p);
    }
    return [{ type: 'text', text: userContent }]; // 假设为字符串
  };

  const appendPathSegment = (base, segment) => {
    if (!base) return segment || '';
    if (!segment) return base;
    const hasTrailingSlash = base.endsWith('/');
    const hasLeadingSlash = segment.startsWith('/');
    if (hasTrailingSlash && hasLeadingSlash) {
      return base + segment.slice(1);
    }
    if (!hasTrailingSlash && !hasLeadingSlash) {
      return `${base}/${segment}`;
    }
    return base + segment;
  };

  const normalizeOpenAIEndpointForChatbot = (baseApiUrlInput, format, endpointMode = 'auto', targetSegment) => {
    if (!baseApiUrlInput || typeof baseApiUrlInput !== 'string') {
      throw new Error('自定义模型需要提供 API Base URL');
    }

    const trimmed = baseApiUrlInput.trim();
    if (!trimmed) {
      throw new Error('自定义模型需要提供 API Base URL');
    }

    const mode = endpointMode || 'auto';
    const normalizedSegment = (targetSegment
      ? targetSegment
      : ((format || '').toLowerCase() === 'anthropic' ? 'messages' : 'chat/completions'))
      .replace(/^\/+/, '');

    const lower = trimmed.toLowerCase();
    const base = trimmed.replace(/\/+$/, '');
    const v1Segment = normalizedSegment.startsWith('v1/') ? normalizedSegment : `v1/${normalizedSegment}`;

    const terminalPaths = [
      normalizedSegment,
      `/${normalizedSegment}`,
      v1Segment,
      `/${v1Segment}`
    ];

    if (terminalPaths.some(path => lower.endsWith(path))) {
      return base;
    }

    if (mode === 'manual') {
      return base;
    }

    if (mode === 'chat') {
      return appendPathSegment(base, normalizedSegment);
    }

    if (/\/v1$/.test(lower)) {
      return appendPathSegment(base, normalizedSegment);
    }

    return appendPathSegment(base, v1Segment);
  };

  function buildCustomApiConfig(key, customApiEndpoint, customModelId, customRequestFormat, temperature, max_tokens, options = {}) {
    let apiEndpoint = customApiEndpoint;
    let modelId = customModelId;
    const endpointMode = options.endpointMode || 'auto';
    let resolvedRequestFormat = customRequestFormat;

    // 代理服务器支持
    // 支持三种配置方式：
    // 1. options.useProxy - 单次调用时指定
    // 2. options.proxyMode - 'proxy' 强制使用代理，'direct' 强制直连，'auto' 自动选择
    // 3. window.PBX_PROXY_MODE - 全局配置（可在 index.html 或配置文件中设置）
    const globalProxyMode = (typeof window !== 'undefined' && window.PBX_PROXY_MODE) || 'auto';
    const useProxy = options.useProxy ||
                     options.proxyMode === 'proxy' ||
                     (options.proxyMode !== 'direct' && globalProxyMode === 'proxy');
    const proxyBaseUrl = options.proxyBaseUrl ||
                        (typeof window !== 'undefined' && window.PBX_PROXY_BASE_URL) ||
                        'http://localhost:3456';
    const provider = options.provider || 'openai';

    // 获取当前选择的模型ID（如果有模型检测模块）
    if (typeof window.modelDetector !== 'undefined') {
      const currentModelId = window.modelDetector.getCurrentModelId();
      if (currentModelId) {
        modelId = currentModelId;
      }
    }

    // 新增：如果 customRequestFormat 为空且 endpoint 以 /v1/chat/completions 结尾，则自动设为 openai
    if ((!resolvedRequestFormat || resolvedRequestFormat === '') && apiEndpoint && apiEndpoint.endsWith('/v1/chat/completions')) {
      resolvedRequestFormat = 'openai';
    }

    // 检查是否有模型检测模块，如果有则使用其提供的完整端点
    // 注意：现在 resolvedRequestFormat 已经确定，端点标准化会使用正确的路径
    if (typeof window.modelDetector !== 'undefined' && typeof window.modelDetector.getFullApiEndpoint === 'function') {
      const fullEndpoint = window.modelDetector.getFullApiEndpoint();
      if (fullEndpoint) {
        apiEndpoint = fullEndpoint;
      } else {
        apiEndpoint = normalizeOpenAIEndpointForChatbot(apiEndpoint, resolvedRequestFormat, endpointMode);
      }
    } else {
      apiEndpoint = normalizeOpenAIEndpointForChatbot(apiEndpoint, resolvedRequestFormat, endpointMode);
    }

    // 如果使用代理服务器，重写端点
    if (useProxy && proxyBaseUrl) {
      console.log('[ApiConfigBuilder] 使用代理服务器:', {
        provider,
        proxyBaseUrl,
        originalEndpoint: apiEndpoint
      });
      apiEndpoint = `${proxyBaseUrl.replace(/\/$/, '')}/api/llm/${provider}/v1/chat/completions`;
    }

    const config = {
      endpoint: apiEndpoint,
      modelName: modelId, // 使用最新获取的modelId
      headers: { 'Content-Type': 'application/json' },
      bodyBuilder: null,
      responseExtractor: null,
      streamSupport: false, // 默认不支持流式
      streamBodyBuilder: null // 流式请求构建器
    };

    const normalizedFormat = (resolvedRequestFormat || 'openai').toLowerCase();

    // 在使用代理服务器模式时，即使前端没有 API Key，也可以正常发送请求
    // 因为 API Key 会在后端代理服务器中配置
    const isProxyMode = useProxy && proxyBaseUrl;
    const authKey = key || ''; // 允许空 Key（代理模式）

    switch (normalizedFormat) {
      case 'openai':
      case 'openai-vision': // Add a specific format for vision-enabled OpenAI
        // 代理模式下，Authorization 头可选（后端会使用环境变量中的 Key）
        if (authKey) {
          config.headers['Authorization'] = `Bearer ${authKey}`;
        }
        config.bodyBuilder = (sys_prompt, user_content) => ({ // user_content can be string or array
          model: modelId,
          messages: [{ role: "system", content: sys_prompt }, { role: "user", content: user_content }],
          temperature: temperature ?? 0.5,
          max_tokens: max_tokens ?? 8000
        });

        // 流式请求构建器 - 针对转接站兼容性优化
        config.streamBodyBuilder = (sys, msgs, user_content) => {
          // 检测是否是 Claude 模型 + OpenAI 格式（转接站场景）
          const isClaudeViaProxy = modelId && typeof modelId === 'string' && modelId.toLowerCase().includes('claude');

          if (isClaudeViaProxy) {
            // 对于通过转接站使用的 Claude 模型，将 system prompt 合并到第一条 user 消息中
            // 因为很多转接站不正确处理 system role
            console.log('[ApiConfigBuilder] 🔧 检测到通过转接站使用 Claude，将 system prompt 合并到 user 消息');

            // 构建合并后的第一条用户消息
            let firstUserMessage = sys ? `${sys}\n\n---\n\n` : '';
            if (typeof user_content === 'string') {
              firstUserMessage += user_content;
            } else if (Array.isArray(user_content)) {
              // 如果是多模态内容，只提取文本部分合并
              const textPart = user_content.find(p => p.type === 'text');
              if (textPart) {
                firstUserMessage += textPart.text;
              }
            }

            return {
              model: modelId,
              messages: [
                ...msgs,
                { role: 'user', content: firstUserMessage }
              ],
              temperature: temperature ?? 0.5,
              max_tokens: max_tokens ?? 8000,
              stream: true
            };
          } else {
            // 其他模型使用标准格式
            return {
              model: modelId,
              messages: [
                { role: 'system', content: sys },
                ...msgs,
                { role: 'user', content: user_content }
              ],
              temperature: temperature ?? 0.5,
              max_tokens: max_tokens ?? 8000,
              stream: true
            };
          }
        };
        config.responseExtractor = (data) => data?.choices?.[0]?.message?.content;
        config.streamSupport = true;
        break;
      case 'anthropic':
        // 代理模式下，x-api-key 可选（后端会使用环境变量中的 Key）
        if (authKey) {
          config.headers['x-api-key'] = authKey;
        }
        config.headers['anthropic-version'] = '2023-06-01';
        config.bodyBuilder = (sys_prompt, user_content) => ({
          model: modelId,
          system: sys_prompt,
          messages: [{ role: "user", content: convertOpenAIToAnthropicContent(user_content) }],
          temperature: temperature ?? 0.5,
          max_tokens: max_tokens ?? 8000
        });
        config.streamBodyBuilder = (sys, msgs, user_content) => {
          return {
            model: modelId,
            system: sys,
            messages: msgs.length ?
              [...msgs, { role: 'user', content: convertOpenAIToAnthropicContent(user_content) }] :
              [{ role: 'user', content: convertOpenAIToAnthropicContent(user_content) }],
            max_tokens: max_tokens ?? 8000,
            temperature: temperature ?? 0.5,
            stream: true
          };
        };
        config.responseExtractor = (data) => data?.content?.[0]?.text;
        config.streamSupport = true;
        config.streamHandler = 'claude';
        break;
      case 'gemini':
      case 'gemini-preview': // Assuming gemini-preview also supports this
        let baseUrl = config.endpoint.split('?')[0];
        // 代理模式下，API Key 由后端配置，前端不需要传递
        if (isProxyMode) {
          // 代理模式：使用后端配置的 Key，前端不传递
          config.endpoint = `${proxyBaseUrl.replace(/\/$/, '')}/api/llm/gemini/v1/models/${modelId || 'gemini-pro'}:streamGenerateContent?alt=sse`;
          config.streamEndpoint = config.endpoint;
        } else {
          // 直连模式：使用前端 Key
          config.endpoint = `${baseUrl}?key=${key}`;
          config.streamEndpoint = `${baseUrl}?key=${key}&alt=sse`;
        }
        const geminiModelIdToUse = modelId || (normalizedFormat === 'gemini-preview' ? 'gemini-1.5-flash-latest' : 'gemini-pro'); // Updated default for preview
        config.modelName = geminiModelIdToUse;

        config.bodyBuilder = (sys_prompt, user_content) => ({
          contents: [{ role: "user", parts: convertOpenAIToGeminiParts(user_content) }],
          generationConfig: { temperature: temperature ?? 0.5, maxOutputTokens: max_tokens ?? 8192 },
          ...(sys_prompt && { systemInstruction: { parts: [{ text: sys_prompt }] }})
        });
        config.streamBodyBuilder = (sys, msgs, user_content) => {
          const geminiMessages = [];
          if (msgs.length) {
            for (const msg of msgs) {
              geminiMessages.push({ role: msg.role === 'assistant' ? 'model' : 'user', parts: convertOpenAIToGeminiParts(msg.content) });
            }
          }
          geminiMessages.push({ role: 'user', parts: convertOpenAIToGeminiParts(user_content) });
          return {
            contents: geminiMessages,
            generationConfig: {
              temperature: temperature ?? 0.5,
              maxOutputTokens: max_tokens ?? 8192,
              ...(normalizedFormat === 'gemini-preview' && { responseModalities: ["TEXT"], responseMimeType: "text/plain" })
            },
            ...(sys && { systemInstruction: { parts: [{ text: sys }] }})
          };
        };
        config.responseExtractor = (data) => {
          if (data?.candidates && data.candidates.length > 0 && data.candidates[0].content) {
            const parts = data.candidates[0].content.parts;
            return parts && parts.length > 0 ? parts.map(p => p.text).join('') : ''; // Join text parts
          }
          return '';
        };
        config.streamSupport = true;
        config.streamHandler = 'gemini';
        break;
      case 'volcano':
      case 'tongyi':
        // 代理模式下，Authorization 头可选（后端会使用环境变量中的 Key）
        if (authKey) {
          config.headers['Authorization'] = `Bearer ${authKey}`;
        }
        let specificModelId = '';
        // 读取保存的默认模型ID作为具体模型
        try {
          const cfg = (customRequestFormat === 'volcano') ? (window.loadModelConfig && loadModelConfig('volcano')) : (window.loadModelConfig && loadModelConfig('tongyi'));
          if (cfg && (cfg.preferredModelId || cfg.modelId)) specificModelId = cfg.preferredModelId || cfg.modelId;
        } catch {}

        config.bodyBuilder = (sys_prompt, user_content) => ({
          model: modelId || specificModelId,
          messages: [
            { role: 'system', content: sys_prompt },
            { role: 'user', content: extractTextFromUserContent(user_content) }
          ],
          temperature: temperature ?? 0.5,
          max_tokens: max_tokens ?? 8192,
          stream: true
        });
        config.streamBodyBuilder = (sys, msgs, user_content) => ({
          model: modelId || specificModelId,
          messages: [
            { role: 'system', content: sys },
            ...msgs.map(m => ({ role: m.role, content: extractTextFromUserContent(m.content) })),
            { role: 'user', content: extractTextFromUserContent(user_content) }
          ],
          temperature: temperature ?? 0.5,
          max_tokens: max_tokens ?? 8192,
          stream: true
        });
        config.responseExtractor = (data) => data?.choices?.[0]?.message?.content;
        config.streamSupport = true;
        config.streamHandler = true;
        break;
      default:
        // 代理模式下，Authorization 头可选（后端会使用环境变量中的 Key）
        if (authKey) {
          config.headers['Authorization'] = `Bearer ${authKey}`;
        }
        config.bodyBuilder = (sys_prompt, user_content) => ({
          model: modelId,
          messages: [{ role: "system", content: sys_prompt }, { role: "user", content: extractTextFromUserContent(user_content) }],
          temperature: temperature ?? 0.5,
          max_tokens: max_tokens ?? 8000
        });
        config.streamBodyBuilder = (sys, msgs, user_content) => ({
          model: modelId,
          messages: [
            { role: 'system', content: sys },
            ...msgs.map(m => ({ role: m.role, content: extractTextFromUserContent(m.content) })),
            { role: 'user', content: extractTextFromUserContent(user_content) }
          ],
          stream: true,
          temperature: temperature ?? 0.5,
          max_tokens: max_tokens ?? 8000,
        });
        config.responseExtractor = (data) => data?.choices?.[0]?.message?.content;
        config.streamSupport = true;
        console.warn(`Custom request format "${resolvedRequestFormat}" is not explicitly handled for multimodal input. Defaulting to text-only for user messages if images are provided.`);
    }
    console.log('buildCustomApiConfig:', {
      customRequestFormat: resolvedRequestFormat,
      endpoint: apiEndpoint,
      modelId,
      streamSupport: config.streamSupport,
      hasStreamBodyBuilder: !!config.streamBodyBuilder
    });
    return config;
  }

  // 导出到全局
  window.ApiConfigBuilder = {
    buildCustomApiConfig
  };

})();
